diff --git a/build/litegraph.js b/build/litegraph.js index 799f36f70..fe597ccbd 100644 --- a/build/litegraph.js +++ b/build/litegraph.js @@ -29,6 +29,7 @@ var LiteGraph = { node_images_path: "", debug: false, + throw_errors: true, registered_node_types: {}, /** @@ -40,13 +41,10 @@ var LiteGraph = { registerNodeType: function(type, base_class) { - var title = type; - if(base_class.prototype && base_class.prototype.title) - title = base_class.prototype.title; - else if(base_class.title) - title = base_class.title; - + if(!base_class.prototype) + throw("Cannot register a simple object, it must be a class with a prototype"); base_class.type = type; + if(LiteGraph.debug) console.log("Node registered: " + type); @@ -56,13 +54,13 @@ var LiteGraph = { base_class.category = type.substr(0,pos); //info.name = name.substr(pos+1,name.length - pos); - //inheritance + //extend class if(base_class.prototype) //is a class for(var i in LGraphNode.prototype) if(!base_class.prototype[i]) base_class.prototype[i] = LGraphNode.prototype[i]; - this.registered_node_types[type] = base_class; + this.registered_node_types[ type ] = base_class; }, /** @@ -73,7 +71,7 @@ var LiteGraph = { * @param {Object} options to set options */ - createNode: function(type,name, options) + createNode: function(type, title, options) { var base_class = this.registered_node_types[type]; if (!base_class) @@ -85,9 +83,11 @@ var LiteGraph = { var prototype = base_class.prototype || base_class; - name = name || prototype.title || base_class.title || type; + title = title || base_class.title || type; - var node = null; + var node = new base_class( name ); + + /* if (base_class.prototype) //is a class { node = new base_class(name); @@ -124,9 +124,10 @@ var LiteGraph = { //set size if(base_class.size) node.size = base_class.size.concat(); //save size } + */ node.type = type; - if(!node.name) node.name = name; + if(!node.title) node.title = title; if(!node.flags) node.flags = {}; if(!node.size) node.size = node.computeSize(); if(!node.pos) node.pos = LiteGraph.DEFAULT_POSITION.concat(); @@ -239,6 +240,7 @@ var LiteGraph = { //separated just to improve if it doesnt work cloneObject: function(obj, target) { + if(obj == null) return null; var r = JSON.parse( JSON.stringify( obj ) ); if(!target) return r; @@ -246,52 +248,11 @@ var LiteGraph = { target[i] = r[i]; return target; } - - /* - benchmark: function(mode) - { - mode = mode || "all"; - - trace("Benchmarking " + mode + "..."); - 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) - links++; - trace(" Num. links: " + links ); - - var numTimes = 200; - if(mode == "core") - numTimes = 30000; - - var start = new Date().getTime(); - - for(var i = 0; i < numTimes; i++) - { - if(mode == "render") - this.draw(false); - else if(mode == "core") - this.sendEventToAllNodes("onExecute"); - else - { - this.sendEventToAllNodes("onExecute"); - this.draw(false); - } - } - - var elapsed = (new Date().getTime()) - start; - trace(" Time take for " + numTimes + " iterations: " + (elapsed*0.001).toFixed(3) + " seconds."); - var seconds_per_iteration = (elapsed*0.001)/numTimes; - trace(" Time per iteration: " + seconds_per_iteration.toFixed( seconds_per_iteration < 0.001 ? 6 : 3) + " seconds"); - trace(" Avg FPS: " + (1000/(elapsed/numTimes)).toFixed(3)); - } - */ }; - + //********************************************************************************* // LGraph CLASS @@ -312,6 +273,12 @@ function LGraph() this.clear(); } +//default supported types +LGraph.supported_types = ["number","string","boolean"]; + +//used to know which types of connections support this graph (some graphs do not allow certain types) +LGraph.prototype.getSupportedTypes = function() { return this.supported_types || LGraph.supported_types; } + LGraph.STATUS_STOPPED = 1; LGraph.STATUS_RUNNING = 2; @@ -349,7 +316,8 @@ LGraph.prototype.clear = function() this.starttime = 0; //globals - this.globals = {}; + this.global_inputs = {}; + this.global_outputs = {}; this.graph = {}; this.debug = true; @@ -672,16 +640,19 @@ LGraph.prototype.add = function(node) node.bgImage = node.loadImage(node.bgImageUrl); */ - if(node.onInit) - node.onInit(); + if(node.onAdded) + node.onAdded(); if(this.config.align_to_grid) node.alignToGrid(); - this.updateExecutionOrder(); + this.updateExecutionOrder(); - if(this.canvas) - this.canvas.dirty_canvas = true; + if(this.onNodeAdded) + this.onNodeAdded(node); + + + this.setDirtyCanvas(true); this.change(); @@ -723,16 +694,19 @@ LGraph.prototype.remove = function(node) node.id = -1; //callback - if(node.onDelete) - node.onDelete(); + if(node.onRemoved) + node.onRemoved(); - //remove from environment - if(this.canvas) + node.graph = null; + + //remove from canvas render + for(var i in this.list_of_graphcanvas) { - if(this.canvas.selected_nodes[node.id]) - delete this.canvas.selected_nodes[node.id]; - if(this.canvas.node_dragged == node) - this.canvas.node_dragged = null; + var canvas = this.list_of_graphcanvas[i]; + if(canvas.selected_nodes[node.id]) + delete canvas.selected_nodes[node.id]; + if(canvas.node_dragged == node) + canvas.node_dragged = null; } //remove from containers @@ -741,8 +715,10 @@ LGraph.prototype.remove = function(node) this._nodes.splice(pos,1); delete this._nodes_by_id[node.id]; - if(this.canvas) - this.canvas.setDirty(true,true); + if(this.onNodeRemoved) + this.onNodeRemoved(node); + + this.setDirtyCanvas(true,true); this.change(); @@ -785,11 +761,11 @@ LGraph.prototype.findNodesByType = function(type) * @return {Array} a list with all the nodes with this name */ -LGraph.prototype.findNodesByName = function(name) +LGraph.prototype.findNodesByTitle = function(title) { var result = []; for (var i in this._nodes) - if(this._nodes[i].name == name) + if(this._nodes[i].title == title) result.push(this._nodes[i]); return result; } @@ -815,6 +791,47 @@ LGraph.prototype.getNodeOnPos = function(x,y, nodes_list) return null; } +//Tell this graph has a global input of this type +LGraph.prototype.addGlobalInput = function(name, type, value) +{ + this.global_inputs[name] = { type: type, value: value }; +} + +//assign a data to the global input +LGraph.prototype.setGlobalInputData = function(name, data) +{ + var input = this.global_inputs[name]; + if (!input) + return; + input.value = data; +} + +//rename the global input +LGraph.prototype.renameGlobalInput = function(old_name, name, data) +{ +} + + +LGraph.prototype.addGlobalOutput = function(name, type, value) +{ + this.global_outputs[name] = { type: type, value: value }; +} + +//assign a data to the global output +LGraph.prototype.setGlobalOutputData = function(name, data) +{ + var output = this.global_outputs[ name ]; + if (!output) + return; + output.value = data; +} + +//rename the global output +LGraph.prototype.renameGlobalOutput = function(old_name, name, data) +{ +} + + /** * Assigns a value to all the nodes that matches this name. This is used to create global variables of the node that * can be easily accesed from the outside of the graph @@ -874,8 +891,12 @@ LGraph.prototype.onConnectionChange = function() LGraph.prototype.isLive = function() { - if(!this.canvas) return false; - return this.canvas.live_mode; + for(var i in this.list_of_graphcanvas) + { + var c = this.list_of_graphcanvas[i]; + if(c.live_mode) return true; + } + return false; } /* Called when something visually changed */ @@ -890,6 +911,11 @@ LGraph.prototype.change = function() this.on_change(this); } +LGraph.prototype.setDirtyCanvas = function(fg,bg) +{ + this.sendActionToCanvas("setDirty",[fg,bg]); +} + //save and recover app state *************************************** /** * Creates a Object containing all the info about this graph, it can be serialized @@ -941,7 +967,7 @@ LGraph.prototype.configure = function(data, keep_old) for (var i in nodes) { var n_info = nodes[i]; //stored info - var n = LiteGraph.createNode( n_info.type, n_info.name ); + var n = LiteGraph.createNode( n_info.type, n_info.title ); if(!n) { if(LiteGraph.debug) @@ -954,17 +980,13 @@ LGraph.prototype.configure = function(data, keep_old) this.add(n); } - //TODO: dispatch redraw - if(this.canvas) - this.canvas.draw(true,true); - + this.setDirtyCanvas(true,true); return error; } LGraph.prototype.onNodeTrace = function(node, msg, color) { - if(this.canvas) - this.canvas.onNodeTrace(node,msg,color); + //TODO } // ************************************************************* @@ -977,18 +999,21 @@ LGraph.prototype.onNodeTrace = function(node, msg, color) + unsafe_execution: not allowed for safe execution supported callbacks: - + onInit: when added to graph + + onAdded: when added to graph + + onRemoved: when removed from graph + onStart: when starts playing + onStop: when stops playing - + onDrawForeground - + onDrawBackground + + onDrawForeground: render the inside widgets inside the node + + onDrawBackground: render the background area inside the node (only in edit mode) + + onMouseDown + onMouseMove - + onMouseOver + + onMouseUp + + onMouseEnter + + onMouseLeave + onExecute: execute the node + onPropertyChange: when a property is changed in the panel (return true to skip default behaviour) + onGetInputs: returns an array of possible inputs + onGetOutputs: returns an array of possible outputs - + onClick + onDblClick + onSerialize + onSelected @@ -1001,9 +1026,9 @@ LGraph.prototype.onNodeTrace = function(node, msg, color) * @param {String} name a name for the node */ -function LGraphNode(name) +function LGraphNode(title) { - this.name = name || "Unnamed"; + this.title = title || "Unnamed"; this.size = [LiteGraph.NODE_WIDTH,60]; this.graph = null; @@ -1080,17 +1105,19 @@ LGraphNode.prototype.serialize = function() { var o = { id: this.id, - name: this.name, + title: this.title, type: this.type, pos: this.pos, size: this.size, data: this.data, - properties: LiteGraph.cloneObject(this.properties), flags: LiteGraph.cloneObject(this.flags), inputs: this.inputs, outputs: this.outputs }; + if(this.properties) + o.properties = LiteGraph.cloneObject(this.properties); + if(!o.type) o.type = this.constructor.type; @@ -1117,8 +1144,8 @@ LGraphNode.prototype.reducedObjectivize = function() var type = LiteGraph.getNodeType(o.type); - if(type.name == o.name) - delete o["name"]; + if(type.title == o.title) + delete o["title"]; if(type.size && compareObjects(o.size,type.size)) delete o["size"]; @@ -1142,6 +1169,18 @@ LGraphNode.prototype.toString = function() //LGraphNode.prototype.unserialize = function(info) {} //this cannot be done from within, must be done in LiteGraph +/** +* get the title string +* @method getTitle +*/ + +LGraphNode.prototype.getTitle = function() +{ + return this.title || this.constructor.title; +} + + + // Execution ************************* /** * sets the output data @@ -1275,6 +1314,29 @@ LGraphNode.prototype.addOutput = function(name,type,extra_info) this.size = this.computeSize(); } +/** +* add a new output slot to use in this node +* @method addOutputs +* @param {Array} array of triplets like [[name,type,extra_info],[...]] +*/ +LGraphNode.prototype.addOutputs = function(array) +{ + for(var i in array) + { + var info = array[i]; + var o = {name:info[0],type:info[1],link:null}; + if(array[2]) + for(var j in info[2]) + o[j] = info[2][j]; + + if(!this.outputs) + this.outputs = []; + this.outputs.push(o); + } + + this.size = this.computeSize(); +} + /** * remove an existing output slot * @method removeOutput @@ -1306,6 +1368,29 @@ LGraphNode.prototype.addInput = function(name,type,extra_info) this.size = this.computeSize(); } +/** +* add several new input slots in this node +* @method addInputs +* @param {Array} array of triplets like [[name,type,extra_info],[...]] +*/ +LGraphNode.prototype.addInputs = function(array) +{ + for(var i in array) + { + var info = array[i]; + var o = {name:info[0],type:info[1],link:null}; + if(array[2]) + for(var j in info[2]) + o[j] = info[2][j]; + + if(!this.inputs) + this.inputs = []; + this.inputs.push(o); + } + + this.size = this.computeSize(); +} + /** * remove an existing input slot * @method removeInput @@ -1521,7 +1606,7 @@ LGraphNode.prototype.disconnectOutput = function(slot, target_node) { output.links.splice(i,1); //remove here target_node.inputs[ link[4] ].link = null; //remove there - delete this.graph.links[link[0]]; + delete this.graph.links[link[0]]; //remove the link from the links pool break; } } @@ -1750,7 +1835,7 @@ LGraphNode.prototype.captureInput = function(v) //change c.node_capturing_input = v ? this : null; if(this.graph.debug) - console.log(this.name + ": Capturing input " + (v?"ON":"OFF")); + console.log(this.title + ": Capturing input " + (v?"ON":"OFF")); } } @@ -2329,8 +2414,8 @@ LGraphCanvas.prototype.processMouseMove = function(e) n.pos[0] += delta[0] / this.scale; n.pos[1] += delta[1] / this.scale; - n.pos[0] = Math.round(n.pos[0]); - n.pos[1] = Math.round(n.pos[1]); + //n.pos[0] = Math.round(n.pos[0]); + //n.pos[1] = Math.round(n.pos[1]); } this.dirty_canvas = true; @@ -2423,7 +2508,8 @@ LGraphCanvas.prototype.processMouseUp = function(e) { this.dirty_canvas = true; this.dirty_bgcanvas = true; - + this.node_dragged.pos[0] = Math.round(this.node_dragged.pos[0]); + this.node_dragged.pos[1] = Math.round(this.node_dragged.pos[1]); if(this.graph.config.align_to_grid) this.node_dragged.alignToGrid(); this.node_dragged = null; @@ -3010,10 +3096,10 @@ LGraphCanvas.prototype.drawNode = function(node, ctx ) } else if(this.render_shadows) { - ctx.shadowColor = "#111"; + ctx.shadowColor = "rgba(0,0,0,0.5)"; ctx.shadowOffsetX = 2; ctx.shadowOffsetY = 2; - ctx.shadowBlur = 4; + ctx.shadowBlur = 3; } else ctx.shadowColor = "transparent"; @@ -3270,10 +3356,11 @@ LGraphCanvas.prototype.drawNodeShape = function(node, ctx, size, fgcolor, bgcolo //title text ctx.font = this.title_text_font; - if(node.name != "" && this.scale > 0.8) + var title = node.getTitle(); + if(title && this.scale > 0.8) { ctx.fillStyle = "#222"; - ctx.fillText(node.name,16,13-title_height ); + ctx.fillText( title, 16, 13 - title_height ); } } } @@ -3386,7 +3473,7 @@ LGraphCanvas.prototype.renderLink = function(ctx,a,b,color) var dist = distance(a,b); - if(this.render_connections_border) + if(this.render_connections_border && this.scale > 0.6) ctx.lineWidth = this.connections_width + 4; ctx.beginPath(); @@ -3406,7 +3493,7 @@ LGraphCanvas.prototype.renderLink = function(ctx,a,b,color) ctx.lineTo(b[0]-10,b[1]); } - if(this.render_connections_border) + if(this.render_connections_border && this.scale > 0.6) { ctx.strokeStyle = "rgba(0,0,0,0.5)"; ctx.stroke(); @@ -3689,7 +3776,7 @@ LGraphCanvas.onMenuNodeOutputs = function(node, e, prev_menu) LGraphCanvas.onMenuNodeCollapse = function(node) { node.flags.collapsed = !node.flags.collapsed; - node.graph.canvas.setDirty(true,true); + node.setDirtyCanvas(true,true); } LGraphCanvas.onMenuNodePin = function(node) @@ -3716,7 +3803,7 @@ LGraphCanvas.onMenuNodeColors = function(node, e, prev_menu) { node.color = color.color; node.bgcolor = color.bgcolor; - node.graph.canvas.setDirty(true); + node.setDirtyCanvas(true); } } @@ -3731,7 +3818,7 @@ LGraphCanvas.onMenuNodeShapes = function(node,e) { if(!node) return; node.shape = v; - node.graph.canvas.setDirty(true); + node.setDirtyCanvas(true); } return false; @@ -3741,7 +3828,7 @@ LGraphCanvas.onMenuNodeRemove = function(node) { if(node.removable == false) return; node.graph.remove(node); - node.graph.canvas.setDirty(true,true); + node.setDirtyCanvas(true,true); } LGraphCanvas.onMenuNodeClone = function(node) @@ -3751,7 +3838,7 @@ LGraphCanvas.onMenuNodeClone = function(node) if(!newnode) return; newnode.pos = [node.pos[0]+5,node.pos[1]+5]; node.graph.add(newnode); - node.graph.canvas.setDirty(true,true); + node.setDirtyCanvas(true,true); } LGraphCanvas.node_colors = { @@ -4126,445 +4213,152 @@ if( !window["requestAnimationFrame"] ) //basic nodes - -LiteGraph.registerNodeType("basic/const",{ - title: "Const", - desc: "Constant", - outputs: [["value","number"]], - properties: {value:1.0}, - editable: { property:"value", type:"number" }, - - setValue: function(v) - { - if( typeof(v) == "string") v = parseFloat(v); - this.properties["value"] = v; - this.setDirtyCanvas(true); - }, - - onExecute: function() - { - this.setOutputData(0, parseFloat( this.properties["value"] ) ); - }, - - onDrawBackground: function(ctx) - { - //show the current value - this.outputs[0].label = this.properties["value"].toFixed(3); - }, - - onWidget: function(e,widget) - { - if(widget.name == "value") - this.setValue(widget.value); - } -}); - -LiteGraph.registerNodeType("math/rand",{ - title: "Rand", - desc: "Random number", - outputs: [["value","number"]], - properties: {min:0,max:1}, - size: [60,20], - - onExecute: function() - { - var min = this.properties.min; - var max = this.properties.max; - this._last_v = Math.random() * (max-min) + min; - this.setOutputData(0, this._last_v ); - }, - - onDrawBackground: function(ctx) - { - //show the current value - if(this._last_v) - this.outputs[0].label = this._last_v.toFixed(3); - else - this.outputs[0].label = "?"; - } -}); - -LiteGraph.registerNodeType("math/clamp",{ - title: "Clamp", - desc: "Clamp number between min and max", - inputs: [["in","number"]], - outputs: [["out","number"]], - size: [60,20], - properties: {min:0,max:1}, - - onExecute: function() - { - var v = this.getInputData(0); - if(v == null) return; - v = Math.max(this.properties.min,v); - v = Math.min(this.properties.max,v); - this.setOutputData(0, v ); - } -}); - -LiteGraph.registerNodeType("math/abs",{ - title: "Abs", - desc: "Absolute", - inputs: [["in","number"]], - outputs: [["out","number"]], - size: [60,20], - - onExecute: function() - { - var v = this.getInputData(0); - if(v == null) return; - this.setOutputData(0, Math.abs(v) ); - } -}); - -LiteGraph.registerNodeType("math/floor",{ - title: "Floor", - desc: "Floor number to remove fractional part", - inputs: [["in","number"]], - outputs: [["out","number"]], - size: [60,20], - - onExecute: function() - { - var v = this.getInputData(0); - if(v == null) return; - this.setOutputData(0, v|1 ); - } -}); +(function(){ -LiteGraph.registerNodeType("math/frac",{ - title: "Frac", - desc: "Returns fractional part", - inputs: [["in","number"]], - outputs: [["out","number"]], - size: [60,20], - - onExecute: function() - { - var v = this.getInputData(0); - if(v == null) return; - this.setOutputData(0, v%1 ); - } -}); - - -LiteGraph.registerNodeType("basic/watch", { - title: "Watch", - desc: "Show value", - size: [60,20], - inputs: [["value",0,{label:""}]], - outputs: [["value",0,{label:""}]], - properties: {value:""}, - - onExecute: function() - { - this.properties.value = this.getInputData(0); - this.setOutputData(0, this.properties.value); - }, - - onDrawBackground: function(ctx) - { - //show the current value - if(this.inputs[0] && this.properties["value"] != null) - { - if (this.properties["value"].constructor === Number ) - this.inputs[0].label = this.properties["value"].toFixed(3); - else - this.inputs[0].label = this.properties["value"]; - } - } -}); - - -LiteGraph.registerNodeType("math/scale",{ - title: "Scale", - desc: "1 - value", - inputs: [["value","number",{label:""}]], - outputs: [["value","number",{label:""}]], - size:[70,20], - properties: {"factor":1}, - - onExecute: function() - { - var value = this.getInputData(0); - if(value != null) - this.setOutputData(0, value * this.properties.factor ); - } -}); - - -LiteGraph.registerNodeType("math/operation",{ - title: "Operation", - desc: "Easy math operators", - inputs: [["A","number"],["B","number"]], - outputs: [["A+B","number"]], - size: [80,20], - //optional_inputs: [["start","number"]], - - properties: {A:1.0, B:1.0}, - - setValue: function(v) - { - if( typeof(v) == "string") v = parseFloat(v); - this.properties["value"] = v; - this.setDirtyCanvas(true); - }, - - onExecute: function() - { - var A = this.getInputData(0); - var B = this.getInputData(1); - if(A!=null) - this.properties["A"] = A; - else - A = this.properties["A"]; - - if(B!=null) - this.properties["B"] = B; - else - B = this.properties["B"]; - - for(var i = 0, l = this.outputs.length; i < l; ++i) - { - var output = this.outputs[i]; - if(!output.links || !output.links.length) - continue; - switch( output.name ) - { - case "A+B": value = A+B; break; - case "A-B": value = A-B; break; - case "A*B": value = A*B; break; - case "A/B": value = A/B; break; - } - this.setOutputData(i, value ); - } - }, - - onGetOutputs: function() - { - return [["A-B","number"],["A*B","number"],["A/B","number"]]; - } -}); - -LiteGraph.registerNodeType("math/compare",{ - title: "Compare", - desc: "compares between two values", - - inputs: [["A","number"],["B","number"]], - outputs: [["A==B","number"],["A!=B","number"]], - properties:{A:0,B:0}, - onExecute: function() - { - var A = this.getInputData(0); - var B = this.getInputData(1); - if(A!=null) - this.properties["A"] = A; - else - A = this.properties["A"]; - - if(B!=null) - this.properties["B"] = B; - else - B = this.properties["B"]; - - for(var i = 0, l = this.outputs.length; i < l; ++i) - { - var output = this.outputs[i]; - if(!output.links || !output.links.length) - continue; - switch( output.name ) - { - case "A==B": value = A==B; break; - case "A!=B": value = A!=B; break; - case "A>B": value = A>B; break; - case "A=B": value = A>=B; break; - } - this.setOutputData(i, value ); - } - }, - - onGetOutputs: function() - { - return [["A==B","number"],["A!=B","number"],["A>B","number"],["A=B","number"],["A<=B","number"]]; - } -}); - -if(window.math) //math library for safe math operations without eval -LiteGraph.registerNodeType("math/formula",{ - title: "Formula", - desc: "Compute safe formula", - inputs: [["x","number"],["y","number"]], - outputs: [["","number"]], - properties: {x:1.0, y:1.0, formula:"x+y"}, - - onExecute: function() - { - var x = this.getInputData(0); - var y = this.getInputData(1); - if(x != null) - this.properties["x"] = x; - else - x = this.properties["x"]; - - if(y!=null) - this.properties["y"] = y; - else - y = this.properties["y"]; - - var f = this.properties["formula"]; - var value = math.eval(f,{x:x,y:y,T: this.graph.globaltime }); - this.setOutputData(0, value ); - }, - - onDrawBackground: function() - { - var f = this.properties["formula"]; - this.outputs[0].label = f; - }, - - onGetOutputs: function() - { - return [["A-B","number"],["A*B","number"],["A/B","number"]]; - } -}); - - -LiteGraph.registerNodeType("math/trigonometry",{ - title: "Trigonometry", - desc: "Sin Cos Tan", - bgImageUrl: "nodes/imgs/icon-sin.png", - - inputs: [["v","number"]], - outputs: [["sin","number"]], - properties: {amplitude:1.0}, - size:[100,20], - - onExecute: function() - { - var v = this.getInputData(0); - var amp = this.properties["amplitude"]; - for(var i = 0, l = this.outputs.length; i < l; ++i) - { - var output = this.outputs[i]; - switch( output.name ) - { - case "sin": value = Math.sin(v); break; - case "cos": value = Math.cos(v); break; - case "tan": value = Math.tan(v); break; - case "asin": value = Math.asin(v); break; - case "acos": value = Math.acos(v); break; - case "atan": value = Math.atan(v); break; - } - this.setOutputData(i, amp * value ); - } - }, - - onGetOutputs: function() - { - return [["sin","number"],["cos","number"],["tan","number"],["asin","number"],["acos","number"],["atan","number"]]; - } -}); - -//if glMatrix is installed... -if(window.glMatrix) +//Input for a subgraph +function GlobalInput() { - LiteGraph.registerNodeType("math3d/vec3-to-xyz",{ - title: "Vec3->XYZ", - desc: "vector 3 to components", - inputs: [["vec3","vec3"]], - outputs: [["x","number"],["y","number"],["z","number"]], + this.addOutput("value",0); +} - onExecute: function() - { - var v = this.getInputData(0); - if(v == null) return; +GlobalInput.prototype.onExecute = function() +{ + var name = this.title; + //read input + var value = node.graph.global_inputs[name]; + this.setOutputData(0,value); +} - this.setOutputData( 0, v[0] ); - this.setOutputData( 1, v[1] ); - this.setOutputData( 2, v[2] ); - } - }); - - LiteGraph.registerNodeType("math3d/xyz-to-vec3",{ - title: "XYZ->Vec3", - desc: "components to vector3", - inputs: [["x","number"],["y","number"],["z","number"]], - outputs: [["vec3","vec3"]], - - onExecute: function() - { - var x = this.getInputData(0); - if(x == null) x = 0; - var y = this.getInputData(1); - if(y == null) y = 0; - var z = this.getInputData(2); - if(z == null) z = 0; - - this.setOutputData( 0, vec3.fromValues(x,y,z) ); - } - }); - - LiteGraph.registerNodeType("math3d/rotation",{ - title: "Rotation", - desc: "rotation quaternion", - inputs: [["degrees","number"],["axis","vec3"]], - outputs: [["quat","quat"]], - properties: {angle:90.0, axis:[0,1,0]}, - - onExecute: function() - { - var angle = this.getInputData(0); - if(angle == null) angle = this.properties.angle; - var axis = this.getInputData(1); - if(axis == null) axis = this.properties.axis; - - var R = quat.setAxisAngle(quat.create(), axis, angle * 0.0174532925 ); - this.setOutputData( 0, R ); - } - }); - - 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 vec = this.getInputData(0); - if(vec == null) vec = this.properties.vec; - var quat = this.getInputData(1); - if(quat == null) - this.setOutputData(vec); - else - this.setOutputData( 0, vec3.transformQuat( vec3.create(), vec, quat ) ); - } - }); +LiteGraph.registerNodeType("graph/input", GlobalInput); - LiteGraph.registerNodeType("math3d/mult-quat",{ - title: "Mult. Quat", - desc: "rotate quaternion", - inputs: [["A","quat"],["B","quat"]], - outputs: [["A*B","quat"]], +//Output for a subgraph +function GlobalOutput() +{ + this.title = "Output"; - onExecute: function() - { - var A = this.getInputData(0); - if(A == null) return; - var B = this.getInputData(1); - if(B == null) return; + //random name to avoid problems with other outputs when added + var genname = "input_" + (Math.random()*1000).toFixed(); + this.properties = { name: genname, type: "number" }; + this.addInput("value","number"); +} - var R = quat.multiply(quat.create(), A,B); - this.setOutputData( 0, R ); - } - }); +GlobalOutput.prototype.onAdded = function() +{ + var name = this.graph.addGlobalOutput( this.properties.name, this.properties.type ); +} + +GlobalOutput.prototype.onExecute = function() +{ + var value = this.getInputData(0); + this.graph.setGlobalOutputData( this.properties.name, value ); +} + +LiteGraph.registerNodeType("graph/output", GlobalOutput); + + +//Subgraph: a node that contains a graph +function Subgraph() +{ + this.subgraph = new LGraph(); + this.bgcolor = "#FA3"; +} + +Subgraph.prototype.onExecute = function() +{ + //send inputs to subgraph global inputs + for(var i in this.inputs) + { + var input = this.inputs[i]; + + //this.subgraph.setGlobalInputData( input.name, input.value ); + } + + //send subgraph global outputs to outputs +} + +Subgraph.prototype.configure = function(o) +{ + LGraph.prototype.configure.call(this, o); + //after configure, ... +} + +LiteGraph.registerNodeType("graph/subgraph", Subgraph); + + + +//Constant +function Constant() +{ + this.addOutput("value","number"); + this.properties = { value:1.0 }; + this.editable = { property:"value", type:"number" }; +} + +Constant.title = "Const"; +Constant.desc = "Constant value"; + + +Constant.prototype.setValue = function(v) +{ + if( typeof(v) == "string") v = parseFloat(v); + this.properties["value"] = v; + this.setDirtyCanvas(true); +}; + +Constant.prototype.onExecute = function() +{ + this.setOutputData(0, parseFloat( this.properties["value"] ) ); +} + +Constant.prototype.onDrawBackground = function(ctx) +{ + //show the current value + this.outputs[0].label = this.properties["value"].toFixed(3); +} + +Constant.prototype.onWidget = function(e,widget) +{ + if(widget.name == "value") + this.setValue(widget.value); +} + +LiteGraph.registerNodeType("basic/const", Constant); + + +//Watch a value in the editor +function Watch() +{ + this.size = [60,20]; + this.addInput("value",0,{label:""}); + this.addOutput("value",0,{label:""}); + this.properties = { value:"" }; +} + +Watch.title = "Watch"; +Watch.desc = "Show value of input"; + +Watch.prototype.onExecute = function() +{ + this.properties.value = this.getInputData(0); + this.setOutputData(0, this.properties.value); +} + +Watch.prototype.onDrawBackground = function(ctx) +{ + //show the current value + if(this.inputs[0] && this.properties["value"] != null) + { + if (this.properties["value"].constructor === Number ) + this.inputs[0].label = this.properties["value"].toFixed(3); + else + this.inputs[0].label = this.properties["value"]; + } +} + +LiteGraph.registerNodeType("basic/watch", Watch); -} //glMatrix /* @@ -4611,106 +4405,6 @@ LiteGraph.registerNodeType("math/sinusoid",{ /* LiteGraph.registerNodeType("basic/number",{ title: "Number", - desc: "Fixed number output", - outputs: [["","number"]], - color: "#66A", - bgcolor: "#336", - widgets: [{name:"value",text:"Value",type:"input",property:"value"}], - - properties: {value:1.0}, - - setValue: function(v) - { - if( typeof(v) == "string") v = parseFloat(v); - this.properties["value"] = v; - this.setDirtyCanvas(true); - }, - - onExecute: function() - { - this.outputs[0].name = this.properties["value"].toString(); - this.setOutputData(0, this.properties["value"]); - }, - - onWidget: function(e,widget) - { - if(widget.name == "value") - this.setValue(widget.value); - } -}); - - -LiteGraph.registerNodeType("basic/string",{ - title: "String", - desc: "Fixed string output", - outputs: [["","string"]], - color: "#66A", - bgcolor: "#336", - widgets: [{name:"value",text:"Value",type:"input"}], - - properties: {value:"..."}, - - setValue: function(v) - { - this.properties["value"] = v; - this.setDirtyCanvas(true); - }, - - onExecute: function() - { - this.outputs[0].name = this.properties["value"].toString(); - this.setOutputData(0, this.properties["value"]); - }, - - onWidget: function(e,widget) - { - if(widget.name == "value") - this.setValue(widget.value); - } -}); - -LiteGraph.registerNodeType("basic/trigger",{ - title: "Trigger", - desc: "Triggers node action", - inputs: [["!0","number"]], - outputs: [["M","node"]], - - properties: {triggerName:null}, - - onExecute: function() - { - if( this.getInputData(0) ) - { - var m = this.getOutputNode(0); - if(m && m.onTrigger) - m.onTrigger(); - if(m && this.properties.triggerName && typeof(m[this.properties.triggerName]) == "function") - m[this.properties.triggerName].call(m); - } - } -}); - - -LiteGraph.registerNodeType("basic/switch",{ - title: "Switch", - desc: "Switch between two inputs", - inputs: [["i","number"],["A",0],["B",0]], - outputs: [["",0]], - - onExecute: function() - { - var f = this.getInputData(0); - if(f) - { - f = Math.round(f)+1; - if(f < 1) f = 1; - if(f > 2) f = 2; - this.setOutputData(0, this.getInputData(f) ); - } - else - this.setOutputData(0, null); - } -}); // System vars ********************************* @@ -4863,296 +4557,345 @@ LiteGraph.registerNodeType("network/network_callback",{ }); */ + + +})(); //widgets +(function(){ - 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"}], + function WidgetKnob() + { + this.size = [64,84]; + this.addOutput("",'number'); + this.properties = {min:0,max:1,value:0.5,wcolor:"#7AF",size:50}; + } - onInit: function() + WidgetKnob.title = "Knob"; + WidgetKnob.desc = "Circular controller"; + WidgetKnob.widgets = [{name:"increase",text:"+",type:"minibutton"},{name:"decrease",text:"-",type:"minibutton"}]; + + + WidgetKnob.prototype.onAdded = 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"); + } + + WidgetKnob.prototype.onDrawImageKnob = function(ctx) + { + if(!this.imgfg || !this.imgfg.width) return; + + var d = this.imgbg.width*0.5; + var scale = this.size[0] / this.imgfg.width; + + ctx.save(); + ctx.translate(0,20); + ctx.scale(scale,scale); + ctx.drawImage(this.imgbg,0,0); + //ctx.drawImage(this.imgfg,0,20); + + ctx.translate(d,d); + ctx.rotate(this.value * (Math.PI*2) * 6/8 + Math.PI * 10/8); + //ctx.rotate(this.value * (Math.PI*2)); + ctx.translate(-d,-d); + ctx.drawImage(this.imgfg,0,0); + + ctx.restore(); + + ctx.font = "bold 16px Criticized,Tahoma"; + ctx.fillStyle="rgba(100,100,100,0.8)"; + ctx.textAlign = "center"; + + ctx.fillText(this.name.toUpperCase(), this.size[0] * 0.5, 18 ); + ctx.textAlign = "left"; + } + + WidgetKnob.prototype.onDrawVectorKnob = function(ctx) + { + if(!this.imgfg || !this.imgfg.width) return; + + //circle around + ctx.lineWidth = 1; + ctx.strokeStyle= this.mouseOver ? "#FFF" : "#AAA"; + ctx.fillStyle="#000"; + ctx.beginPath(); + ctx.arc(this.size[0] * 0.5,this.size[1] * 0.5 + 10,this.properties.size * 0.5,0,Math.PI*2,true); + ctx.stroke(); + + if(this.value > 0) { - 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(ctx) - { - if(!this.imgfg || !this.imgfg.width) return; - - var d = this.imgbg.width*0.5; - var scale = this.size[0] / this.imgfg.width; - - ctx.save(); - ctx.translate(0,20); - ctx.scale(scale,scale); - ctx.drawImage(this.imgbg,0,0); - //ctx.drawImage(this.imgfg,0,20); - - ctx.translate(d,d); - ctx.rotate(this.value * (Math.PI*2) * 6/8 + Math.PI * 10/8); - //ctx.rotate(this.value * (Math.PI*2)); - ctx.translate(-d,-d); - ctx.drawImage(this.imgfg,0,0); - - ctx.restore(); - - ctx.font = "bold 16px Criticized,Tahoma"; - ctx.fillStyle="rgba(100,100,100,0.8)"; - ctx.textAlign = "center"; - - ctx.fillText(this.name.toUpperCase(), this.size[0] * 0.5, 18 ); - ctx.textAlign = "left"; - }, - - onDrawVectorKnob: function(ctx) - { - if(!this.imgfg || !this.imgfg.width) return; - - //circle around - ctx.lineWidth = 1; - ctx.strokeStyle= this.mouseOver ? "#FFF" : "#AAA"; - ctx.fillStyle="#000"; + ctx.strokeStyle=this.properties["wcolor"]; + ctx.lineWidth = (this.properties.size * 0.2); ctx.beginPath(); - ctx.arc(this.size[0] * 0.5,this.size[1] * 0.5 + 10,this.properties.size * 0.5,0,Math.PI*2,true); + ctx.arc(this.size[0] * 0.5,this.size[1] * 0.5 + 10,this.properties.size * 0.35,Math.PI * -0.5 + Math.PI*2 * this.value,Math.PI * -0.5,true); ctx.stroke(); - - if(this.value > 0) - { - ctx.strokeStyle=this.properties["wcolor"]; - ctx.lineWidth = (this.properties.size * 0.2); - ctx.beginPath(); - ctx.arc(this.size[0] * 0.5,this.size[1] * 0.5 + 10,this.properties.size * 0.35,Math.PI * -0.5 + Math.PI*2 * this.value,Math.PI * -0.5,true); - ctx.stroke(); - ctx.lineWidth = 1; - } - - ctx.font = (this.properties.size * 0.2) + "px Arial"; - ctx.fillStyle="#AAA"; - ctx.textAlign = "center"; - - var str = this.properties["value"]; - if(typeof(str) == 'number') - str = str.toFixed(2); - - ctx.fillText(str,this.size[0] * 0.5,this.size[1]*0.65); - ctx.textAlign = "left"; - }, - - onDrawForeground: function(ctx) - { - this.onDrawImageKnob(ctx); - }, - - onExecute: function() - { - this.setOutputData(0, this.properties["value"] ); - - this.boxcolor = colorToString([this.value,this.value,this.value]); - }, - - onMouseDown: function(e) - { - if(!this.imgfg || !this.imgfg.width) return; - - //this.center = [this.imgbg.width * 0.5, this.imgbg.height * 0.5 + 20]; - //this.radius = this.imgbg.width * 0.5; - this.center = [this.size[0] * 0.5, this.size[1] * 0.5 + 20]; - this.radius = this.size[0] * 0.5; - - if(e.canvasY - this.pos[1] < 20 || distance([e.canvasX,e.canvasY],[this.pos[0] + this.center[0],this.pos[1] + this.center[1]]) > this.radius) - return false; - - this.oldmouse = [ e.canvasX - this.pos[0], e.canvasY - this.pos[1] ]; - this.captureInput(true); - - /* - var tmp = this.localToScreenSpace(0,0); - this.trace(tmp[0] + "," + tmp[1]); */ - return true; - }, - - onMouseMove: function(e) - { - if(!this.oldmouse) return; - - var m = [ e.canvasX - this.pos[0], e.canvasY - this.pos[1] ]; - - var v = this.value; - v -= (m[1] - this.oldmouse[1]) * 0.01; - if(v > 1.0) v = 1.0; - else if(v < 0.0) v = 0.0; - - this.value = v; - this.properties["value"] = this.properties["min"] + (this.properties["max"] - this.properties["min"]) * this.value; - - this.oldmouse = m; - this.setDirtyCanvas(true); - }, - - onMouseUp: function(e) - { - if(this.oldmouse) - { - this.oldmouse = null; - this.captureInput(false); - } - }, - - onMouseLeave: function(e) - { - //this.oldmouse = null; - }, - - onWidget: function(e,widget) - { - if(widget.name=="increase") - this.onPropertyChange("size", this.properties.size + 10); - else if(widget.name=="decrease") - this.onPropertyChange("size", this.properties.size - 10); - }, - - onPropertyChange: function(name,value) - { - if(name=="wcolor") - this.properties[name] = value; - else if(name=="size") - { - value = parseInt(value); - this.properties[name] = value; - this.size = [value+4,value+24]; - this.setDirtyCanvas(true,true); - } - else if(name=="min" || name=="max" || name=="value") - { - this.properties[name] = parseFloat(value); - } - else - return false; - return true; + ctx.lineWidth = 1; } - }); - 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"); - }, + ctx.font = (this.properties.size * 0.2) + "px Arial"; + ctx.fillStyle="#AAA"; + ctx.textAlign = "center"; - onDrawVectorial: function(ctx) - { - if(!this.imgfg || !this.imgfg.width) return; + var str = this.properties["value"]; + if(typeof(str) == 'number') + str = str.toFixed(2); - //border - ctx.lineWidth = 1; - ctx.strokeStyle= this.mouseOver ? "#FFF" : "#AAA"; - ctx.fillStyle="#000"; - ctx.beginPath(); - ctx.rect(2,0,this.size[0]-4,20); - ctx.stroke(); + ctx.fillText(str,this.size[0] * 0.5,this.size[1]*0.65); + ctx.textAlign = "left"; + } - ctx.fillStyle=this.properties["wcolor"]; - ctx.beginPath(); - ctx.rect(2+(this.size[0]-4-20)*this.value,0, 20,20); - ctx.fill(); - }, + WidgetKnob.prototype.onDrawForeground = function(ctx) + { + this.onDrawImageKnob(ctx); + } - onDrawImage: function(ctx) - { - if(!this.imgfg || !this.imgfg.width) return; + WidgetKnob.prototype.onExecute = function() + { + this.setOutputData(0, this.properties["value"] ); - //border - ctx.lineWidth = 1; - ctx.fillStyle="#000"; - ctx.fillRect(2,9,this.size[0]-4,2); + this.boxcolor = colorToString([this.value,this.value,this.value]); + } - ctx.strokeStyle= "#333"; - ctx.beginPath(); - ctx.moveTo(2,9); - ctx.lineTo(this.size[0]-4,9); - ctx.stroke(); + WidgetKnob.prototype.onMouseDown = function(e) + { + if(!this.imgfg || !this.imgfg.width) return; - ctx.strokeStyle= "#AAA"; - ctx.beginPath(); - ctx.moveTo(2,11); - ctx.lineTo(this.size[0]-4,11); - ctx.stroke(); + //this.center = [this.imgbg.width * 0.5, this.imgbg.height * 0.5 + 20]; + //this.radius = this.imgbg.width * 0.5; + this.center = [this.size[0] * 0.5, this.size[1] * 0.5 + 20]; + this.radius = this.size[0] * 0.5; - ctx.drawImage(this.imgfg, 2+(this.size[0]-4)*this.value - this.imgfg.width*0.5,-this.imgfg.height*0.5 + 10); - }, + if(e.canvasY - this.pos[1] < 20 || distance([e.canvasX,e.canvasY],[this.pos[0] + this.center[0],this.pos[1] + this.center[1]]) > this.radius) + return false; - onDrawForeground: function(ctx) - { - this.onDrawImage(ctx); - }, + this.oldmouse = [ e.canvasX - this.pos[0], e.canvasY - this.pos[1] ]; + this.captureInput(true); - 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]); - }, + /* + var tmp = this.localToScreenSpace(0,0); + this.trace(tmp[0] + "," + tmp[1]); */ + return true; + } - onMouseDown: function(e) - { - if(e.canvasY - this.pos[1] < 0) - return false; + WidgetKnob.prototype.onMouseMove = function(e) + { + if(!this.oldmouse) return; - this.oldmouse = [ e.canvasX - this.pos[0], e.canvasY - this.pos[1] ]; - this.captureInput(true); - return true; - }, + var m = [ e.canvasX - this.pos[0], e.canvasY - this.pos[1] ]; - onMouseMove: function(e) - { - if(!this.oldmouse) return; + var v = this.value; + v -= (m[1] - this.oldmouse[1]) * 0.01; + if(v > 1.0) v = 1.0; + else if(v < 0.0) v = 0.0; - var m = [ e.canvasX - this.pos[0], e.canvasY - this.pos[1] ]; + this.value = v; + this.properties["value"] = this.properties["min"] + (this.properties["max"] - this.properties["min"]) * this.value; - var v = this.value; - var delta = (m[0] - this.oldmouse[0]); - v += delta / this.size[0]; - if(v > 1.0) v = 1.0; - else if(v < 0.0) v = 0.0; + this.oldmouse = m; + this.setDirtyCanvas(true); + } - this.value = v; - - this.oldmouse = m; - this.setDirtyCanvas(true); - }, - - onMouseUp: function(e) + WidgetKnob.prototype.onMouseUp = function(e) + { + if(this.oldmouse) { this.oldmouse = null; this.captureInput(false); - }, - - onMouseLeave: function(e) - { - //this.oldmouse = null; - }, - - onPropertyChange: function(name,value) - { - if(name=="wcolor") - this.properties[name] = value; - else - return false; - return true; } - }); + } + WidgetKnob.prototype.onMouseLeave = function(e) + { + //this.oldmouse = null; + } + + WidgetKnob.prototype.onWidget = function(e,widget) + { + if(widget.name=="increase") + this.onPropertyChange("size", this.properties.size + 10); + else if(widget.name=="decrease") + this.onPropertyChange("size", this.properties.size - 10); + } + + WidgetKnob.prototype.onPropertyChange = function(name,value) + { + if(name=="wcolor") + this.properties[name] = value; + else if(name=="size") + { + value = parseInt(value); + this.properties[name] = value; + this.size = [value+4,value+24]; + this.setDirtyCanvas(true,true); + } + else if(name=="min" || name=="max" || name=="value") + { + this.properties[name] = parseFloat(value); + } + else + return false; + return true; + } + + LiteGraph.registerNodeType("widget/knob", WidgetKnob); + + //Widget H SLIDER + function WidgetHSlider() + { + this.size = [160,26]; + this.addOutput("",'number'); + this.properties = {wcolor:"#7AF",min:0,max:1,value:0.5}; + } + + WidgetHSlider.title = "H.Slider"; + WidgetHSlider.desc = "Linear slider controller"; + + WidgetHSlider.prototype.onInit = function() + { + this.value = 0.5; + this.imgfg = this.loadImage("imgs/slider_fg.png"); + } + + WidgetHSlider.prototype.onDrawVectorial = function(ctx) + { + if(!this.imgfg || !this.imgfg.width) return; + + //border + ctx.lineWidth = 1; + ctx.strokeStyle= this.mouseOver ? "#FFF" : "#AAA"; + ctx.fillStyle="#000"; + ctx.beginPath(); + ctx.rect(2,0,this.size[0]-4,20); + ctx.stroke(); + + ctx.fillStyle=this.properties["wcolor"]; + ctx.beginPath(); + ctx.rect(2+(this.size[0]-4-20)*this.value,0, 20,20); + ctx.fill(); + } + + WidgetHSlider.prototype.onDrawImage = function(ctx) + { + if(!this.imgfg || !this.imgfg.width) + return; + + //border + ctx.lineWidth = 1; + ctx.fillStyle="#000"; + ctx.fillRect(2,9,this.size[0]-4,2); + + ctx.strokeStyle= "#333"; + ctx.beginPath(); + ctx.moveTo(2,9); + ctx.lineTo(this.size[0]-4,9); + ctx.stroke(); + + ctx.strokeStyle= "#AAA"; + ctx.beginPath(); + ctx.moveTo(2,11); + ctx.lineTo(this.size[0]-4,11); + ctx.stroke(); + + ctx.drawImage(this.imgfg, 2+(this.size[0]-4)*this.value - this.imgfg.width*0.5,-this.imgfg.height*0.5 + 10); + }, + + WidgetHSlider.prototype.onDrawForeground = function(ctx) + { + this.onDrawImage(ctx); + } + + WidgetHSlider.prototype.onExecute = function() + { + this.properties["value"] = this.properties["min"] + (this.properties["max"] - this.properties["min"]) * this.value; + this.setOutputData(0, this.properties["value"] ); + this.boxcolor = colorToString([this.value,this.value,this.value]); + } + + WidgetHSlider.prototype.onMouseDown = function(e) + { + if(e.canvasY - this.pos[1] < 0) + return false; + + this.oldmouse = [ e.canvasX - this.pos[0], e.canvasY - this.pos[1] ]; + this.captureInput(true); + return true; + } + + WidgetHSlider.prototype.onMouseMove = function(e) + { + if(!this.oldmouse) return; + + var m = [ e.canvasX - this.pos[0], e.canvasY - this.pos[1] ]; + + var v = this.value; + var delta = (m[0] - this.oldmouse[0]); + v += delta / this.size[0]; + if(v > 1.0) v = 1.0; + else if(v < 0.0) v = 0.0; + + this.value = v; + + this.oldmouse = m; + this.setDirtyCanvas(true); + } + + WidgetHSlider.prototype.onMouseUp = function(e) + { + this.oldmouse = null; + this.captureInput(false); + } + + WidgetHSlider.prototype.onMouseLeave = function(e) + { + //this.oldmouse = null; + } + + WidgetHSlider.prototype.onPropertyChange = function(name,value) + { + if(name=="wcolor") + this.properties[name] = value; + else + return false; + return true; + } + + LiteGraph.registerNodeType("widget/hslider", WidgetHSlider ); + + + function WidgetProgress() + { + this.size = [160,26]; + this.addInput("",'number'); + this.properties = {min:0,max:1,value:0,wcolor:"#AAF"}; + } + + WidgetProgress.title = "Progress"; + WidgetProgress.desc = "Shows data in linear progress"; + + WidgetProgress.prototype.onExecute = function() + { + var v = this.getInputData(0); + if( v != undefined ) + this.properties["value"] = v; + } + + WidgetProgress.prototype.onDrawForeground = function(ctx) + { + //border + ctx.lineWidth = 1; + ctx.fillStyle=this.properties.wcolor; + var v = (this.properties.value - this.properties.min) / (this.properties.max - this.properties.min); + v = Math.min(1,v); + v = Math.max(0,v); + ctx.fillRect(2,2,(this.size[0]-4)*v,this.size[1]-4); + } + + LiteGraph.registerNodeType("widget/progress", WidgetProgress); + + + /* LiteGraph.registerNodeType("widget/kpad",{ title: "KPad", desc: "bidimensional slider", @@ -5250,6 +4993,7 @@ LiteGraph.registerNodeType("network/network_callback",{ }); + LiteGraph.registerNodeType("widget/button", { title: "Button", desc: "A send command button", @@ -5367,298 +5111,774 @@ LiteGraph.registerNodeType("network/network_callback",{ return true; } }); + */ - 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() + + function WidgetText() + { + this.addInputs("",0); + this.properties = { value:"...",font:"Arial", fontsize:18, color:"#AAA", align:"left", glowSize:0, decimals:1 }; + } + + WidgetText.title = "Text"; + WidgetText.desc = "Shows the input value"; + WidgetText.widgets = [{name:"resize",text:"Resize box",type:"button"},{name:"led_text",text:"LED",type:"minibutton"},{name:"normal_text",text:"Normal",type:"minibutton"}]; + + WidgetText.prototype.onDrawForeground = function(ctx) + { + //ctx.fillStyle="#000"; + //ctx.fillRect(0,0,100,60); + ctx.fillStyle = this.properties["color"]; + var v = this.properties["value"]; + + if(this.properties["glowSize"]) { - var v = this.getInputData(0); - if( v != undefined ) - this.properties["value"] = v; - }, - onDrawForeground: function(ctx) - { - //border - ctx.lineWidth = 1; - ctx.fillStyle=this.properties.wcolor; - var v = (this.properties.value - this.properties.min) / (this.properties.max - this.properties.min); - v = Math.min(1,v); - v = Math.max(0,v); - ctx.fillRect(2,2,(this.size[0]-4)*v,this.size[1]-4); + ctx.shadowColor = this.properties["color"]; + ctx.shadowOffsetX = 0; + ctx.shadowOffsetY = 0; + ctx.shadowBlur = this.properties["glowSize"]; } - }); - - 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(ctx) - { - //ctx.fillStyle="#000"; - //ctx.fillRect(0,0,100,60); - ctx.fillStyle = this.properties["color"]; - var v = this.properties["value"]; - - if(this.properties["glowSize"]) - { - ctx.shadowColor = this.properties["color"]; - ctx.shadowOffsetX = 0; - ctx.shadowOffsetY = 0; - ctx.shadowBlur = this.properties["glowSize"]; - } - else - ctx.shadowColor = "transparent"; - - var fontsize = this.properties["fontsize"]; - - ctx.textAlign = this.properties["align"]; - ctx.font = fontsize.toString() + "px " + this.properties["font"]; - this.str = typeof(v) == 'number' ? v.toFixed(this.properties["decimals"]) : v; - - if( typeof(this.str) == 'string') - { - var lines = this.str.split("\\n"); - for(var i in lines) - ctx.fillText(lines[i],this.properties["align"] == "left" ? 15 : this.size[0] - 15, fontsize * -0.15 + fontsize * (parseInt(i)+1) ); - } - + else ctx.shadowColor = "transparent"; - this.last_ctx = ctx; - ctx.textAlign = "left"; - }, - onExecute: function() + var fontsize = this.properties["fontsize"]; + + ctx.textAlign = this.properties["align"]; + ctx.font = fontsize.toString() + "px " + this.properties["font"]; + this.str = typeof(v) == 'number' ? v.toFixed(this.properties["decimals"]) : v; + + if( typeof(this.str) == 'string') { - var v = this.getInputData(0); - if(v != null) - this.properties["value"] = v; - else - this.properties["value"] = ""; - this.setDirtyCanvas(true); - }, - - resize: function() - { - if(!this.last_ctx) return; - var lines = this.str.split("\\n"); - this.last_ctx.font = this.properties["fontsize"] + "px " + this.properties["font"]; - var max = 0; for(var i in lines) - { - var w = this.last_ctx.measureText(lines[i]).width; - if(max < w) max = w; - } - this.size[0] = max + 20; - this.size[1] = 4 + lines.length * this.properties["fontsize"]; - - this.setDirtyCanvas(true); - }, - - onWidget: function(e,widget) - { - if(widget.name == "resize") - this.resize(); - else if (widget.name == "led_text") - { - this.properties["font"] = "Digital"; - this.properties["glowSize"] = 4; - this.setDirtyCanvas(true); - } - else if (widget.name == "normal_text") - { - this.properties["font"] = "Arial"; - this.setDirtyCanvas(true); - } - }, - - onPropertyChange: function(name,value) - { - this.properties[name] = value; - this.str = typeof(value) == 'number' ? value.toFixed(3) : value; - //this.resize(); - return true; + ctx.fillText(lines[i],this.properties["align"] == "left" ? 15 : this.size[0] - 15, fontsize * -0.15 + fontsize * (parseInt(i)+1) ); } - }); - LiteGraph.registerNodeType("widget/panel", { - title: "Panel", - desc: "Non interactive panel", + ctx.shadowColor = "transparent"; + this.last_ctx = ctx; + ctx.textAlign = "left"; + } - widgets: [{name:"update",text:"Update",type:"button"}], - size: [200,100], - properties:{borderColor:"#ffffff",bgcolorTop:"#f0f0f0",bgcolorBottom:"#e0e0e0",shadowSize:2, borderRadius:3}, + WidgetText.prototype.onExecute = function() + { + var v = this.getInputData(0); + if(v != null) + this.properties["value"] = v; + else + this.properties["value"] = ""; + this.setDirtyCanvas(true); + } - createGradient: function(ctx) + WidgetText.prototype.resize = function() + { + if(!this.last_ctx) return; + + var lines = this.str.split("\\n"); + this.last_ctx.font = this.properties["fontsize"] + "px " + this.properties["font"]; + var max = 0; + for(var i in lines) { - if(this.properties["bgcolorTop"] == "" || this.properties["bgcolorBottom"] == "") - { - this.lineargradient = 0; - return; - } + var w = this.last_ctx.measureText(lines[i]).width; + if(max < w) max = w; + } + this.size[0] = max + 20; + this.size[1] = 4 + lines.length * this.properties["fontsize"]; - this.lineargradient = ctx.createLinearGradient(0,0,0,this.size[1]); - this.lineargradient.addColorStop(0,this.properties["bgcolorTop"]); - this.lineargradient.addColorStop(1,this.properties["bgcolorBottom"]); - }, + this.setDirtyCanvas(true); + } - onDrawForeground: function(ctx) + WidgetText.prototype.onWidget = function(e,widget) + { + if(widget.name == "resize") + this.resize(); + else if (widget.name == "led_text") { - if(this.lineargradient == null) - this.createGradient(ctx); + this.properties["font"] = "Digital"; + this.properties["glowSize"] = 4; + this.setDirtyCanvas(true); + } + else if (widget.name == "normal_text") + { + this.properties["font"] = "Arial"; + this.setDirtyCanvas(true); + } + } - if(!this.lineargradient) - return; + WidgetText.prototype.onPropertyChange = function(name,value) + { + this.properties[name] = value; + this.str = typeof(value) == 'number' ? value.toFixed(3) : value; + //this.resize(); + return true; + } - ctx.lineWidth = 1; - ctx.strokeStyle = this.properties["borderColor"]; - //ctx.fillStyle = "#ebebeb"; - ctx.fillStyle = this.lineargradient; + LiteGraph.registerNodeType("widget/text", WidgetText ); - if(this.properties["shadowSize"]) - { - ctx.shadowColor = "#000"; - ctx.shadowOffsetX = 0; - ctx.shadowOffsetY = 0; - ctx.shadowBlur = this.properties["shadowSize"]; - } - else - ctx.shadowColor = "transparent"; - ctx.roundRect(0,0,this.size[0]-1,this.size[1]-1,this.properties["shadowSize"]); - ctx.fill(); + function WidgetPanel() + { + this.size = [200,100]; + this.properties = {borderColor:"#ffffff",bgcolorTop:"#f0f0f0",bgcolorBottom:"#e0e0e0",shadowSize:2, borderRadius:3}; + } + + WidgetPanel.title = "Panel"; + WidgetPanel.desc = "Non interactive panel"; + WidgetPanel.widgets = [{name:"update",text:"Update",type:"button"}]; + + + WidgetPanel.prototype.createGradient = function(ctx) + { + if(this.properties["bgcolorTop"] == "" || this.properties["bgcolorBottom"] == "") + { + this.lineargradient = 0; + return; + } + + this.lineargradient = ctx.createLinearGradient(0,0,0,this.size[1]); + this.lineargradient.addColorStop(0,this.properties["bgcolorTop"]); + this.lineargradient.addColorStop(1,this.properties["bgcolorBottom"]); + } + + WidgetPanel.prototype.onDrawForeground = function(ctx) + { + if(this.lineargradient == null) + this.createGradient(ctx); + + if(!this.lineargradient) + return; + + ctx.lineWidth = 1; + ctx.strokeStyle = this.properties["borderColor"]; + //ctx.fillStyle = "#ebebeb"; + ctx.fillStyle = this.lineargradient; + + if(this.properties["shadowSize"]) + { + ctx.shadowColor = "#000"; + ctx.shadowOffsetX = 0; + ctx.shadowOffsetY = 0; + ctx.shadowBlur = this.properties["shadowSize"]; + } + else ctx.shadowColor = "transparent"; - ctx.stroke(); - }, - onWidget: function(e,widget) + ctx.roundRect(0,0,this.size[0]-1,this.size[1]-1,this.properties["shadowSize"]); + ctx.fill(); + ctx.shadowColor = "transparent"; + ctx.stroke(); + } + + WidgetPanel.prototype.onWidget = function(e,widget) + { + if(widget.name == "update") { - if(widget.name == "update") - { - this.lineargradient = null; - this.setDirtyCanvas(true); - } - } - }); - -LiteGraph.registerNodeType("color/palette",{ - title: "Palette", - desc: "Generates a color", - - inputs: [["f","number"]], - outputs: [["Color","color"]], - properties: {colorA:"#444444",colorB:"#44AAFF",colorC:"#44FFAA",colorD:"#FFFFFF"}, - - onExecute: function() - { - var c = []; - - if (this.properties.colorA != null) - c.push( hex2num( this.properties.colorA ) ); - if (this.properties.colorB != null) - c.push( hex2num( this.properties.colorB ) ); - if (this.properties.colorC != null) - c.push( hex2num( this.properties.colorC ) ); - if (this.properties.colorD != null) - c.push( hex2num( this.properties.colorD ) ); - - var f = this.getInputData(0); - if(f == null) f = 0.5; - if (f > 1.0) - f = 1.0; - else if (f < 0.0) - f = 0.0; - - if(c.length == 0) - return; - - var result = [0,0,0]; - if(f == 0) - result = c[0]; - else if(f == 1) - result = c[ c.length - 1]; - else - { - var pos = (c.length - 1)* f; - var c1 = c[ Math.floor(pos) ]; - var c2 = c[ Math.floor(pos)+1 ]; - var t = pos - Math.floor(pos); - result[0] = c1[0] * (1-t) + c2[0] * (t); - result[1] = c1[1] * (1-t) + c2[1] * (t); - result[2] = c1[2] * (1-t) + c2[2] * (t); - } - - /* - c[0] = 1.0 - Math.abs( Math.sin( 0.1 * reModular.getTime() * Math.PI) ); - c[1] = Math.abs( Math.sin( 0.07 * reModular.getTime() * Math.PI) ); - c[2] = Math.abs( Math.sin( 0.01 * reModular.getTime() * Math.PI) ); - */ - - for(var i in result) - result[i] /= 255; - - this.boxcolor = colorToString(result); - this.setOutputData(0, result); - } - }); - -LiteGraph.registerNodeType("graphics/frame", { - title: "Frame", - desc: "Frame viewerew", - - inputs: [["","image"]], - size: [200,200], - widgets: [{name:"resize",text:"Resize box",type:"button"},{name:"view",text:"View Image",type:"button"}], - - onDrawBackground: function(ctx) - { - if(this.frame) - ctx.drawImage(this.frame, 0,0,this.size[0],this.size[1]); - }, - - onExecute: function() - { - this.frame = this.getInputData(0); + this.lineargradient = null; this.setDirtyCanvas(true); - }, - - onWidget: function(e,widget) - { - if(widget.name == "resize" && this.frame) - { - var width = this.frame.width; - var height = this.frame.height; - - if(!width && this.frame.videoWidth != null ) - { - width = this.frame.videoWidth; - height = this.frame.videoHeight; - } - - if(width && height) - this.size = [width, height]; - this.setDirtyCanvas(true,true); - } - else if(widget.name == "view") - this.show(); - }, - - show: function() - { - //var str = this.canvas.toDataURL("image/png"); - if(showElement && this.frame) - showElement(this.frame); } - }); + } + LiteGraph.registerNodeType("widget/panel", WidgetPanel ); + +})(); +(function(){ + + +function MathRand() +{ + this.addOutput("value","number"); + this.properties = { min:0, max:1 }; + this.size = [60,20]; +} + +MathRand.title = "Rand"; +MathRand.desc = "Random number"; + +MathRand.prototype.onExecute = function() +{ + var min = this.properties.min; + var max = this.properties.max; + this._last_v = Math.random() * (max-min) + min; + this.setOutputData(0, this._last_v ); +} + +MathRand.prototype.onDrawBackground = function(ctx) +{ + //show the current value + if(this._last_v) + this.outputs[0].label = this._last_v.toFixed(3); + else + this.outputs[0].label = "?"; +} + +LiteGraph.registerNodeType("math/rand", MathRand); + +//Math clamp +function MathClamp() +{ + this.addInput("in","number"); + this.addOutput("out","number"); + this.size = [60,20]; + this.properties = {min:0, max:1}; +} + +MathClamp.title = "Clamp"; +MathClamp.desc = "Clamp number between min and max"; + +MathClamp.prototype.onExecute = function() +{ + var v = this.getInputData(0); + if(v == null) return; + v = Math.max(this.properties.min,v); + v = Math.min(this.properties.max,v); + this.setOutputData(0, v ); +} + +LiteGraph.registerNodeType("math/clamp", MathClamp ); + + +//Math ABS +function MathAbs() +{ + this.addInput("in","number"); + this.addOutput("out","number"); + this.size = [60,20]; +} + +MathAbs.title = "Abs"; +MathAbs.desc = "Absolute"; + +MathAbs.prototype.onExecute = function() +{ + var v = this.getInputData(0); + if(v == null) return; + this.setOutputData(0, Math.abs(v) ); +} + +LiteGraph.registerNodeType("math/abs", MathAbs); + + +//Math Floor +function MathFloor() +{ + this.addInput("in","number"); + this.addOutput("out","number"); + this.size = [60,20]; +} + +MathFloor.title = "Floor"; +MathFloor.desc = "Floor number to remove fractional part"; + +MathFloor.prototype.onExecute = function() +{ + var v = this.getInputData(0); + if(v == null) return; + this.setOutputData(0, v|1 ); +} + +LiteGraph.registerNodeType("math/floor", MathFloor ); + + +//Math frac +function MathFrac() +{ + this.addInput("in","number"); + this.addOutput("out","number"); + this.size = [60,20]; +} + +MathFrac.title = "Frac"; +MathFrac.desc = "Returns fractional part"; + +MathFrac.prototype.onExecute = function() +{ + var v = this.getInputData(0); + if(v == null) + return; + this.setOutputData(0, v%1 ); +} + +LiteGraph.registerNodeType("math/frac",MathFrac); + + +//Math scale +function MathScale() +{ + this.addInput("in","number",{label:""}); + this.addOutput("out","number",{label:""}); + this.size = [60,20]; + this.properties = {"factor":1}; +} + +MathScale.title = "Scale"; +MathScale.desc = "v * factor"; + +MathScale.prototype.onExecute = function() +{ + var value = this.getInputData(0); + if(value != null) + this.setOutputData(0, value * this.properties.factor ); +} + +LiteGraph.registerNodeType("math/scale", MathScale ); + + +//Math operation +function MathOperation() +{ + this.addInput("A","number"); + this.addInput("B","number"); + this.addOutput("A+B","number"); + this.size = [80,20]; + this.properties = {A:1.0, B:1.0}; +} + +MathOperation.title = "Operation"; +MathOperation.desc = "Easy math operators"; + +MathOperation.prototype.setValue = function(v) +{ + if( typeof(v) == "string") v = parseFloat(v); + this.properties["value"] = v; + this.setDirtyCanvas(true); +} + +MathOperation.prototype.onExecute = function() +{ + var A = this.getInputData(0); + var B = this.getInputData(1); + if(A!=null) + this.properties["A"] = A; + else + A = this.properties["A"]; + + if(B!=null) + this.properties["B"] = B; + else + B = this.properties["B"]; + + for(var i = 0, l = this.outputs.length; i < l; ++i) + { + var output = this.outputs[i]; + if(!output.links || !output.links.length) + continue; + var value = 0; + switch( output.name ) + { + case "A+B": value = A+B; break; + case "A-B": value = A-B; break; + case "A*B": value = A*B; break; + case "A/B": value = A/B; break; + } + this.setOutputData(i, value ); + } +} + +MathOperation.prototype.onGetOutputs = function() +{ + return [["A-B","number"],["A*B","number"],["A/B","number"]]; +} + +LiteGraph.registerNodeType("math/operation", MathOperation ); + + +//Math compare +function MathCompare() +{ + this.addInputs( "A","number" ); + this.addInputs( "B","number" ); + this.addOutputs("A==B","number"); + this.addOutputs("A!=B","number"); + this.properties = {A:0,B:0}; +} + + +MathCompare.title = "Compare"; +MathCompare.desc = "compares between two values"; + +MathCompare.prototype.onExecute = function() +{ + var A = this.getInputData(0); + var B = this.getInputData(1); + if(A!=null) + this.properties["A"] = A; + else + A = this.properties["A"]; + + if(B!=null) + this.properties["B"] = B; + else + B = this.properties["B"]; + + for(var i = 0, l = this.outputs.length; i < l; ++i) + { + var output = this.outputs[i]; + if(!output.links || !output.links.length) + continue; + switch( output.name ) + { + case "A==B": value = A==B; break; + case "A!=B": value = A!=B; break; + case "A>B": value = A>B; break; + case "A=B": value = A>=B; break; + } + this.setOutputData(i, value ); + } +}; + +MathCompare.prototype.onGetOutputs = function() +{ + return [["A==B","number"],["A!=B","number"],["A>B","number"],["A=B","number"],["A<=B","number"]]; +} + +LiteGraph.registerNodeType("math/compare",MathCompare); + +//Math Trigonometry +function MathTrigonometry() +{ + this.addInputs("v","number"); + this.addOutputs("sin","number"); + this.properties = {amplitude:1.0}; + this.size = [100,20]; + this.bgImageUrl = "nodes/imgs/icon-sin.png"; +} + +MathTrigonometry.title = "Trigonometry"; +MathTrigonometry.desc = "Sin Cos Tan"; + +MathTrigonometry.prototype.onExecute = function() +{ + var v = this.getInputData(0); + var amp = this.properties["amplitude"]; + for(var i = 0, l = this.outputs.length; i < l; ++i) + { + var output = this.outputs[i]; + switch( output.name ) + { + case "sin": value = Math.sin(v); break; + case "cos": value = Math.cos(v); break; + case "tan": value = Math.tan(v); break; + case "asin": value = Math.asin(v); break; + case "acos": value = Math.acos(v); break; + case "atan": value = Math.atan(v); break; + } + this.setOutputData(i, amp * value ); + } +} + +MathTrigonometry.prototype.onGetOutputs = function() +{ + return [["sin","number"],["cos","number"],["tan","number"],["asin","number"],["acos","number"],["atan","number"]]; +} + + +LiteGraph.registerNodeType("math/trigonometry", MathTrigonometry ); + + + +//math library for safe math operations without eval +if(window.math) +{ + function MathFormula() + { + this.addInputs("x","number"); + this.addInputs("y","number"); + this.addOutputs("","number"); + this.properties = {x:1.0, y:1.0, formula:"x+y"}; + } + + MathFormula.title = "Formula"; + MathFormula.desc = "Compute safe formula"; + + MathFormula.prototype.onExecute = function() + { + var x = this.getInputData(0); + var y = this.getInputData(1); + if(x != null) + this.properties["x"] = x; + else + x = this.properties["x"]; + + if(y!=null) + this.properties["y"] = y; + else + y = this.properties["y"]; + + var f = this.properties["formula"]; + var value = math.eval(f,{x:x,y:y,T: this.graph.globaltime }); + this.setOutputData(0, value ); + } + + MathFormula.prototype.onDrawBackground = function() + { + var f = this.properties["formula"]; + this.outputs[0].label = f; + } + + MathFormula.prototype.onGetOutputs = function() + { + return [["A-B","number"],["A*B","number"],["A/B","number"]]; + } + + LiteGraph.registerNodeType("math/formula", MathFormula ); +} + + +//if glMatrix is installed... +if(window.glMatrix) +{ + function Math3DVec3ToXYZ() + { + this.addInput("vec3","vec3"); + this.addOutput("x","number"); + this.addOutput("y","number"); + this.addOutput("z","number"); + } + + Math3DVec3ToXYZ.title = "Vec3->XYZ"; + Math3DVec3ToXYZ.desc = "vector 3 to components"; + + Math3DVec3ToXYZ.prototype.onExecute = function() + { + var v = this.getInputData(0); + if(v == null) return; + + this.setOutputData( 0, v[0] ); + this.setOutputData( 1, v[1] ); + this.setOutputData( 2, v[2] ); + } + + LiteGraph.registerNodeType("math3d/vec3-to-xyz", Math3DVec3ToXYZ ); + + + function Math3DXYZToVec3() + { + this.addInputs([["x","number"],["y","number"],["z","number"]]); + this.addOutput("vec3","vec3"); + } + + Math3DXYZToVec3.title = "XYZ->Vec3"; + Math3DXYZToVec3.desc = "components to vector3"; + + Math3DXYZToVec3.prototype.onExecute = function() + { + var x = this.getInputData(0); + if(x == null) x = 0; + var y = this.getInputData(1); + if(y == null) y = 0; + var z = this.getInputData(2); + if(z == null) z = 0; + + this.setOutputData( 0, vec3.fromValues(x,y,z) ); + } + + LiteGraph.registerNodeType("math3d/xyz-to-vec3", Math3DXYZToVec3 ); + + + function Math3DRotation() + { + this.addInputs([["degrees","number"],["axis","vec3"]]); + this.addOutput("quat","quat"); + this.properties = { angle:90.0, axis: vec3.fromValues(0,1,0) }; + } + + Math3DRotation.title = "Rotation"; + Math3DRotation.desc = "quaternion rotation"; + + Math3DRotation.prototype.onExecute = function() + { + var angle = this.getInputData(0); + if(angle == null) angle = this.properties.angle; + var axis = this.getInputData(1); + if(axis == null) axis = this.properties.axis; + + var R = quat.setAxisAngle(quat.create(), axis, angle * 0.0174532925 ); + this.setOutputData( 0, R ); + } + + + LiteGraph.registerNodeType("math3d/rotation", Math3DRotation ); + + + //Math3D rotate vec3 + function Math3DRotateVec3() + { + this.addInputs([["vec3","vec3"],["quat","quat"]]); + this.addOutput("result","vec3"); + this.properties = { vec: [0,0,1] }; + } + + Math3DRotateVec3.title = "Rot. Vec3"; + Math3DRotateVec3.desc = "rotate a point"; + + Math3DRotateVec3.prototype.onExecute = function() + { + var vec = this.getInputData(0); + if(vec == null) vec = this.properties.vec; + var quat = this.getInputData(1); + if(quat == null) + this.setOutputData(vec); + else + this.setOutputData( 0, vec3.transformQuat( vec3.create(), vec, quat ) ); + } + + LiteGraph.registerNodeType("math3d/rotate_vec3", Math3DRotateVec3); + + + + function Math3DMultQuat() + { + this.addInputs( [["A","quat"],["B","quat"]] ); + this.addOutput( "A*B","quat" ); + } + + Math3DMultQuat.title = "Mult. Quat"; + Math3DMultQuat.desc = "rotate quaternion"; + + Math3DMultQuat.prototype.onExecute = function() + { + var A = this.getInputData(0); + if(A == null) return; + var B = this.getInputData(1); + if(B == null) return; + + var R = quat.multiply(quat.create(), A,B); + this.setOutputData( 0, R ); + } + + LiteGraph.registerNodeType("math3d/mult-quat", Math3DMultQuat ); + +} //glMatrix + +})(); +(function(){ + + +function ColorPalette() +{ + this.addInput("f","number"); + this.addOutput("Color","color"); + this.properties = {colorA:"#444444",colorB:"#44AAFF",colorC:"#44FFAA",colorD:"#FFFFFF"}; + +} + +ColorPalette.title = "Palette"; +ColorPalette.desc = "Generates a color"; + +ColorPalette.prototype.onExecute = function() +{ + var c = []; + + if (this.properties.colorA != null) + c.push( hex2num( this.properties.colorA ) ); + if (this.properties.colorB != null) + c.push( hex2num( this.properties.colorB ) ); + if (this.properties.colorC != null) + c.push( hex2num( this.properties.colorC ) ); + if (this.properties.colorD != null) + c.push( hex2num( this.properties.colorD ) ); + + var f = this.getInputData(0); + if(f == null) f = 0.5; + if (f > 1.0) + f = 1.0; + else if (f < 0.0) + f = 0.0; + + if(c.length == 0) + return; + + var result = [0,0,0]; + if(f == 0) + result = c[0]; + else if(f == 1) + result = c[ c.length - 1]; + else + { + var pos = (c.length - 1)* f; + var c1 = c[ Math.floor(pos) ]; + var c2 = c[ Math.floor(pos)+1 ]; + var t = pos - Math.floor(pos); + result[0] = c1[0] * (1-t) + c2[0] * (t); + result[1] = c1[1] * (1-t) + c2[1] * (t); + result[2] = c1[2] * (1-t) + c2[2] * (t); + } + + /* + c[0] = 1.0 - Math.abs( Math.sin( 0.1 * reModular.getTime() * Math.PI) ); + c[1] = Math.abs( Math.sin( 0.07 * reModular.getTime() * Math.PI) ); + c[2] = Math.abs( Math.sin( 0.01 * reModular.getTime() * Math.PI) ); + */ + + for(var i in result) + result[i] /= 255; + + this.boxcolor = colorToString(result); + this.setOutputData(0, result); +} + + +LiteGraph.registerNodeType("color/palette", ColorPalette ); + + +function ImageFrame() +{ + this.addInput("","image"); + this.size = [200,200]; +} + +ImageFrame.title = "Frame"; +ImageFrame.desc = "Frame viewerew"; +ImageFrame.widgets = [{name:"resize",text:"Resize box",type:"button"},{name:"view",text:"View Image",type:"button"}]; + + +ImageFrame.prototype.onDrawBackground = function(ctx) +{ + if(this.frame) + ctx.drawImage(this.frame, 0,0,this.size[0],this.size[1]); +} + +ImageFrame.prototype.onExecute = function() +{ + this.frame = this.getInputData(0); + this.setDirtyCanvas(true); +} + +ImageFrame.prototype.onWidget = function(e,widget) +{ + if(widget.name == "resize" && this.frame) + { + var width = this.frame.width; + var height = this.frame.height; + + if(!width && this.frame.videoWidth != null ) + { + width = this.frame.videoWidth; + height = this.frame.videoHeight; + } + + if(width && height) + this.size = [width, height]; + this.setDirtyCanvas(true,true); + } + else if(widget.name == "view") + this.show(); +} + +ImageFrame.prototype.show = function() +{ + //var str = this.canvas.toDataURL("image/png"); + if(showElement && this.frame) + showElement(this.frame); +} + + +LiteGraph.registerNodeType("graphics/frame", ImageFrame ); + + + +/* LiteGraph.registerNodeType("visualization/graph", { desc: "Shows a graph of the inputs", @@ -5667,13 +5887,6 @@ LiteGraph.registerNodeType("visualization/graph", { properties: {min:-1,max:1,bgColor:"#000"}, onDrawBackground: function(ctx) { - /* - ctx.save(); - ctx.beginPath(); - ctx.rect(2,2,this.size[0] - 4, this.size[1]-4); - ctx.clip(); - //*/ - var colors = ["#FFF","#FAA","#AFA","#AAF"]; if(this.properties.bgColor != null && this.properties.bgColor != "") @@ -5714,7 +5927,6 @@ LiteGraph.registerNodeType("visualization/graph", { ctx.stroke(); } } - //*/ //ctx.restore(); }, @@ -5745,500 +5957,443 @@ LiteGraph.registerNodeType("visualization/graph", { this.setDirtyCanvas(true); } }); +*/ -LiteGraph.registerNodeType("graphics/supergraph", { - title: "Supergraph", - desc: "Shows a nice circular graph", +function ImageFade() +{ + this.addInputs([["img1","image"],["img2","image"],["fade","number"]]); + this.addInput("","image"); + this.properties = {fade:0.5,width:512,height:512}; +} - inputs: [["x","number"],["y","number"],["c","color"]], - outputs: [["","image"]], - widgets: [{name:"clear_alpha",text:"Clear Alpha",type:"minibutton"},{name:"clear_color",text:"Clear color",type:"minibutton"}], - properties: {size:256,bgcolor:"#000",lineWidth:1}, - bgcolor: "#000", - flags: {allow_fastrender:true}, - onLoad: function() +ImageFade.title = "Image fade"; +ImageFade.desc = "Fades between images"; +ImageFade.widgets = [{name:"resizeA",text:"Resize to A",type:"button"},{name:"resizeB",text:"Resize to B",type:"button"}]; + +ImageFade.prototype.onAdded = function() +{ + this.createCanvas(); + var ctx = this.canvas.getContext("2d"); + ctx.fillStyle = "#000"; + ctx.fillRect(0,0,this.properties["width"],this.properties["height"]); +} + +ImageFade.prototype.createCanvas = function() +{ + this.canvas = document.createElement("canvas"); + this.canvas.width = this.properties["width"]; + this.canvas.height = this.properties["height"]; +} + +ImageFade.prototype.onExecute = function() +{ + var ctx = this.canvas.getContext("2d"); + this.canvas.width = this.canvas.width; + + var A = this.getInputData(0); + if (A != null) + { + ctx.drawImage(A,0,0,this.canvas.width, this.canvas.height); + } + + var fade = this.getInputData(2); + if(fade == null) + fade = this.properties["fade"]; + else + this.properties["fade"] = fade; + + ctx.globalAlpha = fade; + var B = this.getInputData(1); + if (B != null) + { + ctx.drawImage(B,0,0,this.canvas.width, this.canvas.height); + } + ctx.globalAlpha = 1.0; + + this.setOutputData(0,this.canvas); + this.setDirtyCanvas(true); +} + +LiteGraph.registerNodeType("graphics/imagefade", ImageFade); + + +function GraphicsImage() +{ + this.inputs = []; + this.addOutput("frame","image"); + this.properties = {"url":""}; +} + +GraphicsImage.title = "Image"; +GraphicsImage.desc = "Image loader"; +GraphicsImage.widgets = [{name:"load",text:"Load",type:"button"}]; + + +GraphicsImage.prototype.onAdded = function() +{ + if(this.properties["url"] != "" && this.img == null) + { + this.loadImage(this.properties["url"]); + } +} + + +GraphicsImage.prototype.onExecute = function() +{ + if(!this.img) + this.boxcolor = "#000"; + if(this.img && this.img.width) + this.setOutputData(0,this.img); + else + this.setOutputData(0,null); + if(this.img.dirty) + this.img.dirty = false; +} + +GraphicsImage.prototype.onPropertyChange = function(name,value) +{ + this.properties[name] = value; + if (name == "url" && value != "") + this.loadImage(value); + + return true; +} + +GraphicsImage.prototype.loadImage = function(url) +{ + if(url == "") + { + this.img = null; + return; + } + + this.trace("loading image..."); + this.img = document.createElement("img"); + this.img.src = "miniproxy.php?url=" + url; + this.boxcolor = "#F95"; + var that = this; + this.img.onload = function() + { + that.trace("Image loaded, size: " + that.img.width + "x" + that.img.height ); + this.dirty = true; + that.boxcolor = "#9F9"; + that.setDirtyCanvas(true); + } +} + +GraphicsImage.prototype.onWidget = function(e,widget) +{ + if(widget.name == "load") + { + this.loadImage(this.properties["url"]); + } +} + +LiteGraph.registerNodeType("graphics/image", GraphicsImage); + + + +function ImageCrop() +{ + this.addInput("","image"); + this.addOutputs("","image"); + this.properties = {width:256,height:256,x:0,y:0,scale:1.0 }; + this.size = [50,20]; +} + +ImageCrop.title = "Crop"; +ImageCrop.desc = "Crop Image"; + +ImageCrop.prototype.onAdded = function() +{ + this.createCanvas(); +} + +ImageCrop.prototype.createCanvas = function() +{ + this.canvas = document.createElement("canvas"); + this.canvas.width = this.properties["width"]; + this.canvas.height = this.properties["height"]; +} + +ImageCrop.prototype.onExecute = function() +{ + var input = this.getInputData(0); + if(!input) return; + + if(input.width) + { + var ctx = this.canvas.getContext("2d"); + + ctx.drawImage(input, -this.properties["x"],-this.properties["y"], input.width * this.properties["scale"], input.height * this.properties["scale"]); + this.setOutputData(0,this.canvas); + } + else + this.setOutputData(0,null); +} + +ImageCrop.prototype.onPropertyChange = function(name,value) +{ + this.properties[name] = value; + + if(name == "scale") + { + this.properties[name] = parseFloat(value); + if(this.properties[name] == 0) { - this.createCanvas(); - }, - - createCanvas: function() - { - this.canvas = document.createElement("canvas"); - this.canvas.width = this.properties["size"]; - this.canvas.height = this.properties["size"]; - this.oldpos = null; - this.clearCanvas(true); - }, + this.trace("Error in scale"); + this.properties[name] = 1.0; + } + } + else + this.properties[name] = parseInt(value); - onExecute: function() - { - var x = this.getInputData(0); - var y = this.getInputData(1); - var c = this.getInputData(2); + this.createCanvas(); - if(x == null && y == null) return; + return true; +} - if(!x) x = 0; - if(!y) y = 0; - x*= 0.95; - y*= 0.95; +LiteGraph.registerNodeType("graphics/cropImage", ImageFade ); - var size = this.properties["size"]; - if(size != this.canvas.width || size != this.canvas.height) - this.createCanvas(); - if (!this.oldpos) - { - this.oldpos = [ (x * 0.5 + 0.5) * size, (y*0.5 + 0.5) * size]; - return; - } +function ImageVideo() +{ + this.addInput("t","number"); + this.addOutputs([["frame","image"],["t","number"],["d","number"]]); + this.properties = {"url":""}; +} - var ctx = this.canvas.getContext("2d"); +ImageVideo.title = "Video"; +ImageVideo.desc = "Video playback"; +ImageVideo.widgets = [{name:"play",text:"PLAY",type:"minibutton"},{name:"stop",text:"STOP",type:"minibutton"},{name:"demo",text:"Demo video",type:"button"},{name:"mute",text:"Mute video",type:"button"}]; - if(c == null) - c = "rgba(255,255,255,0.5)"; - else if(typeof(c) == "object") //array - c = colorToString(c); +ImageVideo.prototype.onExecute = function() +{ + if(!this.properties.url) + return; - //stroke line - ctx.strokeStyle = c; - ctx.beginPath(); - ctx.moveTo( this.oldpos[0], this.oldpos[1] ); - this.oldpos = [ (x * 0.5 + 0.5) * size, (y*0.5 + 0.5) * size]; - ctx.lineTo( this.oldpos[0], this.oldpos[1] ); - ctx.stroke(); + if(this.properties.url != this._video_url) + this.loadVideo(this.properties.url); - this.canvas.dirty = true; - this.setOutputData(0,this.canvas); - }, + if(!this._video || this._video.width == 0) + return; - clearCanvas: function(alpha) - { - var ctx = this.canvas.getContext("2d"); - if(alpha) - { - ctx.clearRect(0,0,this.canvas.width,this.canvas.height); - this.trace("Clearing alpha"); - } - else - { - ctx.fillStyle = this.properties["bgcolor"]; - ctx.fillRect(0,0,this.canvas.width,this.canvas.height); - } - }, - - onWidget: function(e,widget) - { - if(widget.name == "clear_color") - { - this.clearCanvas(false); - } - else if(widget.name == "clear_alpha") - { - this.clearCanvas(true); - } - }, + var t = this.getInputData(0); + if(t && t >= 0 && t <= 1.0) + { + this._video.currentTime = t * this._video.duration; + this._video.pause(); + } - onPropertyChange: function(name,value) - { - if(name == "size") - { - this.properties["size"] = parseInt(value); - this.createCanvas(); - } - else if(name == "bgcolor") - { - this.properties["bgcolor"] = value; - this.createCanvas(); - } - else if(name == "lineWidth") - { - this.properties["lineWidth"] = parseInt(value); - this.canvas.getContext("2d").lineWidth = this.properties["lineWidth"]; - } - else - return false; - - return true; + this._video.dirty = true; + this.setOutputData(0,this._video); + this.setOutputData(1,this._video.currentTime); + this.setOutputData(2,this._video.duration); + this.setDirtyCanvas(true); +} + +ImageVideo.prototype.onStart = function() +{ + this.play(); +} + +ImageVideo.prototype.onStop = function() +{ + this.stop(); +} + +ImageVideo.prototype.loadVideo = function(url) +{ + this._video_url = url; + + this._video = document.createElement("video"); + this._video.src = url; + this._video.type = "type=video/mp4"; + + this._video.muted = true; + this._video.autoplay = true; + + var that = this; + this._video.addEventListener("loadedmetadata",function(e) { + //onload + that.trace("Duration: " + this.duration + " seconds"); + that.trace("Size: " + this.videoWidth + "," + this.videoHeight); + that.setDirtyCanvas(true); + this.width = this.videoWidth; + this.height = this.videoHeight; + }); + this._video.addEventListener("progress",function(e) { + //onload + //that.trace("loading..."); + }); + this._video.addEventListener("error",function(e) { + console.log("Error loading video: " + this.src); + that.trace("Error loading video: " + this.src); + if (this.error) { + switch (this.error.code) { + case this.error.MEDIA_ERR_ABORTED: + that.trace("You stopped the video."); + break; + case this.error.MEDIA_ERR_NETWORK: + that.trace("Network error - please try again later."); + break; + case this.error.MEDIA_ERR_DECODE: + that.trace("Video is broken.."); + break; + case this.error.MEDIA_ERR_SRC_NOT_SUPPORTED: + that.trace("Sorry, your browser can't play this video."); + break; + } } }); - -LiteGraph.registerNodeType("graphics/imagefade", { - title: "Image fade", - desc: "Fades between images", - - inputs: [["img1","image"],["img2","image"],["fade","number"]], - outputs: [["","image"]], - properties: {fade:0.5,width:512,height:512}, - widgets: [{name:"resizeA",text:"Resize to A",type:"button"},{name:"resizeB",text:"Resize to B",type:"button"}], - - onLoad: function() - { - this.createCanvas(); - var ctx = this.canvas.getContext("2d"); - ctx.fillStyle = "#000"; - ctx.fillRect(0,0,this.properties["width"],this.properties["height"]); - }, - - createCanvas: function() - { - this.canvas = document.createElement("canvas"); - this.canvas.width = this.properties["width"]; - this.canvas.height = this.properties["height"]; - }, - - onExecute: function() - { - var ctx = this.canvas.getContext("2d"); - this.canvas.width = this.canvas.width; - - var A = this.getInputData(0); - if (A != null) - { - ctx.drawImage(A,0,0,this.canvas.width, this.canvas.height); - } - - var fade = this.getInputData(2); - if(fade == null) - fade = this.properties["fade"]; - else - this.properties["fade"] = fade; - - ctx.globalAlpha = fade; - var B = this.getInputData(1); - if (B != null) - { - ctx.drawImage(B,0,0,this.canvas.width, this.canvas.height); - } - ctx.globalAlpha = 1.0; - - this.setOutputData(0,this.canvas); - this.setDirtyCanvas(true); - } + this._video.addEventListener("ended",function(e) { + that.trace("Ended."); + this.play(); //loop }); -LiteGraph.registerNodeType("graphics/image", { - title: "Image", - desc: "Image loader", + //document.body.appendChild(this.video); +} - inputs: [], - outputs: [["frame","image"]], - properties: {"url":""}, - widgets: [{name:"load",text:"Load",type:"button"}], +ImageVideo.prototype.onPropertyChange = function(name,value) +{ + this.properties[name] = value; + if (name == "url" && value != "") + this.loadVideo(value); - onLoad: function() - { - if(this.properties["url"] != "" && this.img == null) - { - this.loadImage(this.properties["url"]); - } - }, + return true; +} - onStart: function() - { - }, +ImageVideo.prototype.play = function() +{ + if(this._video) + this._video.play(); +} - onExecute: function() - { - if(!this.img) - this.boxcolor = "#000"; - if(this.img && this.img.width) - this.setOutputData(0,this.img); - else - this.setOutputData(0,null); - if(this.img.dirty) - this.img.dirty = false; - }, +ImageVideo.prototype.playPause = function() +{ + if(!this._video) + return; + if(this._video.paused) + this.play(); + else + this.pause(); +} - onPropertyChange: function(name,value) - { - this.properties[name] = value; - if (name == "url" && value != "") - this.loadImage(value); +ImageVideo.prototype.stop = function() +{ + if(!this._video) + return; + this._video.pause(); + this._video.currentTime = 0; +} - return true; - }, +ImageVideo.prototype.pause = function() +{ + if(!this._video) + return; + this.trace("Video paused"); + this._video.pause(); +} - loadImage: function(url) - { - if(url == "") - { - this.img = null; - return; - } +ImageVideo.prototype.onWidget = function(e,widget) +{ + /* + if(widget.name == "demo") + { + this.loadVideo(); + } + else if(widget.name == "play") + { + if(this._video) + this.playPause(); + } + if(widget.name == "stop") + { + this.stop(); + } + else if(widget.name == "mute") + { + if(this._video) + this._video.muted = !this._video.muted; + } + */ +} - this.trace("loading image..."); - this.img = document.createElement("img"); - this.img.src = "miniproxy.php?url=" + url; - this.boxcolor = "#F95"; - var that = this; - this.img.onload = function() - { - that.trace("Image loaded, size: " + that.img.width + "x" + that.img.height ); - this.dirty = true; - that.boxcolor = "#9F9"; - that.setDirtyCanvas(true); - } - }, - - onWidget: function(e,widget) - { - if(widget.name == "load") - { - this.loadImage(this.properties["url"]); - } - } - }); - -LiteGraph.registerNodeType("graphics/cropImage", { - title: "Crop", - desc: "Crop Image", - - inputs: [["","image"]], - outputs: [["","image"]], - properties: {width:256,height:256,x:0,y:0,scale:1.0 }, - size: [50,20], - - onLoad: function() - { - this.createCanvas(); - }, - - createCanvas: function() - { - this.canvas = document.createElement("canvas"); - this.canvas.width = this.properties["width"]; - this.canvas.height = this.properties["height"]; - }, - - onExecute: function() - { - var input = this.getInputData(0); - if(!input) return; - - if(input.width) - { - var ctx = this.canvas.getContext("2d"); - - ctx.drawImage(input, -this.properties["x"],-this.properties["y"], input.width * this.properties["scale"], input.height * this.properties["scale"]); - this.setOutputData(0,this.canvas); - } - else - this.setOutputData(0,null); - }, - - onPropertyChange: function(name,value) - { - this.properties[name] = value; - - if(name == "scale") - { - this.properties[name] = parseFloat(value); - if(this.properties[name] == 0) - { - this.trace("Error in scale"); - this.properties[name] = 1.0; - } - } - else - this.properties[name] = parseInt(value); - - this.createCanvas(); - - return true; - } - }); +LiteGraph.registerNodeType("graphics/video", ImageVideo ); -LiteGraph.registerNodeType("graphics/video", { - title: "Video", - desc: "Video playback", +// Texture Webcam ***************************************** +function ImageWebcam() +{ + this.addOutput("Webcam","image"); + this.properties = {}; +} - inputs: [["t","number"]], - outputs: [["frame","image"],["t","number"],["d","number"]], - properties: {"url":""}, - widgets: [{name:"play",text:"PLAY",type:"minibutton"},{name:"stop",text:"STOP",type:"minibutton"},{name:"demo",text:"Demo video",type:"button"},{name:"mute",text:"Mute video",type:"button"}], +ImageWebcam.title = "Webcam"; +ImageWebcam.desc = "Webcam image"; - onClick: function(e) - { - if(!this.video) return; - //press play - if( distance( [e.canvasX,e.canvasY], [ this.pos[0] + 55, this.pos[1] + 40] ) < 20 ) - { - this.play(); - return true; - } - }, +ImageWebcam.prototype.openStream = function() +{ + //Vendor prefixes hell + navigator.getUserMedia = (navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia); + window.URL = window.URL || window.webkitURL; - onKeyDown: function(e) - { - if(e.keyCode == 32) - this.playPause(); - }, + if (!navigator.getUserMedia) { + //console.log('getUserMedia() is not supported in your browser, use chrome and enable WebRTC from about://flags'); + return; + } - onLoad: function() - { - if(this.properties.url != "") - this.loadVideo(this.properties.url); - }, + this._waiting_confirmation = true; - play: function() - { - if(this.video) - { - this.trace("Video playing"); - this.video.play(); - } - }, + // Not showing vendor prefixes. + navigator.getUserMedia({video: true}, this.streamReady.bind(this), onFailSoHard); - playPause: function() - { - if(this.video) - { - if(this.video.paused) - this.play(); - else - this.pause(); - } - }, + var that = this; + function onFailSoHard(e) { + trace('Webcam rejected', e); + that._webcam_stream = false; + that.box_color = "red"; + }; +} - stop: function() - { - if(this.video) - { - this.trace("Video stopped"); - this.video.pause(); - this.video.currentTime = 0; - } - }, +ImageWebcam.prototype.streamReady = function(localMediaStream) +{ + this._webcam_stream = localMediaStream; + //this._waiting_confirmation = false; - pause: function() - { - if(this.video) - { - this.trace("Video paused"); - this.video.pause(); - } - }, + var video = this._video; + if(!video) + { + video = document.createElement("video"); + video.autoplay = true; + video.src = window.URL.createObjectURL(localMediaStream); + this._video = video; + //document.body.appendChild( video ); //debug + //when video info is loaded (size and so) + video.onloadedmetadata = function(e) { + // Ready to go. Do some stuff. + console.log(e); + }; + } - onExecute: function() - { - if(!this.video) - return; - var t = this.getInputData(0); - if(t && t >= 0 && t <= 1.0) - { - this.video.currentTime = t * this.video.duration; - this.video.pause(); - } +}, - this.video.dirty = true; - this.setOutputData(0,this.video); - this.setOutputData(1,this.video.currentTime); - this.setOutputData(2,this.video.duration); - this.setDirtyCanvas(true); - }, +ImageWebcam.prototype.onExecute = function() +{ + if(this._webcam_stream == null && !this._waiting_confirmation) + this.openStream(); - onStart: function() - { - //this.play(); - }, + if(!this._video || !this._video.videoWidth) return; - onStop: function() - { - this.pause(); - }, + this._video.width = this._video.videoWidth; + this._video.hieght = this._video.videoHeight; + this.setOutputData(0, this._video); +} - loadVideo: function(url) - { - this.video = document.createElement("video"); - if(url) - this.video.src = url; - else - { - this.video.src = "modules/data/video.webm"; - this.properties.url = this.video.src; - } - this.video.type = "type=video/mp4"; - //this.video.loop = true; //not work in FF - this.video.muted = true; - this.video.autoplay = false; +LiteGraph.registerNodeType("graphics/webcam", ImageWebcam ); - //if(reModular.status == "running") this.play(); - - var that = this; - this.video.addEventListener("loadedmetadata",function(e) { - //onload - that.trace("Duration: " + that.video.duration + " seconds"); - that.trace("Size: " + that.video.videoWidth + "," + that.video.videoHeight); - that.setDirtyCanvas(true); - this.width = this.videoWidth; - this.height = this.videoHeight; - }); - this.video.addEventListener("progress",function(e) { - //onload - //that.trace("loading..."); - }); - this.video.addEventListener("error",function(e) { - that.trace("Error loading video: " + this.src); - if (this.error) { - switch (this.error.code) { - case this.error.MEDIA_ERR_ABORTED: - that.trace("You stopped the video."); - break; - case this.error.MEDIA_ERR_NETWORK: - that.trace("Network error - please try again later."); - break; - case this.error.MEDIA_ERR_DECODE: - that.trace("Video is broken.."); - break; - case this.error.MEDIA_ERR_SRC_NOT_SUPPORTED: - that.trace("Sorry, your browser can't play this video."); - break; - } - } - }); - - this.video.addEventListener("ended",function(e) { - that.trace("Ended."); - this.play(); - }); - - //$("body").append(this.video); - }, - - onPropertyChange: function(name,value) - { - this.properties[name] = value; - if (name == "url" && value != "") - this.loadVideo(value); - - return true; - }, - onWidget: function(e,widget) - { - if(widget.name == "demo") - { - this.loadVideo(); - } - else if(widget.name == "play") - { - if(this.video) - this.playPause(); - } - if(widget.name == "stop") - { - this.stop(); - } - else if(widget.name == "mute") - { - if(this.video) - this.video.muted = !this.video.muted; - } - - } - }); +})(); diff --git a/build/litegraph.min.js b/build/litegraph.min.js index f3387ecb4..eb3b7c41d 100644 --- a/build/litegraph.min.js +++ b/build/litegraph.min.js @@ -1,32 +1,35 @@ -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)}}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")}; +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,throw_errors:!0,registered_node_types:{},registerNodeType:function(a,b){if(!b.prototype)throw"Cannot register a simple object, it must be a class with a prototype"; +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;b=b||d.title||a;d=new d(name);d.type=a;d.title||(d.title=b);d.flags||(d.flags={});d.size|| +(d.size=d.computeSize());d.pos||(d.pos=LiteGraph.DEFAULT_POSITION.concat());if(c)for(var e in c)d[e]=c[e];return d},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)}}LiteGraph.debug&&console.log("Nodes reloaded")},cloneObject:function(a,b){if(null==a)return null;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.supported_types=["number","string","boolean"];LGraph.prototype.getSupportedTypes=function(){return this.supported_types||LGraph.supported_types}; +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.global_inputs={};this.global_outputs={};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=window.performance.now();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.onAdded)a.onAdded();this.config.align_to_grid&&a.alignToGrid();this.updateExecutionOrder();if(this.onNodeAdded)this.onNodeAdded(a);this.setDirtyCanvas(!0);this.change();return a}}; +LGraph.prototype.remove=function(a){if(null!=this._nodes_by_id[a.id]&&!a.ignore_remove){if(a.inputs)for(var b=0;ba&&this.pos[1]-cb)return!0;return!1}; @@ -42,7 +45,7 @@ a?[this.pos[0],this.pos[1]+10+b*LiteGraph.NODE_SLOT_HEIGHT]:[this.pos[0]+this.si 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.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){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.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.title+": 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)))}; @@ -52,18 +55,18 @@ this.processMouseWheel.bind(this),!1);this.canvas.addEventListener("touchstart", LGraphCanvas.prototype.setDirty=function(a,b){a&&(this.dirty_canvas=!0);b&&(this.dirty_bgcanvas=!0)};LGraphCanvas.prototype.getCanvasWindow=function(){var a=this.canvas.ownerDocument;return a.defaultView||a.parentWindow};LGraphCanvas.prototype.startRendering=function(){function a(){this.pause_rendering||this.draw();var b=this.getCanvasWindow();this.is_rendering&&b.requestAnimationFrame(a.bind(this))}this.is_rendering||(this.is_rendering=!0,a.call(this))}; LGraphCanvas.prototype.stopRendering=function(){this.is_rendering=!1}; LGraphCanvas.prototype.processMouseDown=function(a){if(this.graph){this.adjustMouseEvent(a);var b=this.getCanvasWindow();this.canvas.removeEventListener("mousemove",this._mousemove_callback);b.document.addEventListener("mousemove",this._mousemove_callback);b.document.addEventListener("mouseup",this._mouseup_callback);var c=this.graph.getNodeOnPos(a.canvasX,a.canvasY,this.visible_nodes);if(1==a.which){if(!a.shiftKey){var d=[],e;for(e in this.selected_nodes)this.selected_nodes[e]!=c&&d.push(this.selected_nodes[e]); -for(e in d)this.processNodeDeselected(d[e])}d=!1;if(c){this.live_mode||c.flags.pinned||this.bringToFront(c);var f=!1;if(!this.connecting_node&&!c.flags.collapsed&&!this.live_mode){if(c.outputs){e=0;for(var g=c.outputs.length;ewindow.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)? +for(e in d)this.processNodeDeselected(d[e])}d=!1;if(c){this.live_mode||c.flags.pinned||this.bringToFront(c);var f=!1;if(!this.connecting_node&&!c.flags.collapsed&&!this.live_mode){if(c.outputs){e=0;for(var g=c.outputs.length;ewindow.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}}; @@ -82,33 +85,33 @@ this.frame,5,39),a.fillText("FPS:"+this.fps.toFixed(2),5,52)):a.fillText("No gra 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.scale,this.scale);b.translate(this.offset[0],this.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.renderLink=function(a,b,c,d){if(this.highquality_render){var e=distance(b,c);this.render_connections_border&&0.6b[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.onMenuAdd=function(a,b,c,d,e){function f(a,b){var c=LiteGraph.createNode(a.value);c&&(c.pos=d.convertEventToCanvas(e),d.graph.add(c))}var g=d.getCanvasWindow();a=LiteGraph.getNodeTypesCategories();var h={},l;for(l in a)a[l]&&(h[l]={value:a[l],content:a[l],is_menu:!0});var k=LiteGraph.createContextualMenu(h,{event:b,callback:function(a,b){var c=LiteGraph.getNodeTypesInCategory(a.value),d=[],e;for(e in c)d.push({content:c[e].title,value:c[e].type});LiteGraph.createContextualMenu(d,{event:b, +callback:f,from:k},g);return!1},from:c},g);return!1};LGraphCanvas.onMenuCollapseAll=function(){};LGraphCanvas.onMenuNodeEdit=function(){};LGraphCanvas.onMenuNodeInputs=function(a,b,c){function d(b){a&&a.addInput(b.value[0],b.value[1],b.value[2])}if(a){var e=a.optional_inputs;a.onGetInputs&&(e=a.onGetInputs());if(e){var f=[],g;for(g in e){var h=e[g],l=h[0];h[2]&&h[2].label&&(l=h[2].label);f.push({content:l,value:h})}LiteGraph.createContextualMenu(f,{event:b,callback:d,from:c})}return!1}}; +LGraphCanvas.onMenuNodeOutputs=function(a,b,c){function d(f){if(a){var e=f.value[1];if(!e||e.constructor!==Object&&e.constructor!==Array)a.addOutput(f.value[0],f.value[1]);else{f=[];for(var g in e)f.push({content:g,value:e[g]});LiteGraph.createContextualMenu(f,{event:b,callback:d,from:c});return!1}}}if(a){var e=a.optional_outputs;a.onGetOutputs&&(e=a.onGetOutputs());if(e){var f=[],g;for(g in e)-1==a.findOutputSlot(e[g][0])&&f.push({content:e[g][0],value:e[g]});f.length&&LiteGraph.createContextualMenu(f, +{event:b,callback:d,from:c})}return!1}};LGraphCanvas.onMenuNodeCollapse=function(a){a.flags.collapsed=!a.flags.collapsed;a.setDirtyCanvas(!0,!0)};LGraphCanvas.onMenuNodePin=function(a){a.pin()}; +LGraphCanvas.onMenuNodeColors=function(a,b,c){var d=[],e;for(e in LGraphCanvas.node_colors){var f=LGraphCanvas.node_colors[e];d.push({value:e,content:""+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.setDirtyCanvas(!0))},from:c});return!1}; +LGraphCanvas.onMenuNodeShapes=function(a,b){LiteGraph.createContextualMenu(["box","round"],{event:b,callback:function(b){a&&(a.shape=b,a.setDirtyCanvas(!0))}});return!1};LGraphCanvas.onMenuNodeRemove=function(a){!1!=a.removable&&(a.graph.remove(a),a.setDirtyCanvas(!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.setDirtyCanvas(!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:"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)}; +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(f,d){if(f&&f.callback)return f.callback(a,d,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} @@ -118,75 +121,57 @@ f.backgroundColor="#444";e.addEventListener("contextmenu",function(a){a.preventD d));e.appendChild(h)}e.addEventListener("mouseover",function(a){this.mouse_inside=!0});e.addEventListener("mouseout",function(a){for(a=a.toElement;a!=this&&a!=c.document;)a=a.parentNode;a!=this&&(this.mouse_inside=!1,this.block_close||this.closeMenu())});c.document.body.appendChild(e);a=e.getClientRects()[0];b.from&&(b.from.block_close=!0);h=b.left||0;g=b.top||0;b.event&&(h=b.event.pageX-10,g=b.event.pageY-10,b.left&&(h=b.left),f=c.document.body.getClientRects()[0],b.from&&(h=b.from.getClientRects()[0], h=h.left+h.width),h>f.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;cB":value=a>b;break;case "A=B":value=a>=b}this.setOutputData(c,value)}}},onGetOutputs:function(){return[["A==B","number"],["A!=B","number"],["A>B","number"],["A=B","number"],["A<=B","number"]]}}); -window.math&&LiteGraph.registerNodeType("math/formula",{title:"Formula",desc:"Compute safe formula",inputs:[["x","number"],["y","number"]],outputs:[["","number"]],properties:{x:1,y:1,formula:"x+y"},onExecute:function(){var a=this.getInputData(0),b=this.getInputData(1);null!=a?this.properties.x=a:a=this.properties.x;null!=b?this.properties.y=b:b=this.properties.y;a=math.eval(this.properties.formula,{x:a,y:b,T:this.graph.globaltime});this.setOutputData(0,a)},onDrawBackground:function(){this.outputs[0].label= -this.properties.formula},onGetOutputs:function(){return[["A-B","number"],["A*B","number"],["A/B","number"]]}}); -LiteGraph.registerNodeType("math/trigonometry",{title:"Trigonometry",desc:"Sin Cos Tan",bgImageUrl:"nodes/imgs/icon-sin.png",inputs:[["v","number"]],outputs:[["sin","number"]],properties:{amplitude:1},size:[100,20],onExecute:function(){for(var a=this.getInputData(0),b=this.properties.amplitude,c=0,d=this.outputs.length;cXYZ",desc:"vector 3 to components",inputs:[["vec3","vec3"]],outputs:[["x","number"],["y","number"],["z","number"]],onExecute:function(){var a=this.getInputData(0);null!=a&&(this.setOutputData(0,a[0]),this.setOutputData(1,a[1]),this.setOutputData(2,a[2]))}}),LiteGraph.registerNodeType("math3d/xyz-to-vec3",{title:"XYZ->Vec3",desc:"components to vector3",inputs:[["x","number"],["y","number"],["z","number"]],outputs:[["vec3", -"vec3"]],onExecute:function(){var a=this.getInputData(0);null==a&&(a=0);var b=this.getInputData(1);null==b&&(b=0);var c=this.getInputData(2);null==c&&(c=0);this.setOutputData(0,vec3.fromValues(a,b,c))}}),LiteGraph.registerNodeType("math3d/rotation",{title:"Rotation",desc:"rotation quaternion",inputs:[["degrees","number"],["axis","vec3"]],outputs:[["quat","quat"]],properties:{angle:90,axis:[0,1,0]},onExecute:function(){var a=this.getInputData(0);null==a&&(a=this.properties.angle);var b=this.getInputData(1); -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(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, -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))},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|| -this.createGradient(a);a.lineWidth=1;a.strokeStyle=this.properties.borderColor;a.fillStyle=this.lineargradient;a.shadowColor="#000";a.shadowOffsetX=0;a.shadowOffsetY=0;a.shadowBlur=this.properties.shadowSize;a.roundRect(0,0,this.size[0],this.size[1],this.properties.shadowSize);a.fill();a.shadowColor="rgba(0,0,0,0)";a.stroke();a.fillStyle="#A00";a.fillRect(this.size[0]*this.properties.x-5,this.size[1]*this.properties.y-5,10,10)},onWidget:function(a,b){"update"==b.name&&(this.lineargradient=null,this.setDirtyCanvas(!0))}, -onExecute:function(){this.setOutputData(0,this.properties.x);this.setOutputData(1,this.properties.y)},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){this.oldmouse&&(a=[a.canvasX-this.pos[0],a.canvasY-this.pos[1]],this.properties.x=a[0]/this.size[0],this.properties.y=a[1]/this.size[1],1this.properties.x&&(this.properties.x=0),1this.properties.y&&(this.properties.y=0),this.oldmouse=a,this.setDirtyCanvas(!0))},onMouseUp:function(a){this.oldmouse&&(this.oldmouse=null,this.captureInput(!1))},onMouseLeave:function(a){}}); -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"},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)},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},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)}}}); -LiteGraph.registerNodeType("graphics/frame",{title:"Frame",desc:"Frame viewerew",inputs:[["","image"]],size:[200,200],widgets:[{name:"resize",text:"Resize box",type:"button"},{name:"view",text:"View Image",type:"button"}],onDrawBackground:function(a){this.frame&&a.drawImage(this.frame,0,0,this.size[0],this.size[1])},onExecute:function(){this.frame=this.getInputData(0);this.setDirtyCanvas(!0)},onWidget:function(a,b){if("resize"==b.name&&this.frame){var c=this.frame.width,d=this.frame.height;c||null== -this.frame.videoWidth||(c=this.frame.videoWidth,d=this.frame.videoHeight);c&&d&&(this.size=[c,d]);this.setDirtyCanvas(!0,!0)}else"view"==b.name&&this.show()},show:function(){showElement&&this.frame&&showElement(this.frame)}}); -LiteGraph.registerNodeType("visualization/graph",{desc:"Shows a graph of the inputs",inputs:[["",0],["",0],["",0],["",0]],size:[200,200],properties:{min:-1,max:1,bgColor:"#000"},onDrawBackground:function(a){var b=["#FFF","#FAA","#AFA","#AAF"];null!=this.properties.bgColor&&""!=this.properties.bgColor&&(a.fillStyle="#000",a.fillRect(2,2,this.size[0]-4,this.size[1]-4));if(this.data){var c=this.properties.min,d=this.properties.max,e;for(e in this.data){var f=this.data[e];if(f&&null!=this.getInputInfo(e)){a.strokeStyle= -b[e];a.beginPath();for(var g=f.length/this.size[0],h=0;hk&&(k=0);0==h?a.moveTo(h/g,this.size[1]-5-(this.size[1]-10)*k):a.lineTo(h/g,this.size[1]-5-(this.size[1]-10)*k)}a.stroke()}}}},onExecute:function(){this.data||(this.data=[]);for(var a in this.inputs){var b=this.getInputData(a);"number"==typeof b?(b=b?b:0,this.data[a]||(this.data[a]=[]),this.data[a].push(b),this.data[a].length>this.size[1]-4&&(this.data[a]=this.data[a].slice(1,this.data[a].length))): -this.data[a]=b}this.data.length&&this.setDirtyCanvas(!0)}}); -LiteGraph.registerNodeType("graphics/supergraph",{title:"Supergraph",desc:"Shows a nice circular graph",inputs:[["x","number"],["y","number"],["c","color"]],outputs:[["","image"]],widgets:[{name:"clear_alpha",text:"Clear Alpha",type:"minibutton"},{name:"clear_color",text:"Clear color",type:"minibutton"}],properties:{size:256,bgcolor:"#000",lineWidth:1},bgcolor:"#000",flags:{allow_fastrender:!0},onLoad:function(){this.createCanvas()},createCanvas:function(){this.canvas=document.createElement("canvas"); -this.canvas.width=this.properties.size;this.canvas.height=this.properties.size;this.oldpos=null;this.clearCanvas(!0)},onExecute:function(){var a=this.getInputData(0),b=this.getInputData(1),c=this.getInputData(2);if(null!=a||null!=b){a||(a=0);b||(b=0);var a=0.95*a,b=0.95*b,d=this.properties.size;d==this.canvas.width&&d==this.canvas.height||this.createCanvas();if(this.oldpos){var e=this.canvas.getContext("2d");null==c?c="rgba(255,255,255,0.5)":"object"==typeof c&&(c=colorToString(c));e.strokeStyle= -c;e.beginPath();e.moveTo(this.oldpos[0],this.oldpos[1]);this.oldpos=[(0.5*a+0.5)*d,(0.5*b+0.5)*d];e.lineTo(this.oldpos[0],this.oldpos[1]);e.stroke();this.canvas.dirty=!0;this.setOutputData(0,this.canvas)}else this.oldpos=[(0.5*a+0.5)*d,(0.5*b+0.5)*d]}},clearCanvas:function(a){var b=this.canvas.getContext("2d");a?(b.clearRect(0,0,this.canvas.width,this.canvas.height),this.trace("Clearing alpha")):(b.fillStyle=this.properties.bgcolor,b.fillRect(0,0,this.canvas.width,this.canvas.height))},onWidget:function(a, -b){"clear_color"==b.name?this.clearCanvas(!1):"clear_alpha"==b.name&&this.clearCanvas(!0)},onPropertyChange:function(a,b){if("size"==a)this.properties.size=parseInt(b),this.createCanvas();else if("bgcolor"==a)this.properties.bgcolor=b,this.createCanvas();else if("lineWidth"==a)this.properties.lineWidth=parseInt(b),this.canvas.getContext("2d").lineWidth=this.properties.lineWidth;else return!1;return!0}}); -LiteGraph.registerNodeType("graphics/imagefade",{title:"Image fade",desc:"Fades between images",inputs:[["img1","image"],["img2","image"],["fade","number"]],outputs:[["","image"]],properties:{fade:0.5,width:512,height:512},widgets:[{name:"resizeA",text:"Resize to A",type:"button"},{name:"resizeB",text:"Resize to B",type:"button"}],onLoad:function(){this.createCanvas();var a=this.canvas.getContext("2d");a.fillStyle="#000";a.fillRect(0,0,this.properties.width,this.properties.height)},createCanvas:function(){this.canvas= -document.createElement("canvas");this.canvas.width=this.properties.width;this.canvas.height=this.properties.height},onExecute:function(){var a=this.canvas.getContext("2d");this.canvas.width=this.canvas.width;var b=this.getInputData(0);null!=b&&a.drawImage(b,0,0,this.canvas.width,this.canvas.height);b=this.getInputData(2);null==b?b=this.properties.fade:this.properties.fade=b;a.globalAlpha=b;b=this.getInputData(1);null!=b&&a.drawImage(b,0,0,this.canvas.width,this.canvas.height);a.globalAlpha=1;this.setOutputData(0, -this.canvas);this.setDirtyCanvas(!0)}}); -LiteGraph.registerNodeType("graphics/image",{title:"Image",desc:"Image loader",inputs:[],outputs:[["frame","image"]],properties:{url:""},widgets:[{name:"load",text:"Load",type:"button"}],onLoad:function(){""!=this.properties.url&&null==this.img&&this.loadImage(this.properties.url)},onStart:function(){},onExecute:function(){this.img||(this.boxcolor="#000");this.img&&this.img.width?this.setOutputData(0,this.img):this.setOutputData(0,null);this.img.dirty&&(this.img.dirty=!1)},onPropertyChange:function(a, -b){this.properties[a]=b;"url"==a&&""!=b&&this.loadImage(b);return!0},loadImage:function(a){if(""==a)this.img=null;else{this.trace("loading image...");this.img=document.createElement("img");this.img.src="miniproxy.php?url="+a;this.boxcolor="#F95";var b=this;this.img.onload=function(){b.trace("Image loaded, size: "+b.img.width+"x"+b.img.height);this.dirty=!0;b.boxcolor="#9F9";b.setDirtyCanvas(!0)}}},onWidget:function(a,b){"load"==b.name&&this.loadImage(this.properties.url)}}); -LiteGraph.registerNodeType("graphics/cropImage",{title:"Crop",desc:"Crop Image",inputs:[["","image"]],outputs:[["","image"]],properties:{width:256,height:256,x:0,y:0,scale:1},size:[50,20],onLoad:function(){this.createCanvas()},createCanvas:function(){this.canvas=document.createElement("canvas");this.canvas.width=this.properties.width;this.canvas.height=this.properties.height},onExecute:function(){var a=this.getInputData(0);a&&(a.width?(this.canvas.getContext("2d").drawImage(a,-this.properties.x,-this.properties.y, -a.width*this.properties.scale,a.height*this.properties.scale),this.setOutputData(0,this.canvas)):this.setOutputData(0,null))},onPropertyChange:function(a,b){this.properties[a]=b;"scale"==a?(this.properties[a]=parseFloat(b),0==this.properties[a]&&(this.trace("Error in scale"),this.properties[a]=1)):this.properties[a]=parseInt(b);this.createCanvas();return!0}}); -LiteGraph.registerNodeType("graphics/video",{title:"Video",desc:"Video playback",inputs:[["t","number"]],outputs:[["frame","image"],["t","number"],["d","number"]],properties:{url:""},widgets:[{name:"play",text:"PLAY",type:"minibutton"},{name:"stop",text:"STOP",type:"minibutton"},{name:"demo",text:"Demo video",type:"button"},{name:"mute",text:"Mute video",type:"button"}],onClick:function(a){if(this.video&&20>distance([a.canvasX,a.canvasY],[this.pos[0]+55,this.pos[1]+40]))return this.play(),!0},onKeyDown:function(a){32== -a.keyCode&&this.playPause()},onLoad:function(){""!=this.properties.url&&this.loadVideo(this.properties.url)},play:function(){this.video&&(this.trace("Video playing"),this.video.play())},playPause:function(){this.video&&(this.video.paused?this.play():this.pause())},stop:function(){this.video&&(this.trace("Video stopped"),this.video.pause(),this.video.currentTime=0)},pause:function(){this.video&&(this.trace("Video paused"),this.video.pause())},onExecute:function(){if(this.video){var a=this.getInputData(0); -a&&0<=a&&1>=a&&(this.video.currentTime=a*this.video.duration,this.video.pause());this.video.dirty=!0;this.setOutputData(0,this.video);this.setOutputData(1,this.video.currentTime);this.setOutputData(2,this.video.duration);this.setDirtyCanvas(!0)}},onStart:function(){},onStop:function(){this.pause()},loadVideo:function(a){this.video=document.createElement("video");a?this.video.src=a:(this.video.src="modules/data/video.webm",this.properties.url=this.video.src);this.video.type="type=video/mp4";this.video.muted= -!0;this.video.autoplay=!1;var b=this;this.video.addEventListener("loadedmetadata",function(a){b.trace("Duration: "+b.video.duration+" seconds");b.trace("Size: "+b.video.videoWidth+","+b.video.videoHeight);b.setDirtyCanvas(!0);this.width=this.videoWidth;this.height=this.videoHeight});this.video.addEventListener("progress",function(a){});this.video.addEventListener("error",function(a){b.trace("Error loading video: "+this.src);if(this.error)switch(this.error.code){case this.error.MEDIA_ERR_ABORTED:b.trace("You stopped the video."); -break;case this.error.MEDIA_ERR_NETWORK:b.trace("Network error - please try again later.");break;case this.error.MEDIA_ERR_DECODE:b.trace("Video is broken..");break;case this.error.MEDIA_ERR_SRC_NOT_SUPPORTED:b.trace("Sorry, your browser can't play this video.")}});this.video.addEventListener("ended",function(a){b.trace("Ended.");this.play()})},onPropertyChange:function(a,b){this.properties[a]=b;"url"==a&&""!=b&&this.loadVideo(b);return!0},onWidget:function(a,b){"demo"==b.name?this.loadVideo():"play"== -b.name&&this.video&&this.playPause();"stop"==b.name?this.stop():"mute"==b.name&&this.video&&(this.video.muted=!this.video.muted)}}); +(function(){function a(){this.addOutput("value",0)}function b(){this.title="Output";this.properties={name:"input_"+(1E3*Math.random()).toFixed(),type:"number"};this.addInput("value","number")}function c(){this.subgraph=new LGraph;this.bgcolor="#FA3"}function d(){this.addOutput("value","number");this.properties={value:1};this.editable={property:"value",type:"number"}}function e(){this.size=[60,20];this.addInput("value",0,{label:""});this.addOutput("value",0,{label:""});this.properties={value:""}}a.prototype.onExecute= +function(){this.setOutputData(0,node.graph.global_inputs[this.title])};LiteGraph.registerNodeType("graph/input",a);b.prototype.onAdded=function(){this.graph.addGlobalOutput(this.properties.name,this.properties.type)};b.prototype.onExecute=function(){var a=this.getInputData(0);this.graph.setGlobalOutputData(this.properties.name,a)};LiteGraph.registerNodeType("graph/output",b);c.prototype.onExecute=function(){for(var a in this.inputs);};c.prototype.configure=function(a){LGraph.prototype.configure.call(this, +a)};LiteGraph.registerNodeType("graph/subgraph",c);d.title="Const";d.desc="Constant value";d.prototype.setValue=function(a){"string"==typeof a&&(a=parseFloat(a));this.properties.value=a;this.setDirtyCanvas(!0)};d.prototype.onExecute=function(){this.setOutputData(0,parseFloat(this.properties.value))};d.prototype.onDrawBackground=function(a){this.outputs[0].label=this.properties.value.toFixed(3)};d.prototype.onWidget=function(a,b){"value"==b.name&&this.setValue(b.value)};LiteGraph.registerNodeType("basic/const", +d);e.title="Watch";e.desc="Show value of input";e.prototype.onExecute=function(){this.properties.value=this.getInputData(0);this.setOutputData(0,this.properties.value)};e.prototype.onDrawBackground=function(a){this.inputs[0]&&null!=this.properties.value&&(this.inputs[0].label=this.properties.value.constructor===Number?this.properties.value.toFixed(3):this.properties.value)};LiteGraph.registerNodeType("basic/watch",e)})(); +(function(){function a(){this.size=[64,84];this.addOutput("","number");this.properties={min:0,max:1,value:0.5,wcolor:"#7AF",size:50}}function b(){this.size=[160,26];this.addOutput("","number");this.properties={wcolor:"#7AF",min:0,max:1,value:0.5}}function c(){this.size=[160,26];this.addInput("","number");this.properties={min:0,max:1,value:0,wcolor:"#AAF"}}function d(){this.addInputs("",0);this.properties={value:"...",font:"Arial",fontsize:18,color:"#AAA",align:"left",glowSize:0,decimals:1}}function e(){this.size= +[200,100];this.properties={borderColor:"#ffffff",bgcolorTop:"#f0f0f0",bgcolorBottom:"#e0e0e0",shadowSize:2,borderRadius:3}}a.title="Knob";a.desc="Circular controller";a.widgets=[{name:"increase",text:"+",type:"minibutton"},{name:"decrease",text:"-",type:"minibutton"}];a.prototype.onAdded=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")};a.prototype.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(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"}};a.prototype.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,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}};a.prototype.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)}};a.prototype.onMouseUp=function(a){this.oldmouse&&(this.oldmouse=null,this.captureInput(!1))};a.prototype.onMouseLeave=function(a){};a.prototype.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)};a.prototype.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/knob",a);b.title="H.Slider";b.desc="Linear slider controller";b.prototype.onInit=function(){this.value=0.5;this.imgfg=this.loadImage("imgs/slider_fg.png")};b.prototype.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())};b.prototype.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))};b.prototype.onDrawForeground=function(a){this.onDrawImage(a)};b.prototype.onExecute=function(){this.properties.value=this.properties.min+(this.properties.max-this.properties.min)*this.value;this.setOutputData(0,this.properties.value);this.boxcolor=colorToString([this.value,this.value,this.value])};b.prototype.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};b.prototype.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)}};b.prototype.onMouseUp=function(a){this.oldmouse=null;this.captureInput(!1)};b.prototype.onMouseLeave=function(a){};b.prototype.onPropertyChange=function(a,b){if("wcolor"==a)this.properties[a]=b;else return!1;return!0};LiteGraph.registerNodeType("widget/hslider", +b);c.title="Progress";c.desc="Shows data in linear progress";c.prototype.onExecute=function(){var a=this.getInputData(0);void 0!=a&&(this.properties.value=a)};c.prototype.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/progress",c);d.title="Text";d.desc="Shows the input value"; +d.widgets=[{name:"resize",text:"Resize box",type:"button"},{name:"led_text",text:"LED",type:"minibutton"},{name:"normal_text",text:"Normal",type:"minibutton"}];d.prototype.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"};d.prototype.onExecute=function(){var a=this.getInputData(0);this.properties.value=null!=a?a:"";this.setDirtyCanvas(!0)};d.prototype.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":value=a>b;break;case "A=B":value=a>=b}this.setOutputData(c,value)}}};h.prototype.onGetOutputs=function(){return[["A==B","number"],["A!=B","number"],["A>B","number"],["A=B","number"],["A<=B","number"]]};LiteGraph.registerNodeType("math/compare",h);l.title="Trigonometry";l.desc="Sin Cos Tan";l.prototype.onExecute=function(){for(var a=this.getInputData(0),b=this.properties.amplitude,c=0,d=this.outputs.length;c< +d;++c){switch(this.outputs[c].name){case "sin":value=Math.sin(a);break;case "cos":value=Math.cos(a);break;case "tan":value=Math.tan(a);break;case "asin":value=Math.asin(a);break;case "acos":value=Math.acos(a);break;case "atan":value=Math.atan(a)}this.setOutputData(c,b*value)}};l.prototype.onGetOutputs=function(){return[["sin","number"],["cos","number"],["tan","number"],["asin","number"],["acos","number"],["atan","number"]]};LiteGraph.registerNodeType("math/trigonometry",l);if(window.math){var k=function(){this.addInputs("x", +"number");this.addInputs("y","number");this.addOutputs("","number");this.properties={x:1,y:1,formula:"x+y"}};k.title="Formula";k.desc="Compute safe formula";k.prototype.onExecute=function(){var a=this.getInputData(0),b=this.getInputData(1);null!=a?this.properties.x=a:a=this.properties.x;null!=b?this.properties.y=b:b=this.properties.y;a=math.eval(this.properties.formula,{x:a,y:b,T:this.graph.globaltime});this.setOutputData(0,a)};k.prototype.onDrawBackground=function(){this.outputs[0].label=this.properties.formula}; +k.prototype.onGetOutputs=function(){return[["A-B","number"],["A*B","number"],["A/B","number"]]};LiteGraph.registerNodeType("math/formula",k)}window.glMatrix&&(k=function(){this.addInput("vec3","vec3");this.addOutput("x","number");this.addOutput("y","number");this.addOutput("z","number")},k.title="Vec3->XYZ",k.desc="vector 3 to components",k.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&(this.setOutputData(0,a[0]),this.setOutputData(1,a[1]),this.setOutputData(2,a[2]))},LiteGraph.registerNodeType("math3d/vec3-to-xyz", +k),k=function(){this.addInputs([["x","number"],["y","number"],["z","number"]]);this.addOutput("vec3","vec3")},k.title="XYZ->Vec3",k.desc="components to vector3",k.prototype.onExecute=function(){var a=this.getInputData(0);null==a&&(a=0);var b=this.getInputData(1);null==b&&(b=0);var c=this.getInputData(2);null==c&&(c=0);this.setOutputData(0,vec3.fromValues(a,b,c))},LiteGraph.registerNodeType("math3d/xyz-to-vec3",k),k=function(){this.addInputs([["degrees","number"],["axis","vec3"]]);this.addOutput("quat", +"quat");this.properties={angle:90,axis:vec3.fromValues(0,1,0)}},k.title="Rotation",k.desc="quaternion rotation",k.prototype.onExecute=function(){var a=this.getInputData(0);null==a&&(a=this.properties.angle);var b=this.getInputData(1);null==b&&(b=this.properties.axis);a=quat.setAxisAngle(quat.create(),b,0.0174532925*a);this.setOutputData(0,a)},LiteGraph.registerNodeType("math3d/rotation",k),k=function(){this.addInputs([["vec3","vec3"],["quat","quat"]]);this.addOutput("result","vec3");this.properties= +{vec:[0,0,1]}},k.title="Rot. Vec3",k.desc="rotate a point",k.prototype.onExecute=function(){var a=this.getInputData(0);null==a&&(a=this.properties.vec);var b=this.getInputData(1);null==b?this.setOutputData(a):this.setOutputData(0,vec3.transformQuat(vec3.create(),a,b))},LiteGraph.registerNodeType("math3d/rotate_vec3",k),k=function(){this.addInputs([["A","quat"],["B","quat"]]);this.addOutput("A*B","quat")},k.title="Mult. Quat",k.desc="rotate quaternion",k.prototype.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("math3d/mult-quat",k))})(); +(function(){function a(){this.addInput("f","number");this.addOutput("Color","color");this.properties={colorA:"#444444",colorB:"#44AAFF",colorC:"#44FFAA",colorD:"#FFFFFF"}}function b(){this.addInput("","image");this.size=[200,200]}function c(){this.addInputs([["img1","image"],["img2","image"],["fade","number"]]);this.addInput("","image");this.properties={fade:0.5,width:512,height:512}}function d(){this.inputs=[];this.addOutput("frame","image");this.properties={url:""}}function e(){this.addInput("", +"image");this.addOutputs("","image");this.properties={width:256,height:256,x:0,y:0,scale:1};this.size=[50,20]}function f(){this.addInput("t","number");this.addOutputs([["frame","image"],["t","number"],["d","number"]]);this.properties={url:""}}function g(){this.addOutput("Webcam","image");this.properties={}}a.title="Palette";a.desc="Generates a color";a.prototype.onExecute=function(){var a=[];null!=this.properties.colorA&&a.push(hex2num(this.properties.colorA));null!=this.properties.colorB&&a.push(hex2num(this.properties.colorB)); +null!=this.properties.colorC&&a.push(hex2num(this.properties.colorC));null!=this.properties.colorD&&a.push(hex2num(this.properties.colorD));var b=this.getInputData(0);null==b&&(b=0.5);1b&&(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)}};LiteGraph.registerNodeType("color/palette",a);b.title="Frame";b.desc="Frame viewerew";b.widgets=[{name:"resize",text:"Resize box",type:"button"},{name:"view",text:"View Image",type:"button"}];b.prototype.onDrawBackground=function(a){this.frame&&a.drawImage(this.frame,0,0,this.size[0],this.size[1])};b.prototype.onExecute=function(){this.frame=this.getInputData(0);this.setDirtyCanvas(!0)};b.prototype.onWidget=function(a,b){if("resize"==b.name&&this.frame){var c=this.frame.width,d=this.frame.height; +c||null==this.frame.videoWidth||(c=this.frame.videoWidth,d=this.frame.videoHeight);c&&d&&(this.size=[c,d]);this.setDirtyCanvas(!0,!0)}else"view"==b.name&&this.show()};b.prototype.show=function(){showElement&&this.frame&&showElement(this.frame)};LiteGraph.registerNodeType("graphics/frame",b);c.title="Image fade";c.desc="Fades between images";c.widgets=[{name:"resizeA",text:"Resize to A",type:"button"},{name:"resizeB",text:"Resize to B",type:"button"}];c.prototype.onAdded=function(){this.createCanvas(); +var a=this.canvas.getContext("2d");a.fillStyle="#000";a.fillRect(0,0,this.properties.width,this.properties.height)};c.prototype.createCanvas=function(){this.canvas=document.createElement("canvas");this.canvas.width=this.properties.width;this.canvas.height=this.properties.height};c.prototype.onExecute=function(){var a=this.canvas.getContext("2d");this.canvas.width=this.canvas.width;var b=this.getInputData(0);null!=b&&a.drawImage(b,0,0,this.canvas.width,this.canvas.height);b=this.getInputData(2);null== +b?b=this.properties.fade:this.properties.fade=b;a.globalAlpha=b;b=this.getInputData(1);null!=b&&a.drawImage(b,0,0,this.canvas.width,this.canvas.height);a.globalAlpha=1;this.setOutputData(0,this.canvas);this.setDirtyCanvas(!0)};LiteGraph.registerNodeType("graphics/imagefade",c);d.title="Image";d.desc="Image loader";d.widgets=[{name:"load",text:"Load",type:"button"}];d.prototype.onAdded=function(){""!=this.properties.url&&null==this.img&&this.loadImage(this.properties.url)};d.prototype.onExecute=function(){this.img|| +(this.boxcolor="#000");this.img&&this.img.width?this.setOutputData(0,this.img):this.setOutputData(0,null);this.img.dirty&&(this.img.dirty=!1)};d.prototype.onPropertyChange=function(a,b){this.properties[a]=b;"url"==a&&""!=b&&this.loadImage(b);return!0};d.prototype.loadImage=function(a){if(""==a)this.img=null;else{this.trace("loading image...");this.img=document.createElement("img");this.img.src="miniproxy.php?url="+a;this.boxcolor="#F95";var b=this;this.img.onload=function(){b.trace("Image loaded, size: "+ +b.img.width+"x"+b.img.height);this.dirty=!0;b.boxcolor="#9F9";b.setDirtyCanvas(!0)}}};d.prototype.onWidget=function(a,b){"load"==b.name&&this.loadImage(this.properties.url)};LiteGraph.registerNodeType("graphics/image",d);e.title="Crop";e.desc="Crop Image";e.prototype.onAdded=function(){this.createCanvas()};e.prototype.createCanvas=function(){this.canvas=document.createElement("canvas");this.canvas.width=this.properties.width;this.canvas.height=this.properties.height};e.prototype.onExecute=function(){var a= +this.getInputData(0);a&&(a.width?(this.canvas.getContext("2d").drawImage(a,-this.properties.x,-this.properties.y,a.width*this.properties.scale,a.height*this.properties.scale),this.setOutputData(0,this.canvas)):this.setOutputData(0,null))};e.prototype.onPropertyChange=function(a,b){this.properties[a]=b;"scale"==a?(this.properties[a]=parseFloat(b),0==this.properties[a]&&(this.trace("Error in scale"),this.properties[a]=1)):this.properties[a]=parseInt(b);this.createCanvas();return!0};LiteGraph.registerNodeType("graphics/cropImage", +c);f.title="Video";f.desc="Video playback";f.widgets=[{name:"play",text:"PLAY",type:"minibutton"},{name:"stop",text:"STOP",type:"minibutton"},{name:"demo",text:"Demo video",type:"button"},{name:"mute",text:"Mute video",type:"button"}];f.prototype.onExecute=function(){if(this.properties.url&&(this.properties.url!=this._video_url&&this.loadVideo(this.properties.url),this._video&&0!=this._video.width)){var a=this.getInputData(0);a&&0<=a&&1>=a&&(this._video.currentTime=a*this._video.duration,this._video.pause()); +this._video.dirty=!0;this.setOutputData(0,this._video);this.setOutputData(1,this._video.currentTime);this.setOutputData(2,this._video.duration);this.setDirtyCanvas(!0)}};f.prototype.onStart=function(){this.play()};f.prototype.onStop=function(){this.stop()};f.prototype.loadVideo=function(a){this._video_url=a;this._video=document.createElement("video");this._video.src=a;this._video.type="type=video/mp4";this._video.muted=!0;this._video.autoplay=!0;var b=this;this._video.addEventListener("loadedmetadata", +function(a){b.trace("Duration: "+this.duration+" seconds");b.trace("Size: "+this.videoWidth+","+this.videoHeight);b.setDirtyCanvas(!0);this.width=this.videoWidth;this.height=this.videoHeight});this._video.addEventListener("progress",function(a){});this._video.addEventListener("error",function(a){console.log("Error loading video: "+this.src);b.trace("Error loading video: "+this.src);if(this.error)switch(this.error.code){case this.error.MEDIA_ERR_ABORTED:b.trace("You stopped the video.");break;case this.error.MEDIA_ERR_NETWORK:b.trace("Network error - please try again later."); +break;case this.error.MEDIA_ERR_DECODE:b.trace("Video is broken..");break;case this.error.MEDIA_ERR_SRC_NOT_SUPPORTED:b.trace("Sorry, your browser can't play this video.")}});this._video.addEventListener("ended",function(a){b.trace("Ended.");this.play()})};f.prototype.onPropertyChange=function(a,b){this.properties[a]=b;"url"==a&&""!=b&&this.loadVideo(b);return!0};f.prototype.play=function(){this._video&&this._video.play()};f.prototype.playPause=function(){this._video&&(this._video.paused?this.play(): +this.pause())};f.prototype.stop=function(){this._video&&(this._video.pause(),this._video.currentTime=0)};f.prototype.pause=function(){this._video&&(this.trace("Video paused"),this._video.pause())};f.prototype.onWidget=function(a,b){};LiteGraph.registerNodeType("graphics/video",f);g.title="Webcam";g.desc="Webcam image";g.prototype.openStream=function(){function a(c){trace("Webcam rejected",c);b._webcam_stream=!1;b.box_color="red"}navigator.getUserMedia=navigator.getUserMedia||navigator.webkitGetUserMedia|| +navigator.mozGetUserMedia||navigator.msGetUserMedia;window.URL=window.URL||window.webkitURL;if(navigator.getUserMedia){this._waiting_confirmation=!0;navigator.getUserMedia({video:!0},this.streamReady.bind(this),a);var b=this}};g.prototype.streamReady=function(a){this._webcam_stream=a;var b=this._video;b||(b=document.createElement("video"),b.autoplay=!0,b.src=window.URL.createObjectURL(a),this._video=b,b.onloadedmetadata=function(a){console.log(a)})};g.prototype.onExecute=function(){null!=this._webcam_stream|| +this._waiting_confirmation||this.openStream();this._video&&this._video.videoWidth&&(this._video.width=this._video.videoWidth,this._video.hieght=this._video.videoHeight,this.setOutputData(0,this._video))};LiteGraph.registerNodeType("graphics/webcam",g)})(); diff --git a/demo/demodata/video.webm b/demo/demodata/video.webm new file mode 100644 index 000000000..0c8d15d40 Binary files /dev/null and b/demo/demodata/video.webm differ diff --git a/demo/index.html b/demo/index.html index ef592e175..0a6b364fd 100644 --- a/demo/index.html +++ b/demo/index.html @@ -64,9 +64,10 @@ - - - + + + + diff --git a/src/litegraph.js b/src/litegraph.js index 43e71b165..aae3b65ce 100644 --- a/src/litegraph.js +++ b/src/litegraph.js @@ -28,6 +28,7 @@ var LiteGraph = { node_images_path: "", debug: false, + throw_errors: true, registered_node_types: {}, /** @@ -39,13 +40,10 @@ var LiteGraph = { registerNodeType: function(type, base_class) { - var title = type; - if(base_class.prototype && base_class.prototype.title) - title = base_class.prototype.title; - else if(base_class.title) - title = base_class.title; - + if(!base_class.prototype) + throw("Cannot register a simple object, it must be a class with a prototype"); base_class.type = type; + if(LiteGraph.debug) console.log("Node registered: " + type); @@ -55,13 +53,13 @@ var LiteGraph = { base_class.category = type.substr(0,pos); //info.name = name.substr(pos+1,name.length - pos); - //inheritance + //extend class if(base_class.prototype) //is a class for(var i in LGraphNode.prototype) if(!base_class.prototype[i]) base_class.prototype[i] = LGraphNode.prototype[i]; - this.registered_node_types[type] = base_class; + this.registered_node_types[ type ] = base_class; }, /** @@ -72,7 +70,7 @@ var LiteGraph = { * @param {Object} options to set options */ - createNode: function(type,name, options) + createNode: function(type, title, options) { var base_class = this.registered_node_types[type]; if (!base_class) @@ -84,9 +82,11 @@ var LiteGraph = { var prototype = base_class.prototype || base_class; - name = name || prototype.title || base_class.title || type; + title = title || base_class.title || type; - var node = null; + var node = new base_class( name ); + + /* if (base_class.prototype) //is a class { node = new base_class(name); @@ -123,9 +123,10 @@ var LiteGraph = { //set size if(base_class.size) node.size = base_class.size.concat(); //save size } + */ node.type = type; - if(!node.name) node.name = name; + if(!node.title) node.title = title; if(!node.flags) node.flags = {}; if(!node.size) node.size = node.computeSize(); if(!node.pos) node.pos = LiteGraph.DEFAULT_POSITION.concat(); @@ -238,6 +239,7 @@ var LiteGraph = { //separated just to improve if it doesnt work cloneObject: function(obj, target) { + if(obj == null) return null; var r = JSON.parse( JSON.stringify( obj ) ); if(!target) return r; @@ -245,52 +247,11 @@ var LiteGraph = { target[i] = r[i]; return target; } - - /* - benchmark: function(mode) - { - mode = mode || "all"; - - trace("Benchmarking " + mode + "..."); - 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) - links++; - trace(" Num. links: " + links ); - - var numTimes = 200; - if(mode == "core") - numTimes = 30000; - - var start = new Date().getTime(); - - for(var i = 0; i < numTimes; i++) - { - if(mode == "render") - this.draw(false); - else if(mode == "core") - this.sendEventToAllNodes("onExecute"); - else - { - this.sendEventToAllNodes("onExecute"); - this.draw(false); - } - } - - var elapsed = (new Date().getTime()) - start; - trace(" Time take for " + numTimes + " iterations: " + (elapsed*0.001).toFixed(3) + " seconds."); - var seconds_per_iteration = (elapsed*0.001)/numTimes; - trace(" Time per iteration: " + seconds_per_iteration.toFixed( seconds_per_iteration < 0.001 ? 6 : 3) + " seconds"); - trace(" Avg FPS: " + (1000/(elapsed/numTimes)).toFixed(3)); - } - */ }; - + //********************************************************************************* // LGraph CLASS @@ -311,6 +272,12 @@ function LGraph() this.clear(); } +//default supported types +LGraph.supported_types = ["number","string","boolean"]; + +//used to know which types of connections support this graph (some graphs do not allow certain types) +LGraph.prototype.getSupportedTypes = function() { return this.supported_types || LGraph.supported_types; } + LGraph.STATUS_STOPPED = 1; LGraph.STATUS_RUNNING = 2; @@ -348,7 +315,8 @@ LGraph.prototype.clear = function() this.starttime = 0; //globals - this.globals = {}; + this.global_inputs = {}; + this.global_outputs = {}; this.graph = {}; this.debug = true; @@ -671,16 +639,19 @@ LGraph.prototype.add = function(node) node.bgImage = node.loadImage(node.bgImageUrl); */ - if(node.onInit) - node.onInit(); + if(node.onAdded) + node.onAdded(); if(this.config.align_to_grid) node.alignToGrid(); - this.updateExecutionOrder(); + this.updateExecutionOrder(); - if(this.canvas) - this.canvas.dirty_canvas = true; + if(this.onNodeAdded) + this.onNodeAdded(node); + + + this.setDirtyCanvas(true); this.change(); @@ -722,16 +693,19 @@ LGraph.prototype.remove = function(node) node.id = -1; //callback - if(node.onDelete) - node.onDelete(); + if(node.onRemoved) + node.onRemoved(); - //remove from environment - if(this.canvas) + node.graph = null; + + //remove from canvas render + for(var i in this.list_of_graphcanvas) { - if(this.canvas.selected_nodes[node.id]) - delete this.canvas.selected_nodes[node.id]; - if(this.canvas.node_dragged == node) - this.canvas.node_dragged = null; + var canvas = this.list_of_graphcanvas[i]; + if(canvas.selected_nodes[node.id]) + delete canvas.selected_nodes[node.id]; + if(canvas.node_dragged == node) + canvas.node_dragged = null; } //remove from containers @@ -740,8 +714,10 @@ LGraph.prototype.remove = function(node) this._nodes.splice(pos,1); delete this._nodes_by_id[node.id]; - if(this.canvas) - this.canvas.setDirty(true,true); + if(this.onNodeRemoved) + this.onNodeRemoved(node); + + this.setDirtyCanvas(true,true); this.change(); @@ -784,11 +760,11 @@ LGraph.prototype.findNodesByType = function(type) * @return {Array} a list with all the nodes with this name */ -LGraph.prototype.findNodesByName = function(name) +LGraph.prototype.findNodesByTitle = function(title) { var result = []; for (var i in this._nodes) - if(this._nodes[i].name == name) + if(this._nodes[i].title == title) result.push(this._nodes[i]); return result; } @@ -814,6 +790,47 @@ LGraph.prototype.getNodeOnPos = function(x,y, nodes_list) return null; } +//Tell this graph has a global input of this type +LGraph.prototype.addGlobalInput = function(name, type, value) +{ + this.global_inputs[name] = { type: type, value: value }; +} + +//assign a data to the global input +LGraph.prototype.setGlobalInputData = function(name, data) +{ + var input = this.global_inputs[name]; + if (!input) + return; + input.value = data; +} + +//rename the global input +LGraph.prototype.renameGlobalInput = function(old_name, name, data) +{ +} + + +LGraph.prototype.addGlobalOutput = function(name, type, value) +{ + this.global_outputs[name] = { type: type, value: value }; +} + +//assign a data to the global output +LGraph.prototype.setGlobalOutputData = function(name, data) +{ + var output = this.global_outputs[ name ]; + if (!output) + return; + output.value = data; +} + +//rename the global output +LGraph.prototype.renameGlobalOutput = function(old_name, name, data) +{ +} + + /** * Assigns a value to all the nodes that matches this name. This is used to create global variables of the node that * can be easily accesed from the outside of the graph @@ -873,8 +890,12 @@ LGraph.prototype.onConnectionChange = function() LGraph.prototype.isLive = function() { - if(!this.canvas) return false; - return this.canvas.live_mode; + for(var i in this.list_of_graphcanvas) + { + var c = this.list_of_graphcanvas[i]; + if(c.live_mode) return true; + } + return false; } /* Called when something visually changed */ @@ -889,6 +910,11 @@ LGraph.prototype.change = function() this.on_change(this); } +LGraph.prototype.setDirtyCanvas = function(fg,bg) +{ + this.sendActionToCanvas("setDirty",[fg,bg]); +} + //save and recover app state *************************************** /** * Creates a Object containing all the info about this graph, it can be serialized @@ -940,7 +966,7 @@ LGraph.prototype.configure = function(data, keep_old) for (var i in nodes) { var n_info = nodes[i]; //stored info - var n = LiteGraph.createNode( n_info.type, n_info.name ); + var n = LiteGraph.createNode( n_info.type, n_info.title ); if(!n) { if(LiteGraph.debug) @@ -953,17 +979,13 @@ LGraph.prototype.configure = function(data, keep_old) this.add(n); } - //TODO: dispatch redraw - if(this.canvas) - this.canvas.draw(true,true); - + this.setDirtyCanvas(true,true); return error; } LGraph.prototype.onNodeTrace = function(node, msg, color) { - if(this.canvas) - this.canvas.onNodeTrace(node,msg,color); + //TODO } // ************************************************************* @@ -976,18 +998,21 @@ LGraph.prototype.onNodeTrace = function(node, msg, color) + unsafe_execution: not allowed for safe execution supported callbacks: - + onInit: when added to graph + + onAdded: when added to graph + + onRemoved: when removed from graph + onStart: when starts playing + onStop: when stops playing - + onDrawForeground - + onDrawBackground + + onDrawForeground: render the inside widgets inside the node + + onDrawBackground: render the background area inside the node (only in edit mode) + + onMouseDown + onMouseMove - + onMouseOver + + onMouseUp + + onMouseEnter + + onMouseLeave + onExecute: execute the node + onPropertyChange: when a property is changed in the panel (return true to skip default behaviour) + onGetInputs: returns an array of possible inputs + onGetOutputs: returns an array of possible outputs - + onClick + onDblClick + onSerialize + onSelected @@ -1000,9 +1025,9 @@ LGraph.prototype.onNodeTrace = function(node, msg, color) * @param {String} name a name for the node */ -function LGraphNode(name) +function LGraphNode(title) { - this.name = name || "Unnamed"; + this.title = title || "Unnamed"; this.size = [LiteGraph.NODE_WIDTH,60]; this.graph = null; @@ -1079,17 +1104,19 @@ LGraphNode.prototype.serialize = function() { var o = { id: this.id, - name: this.name, + title: this.title, type: this.type, pos: this.pos, size: this.size, data: this.data, - properties: LiteGraph.cloneObject(this.properties), flags: LiteGraph.cloneObject(this.flags), inputs: this.inputs, outputs: this.outputs }; + if(this.properties) + o.properties = LiteGraph.cloneObject(this.properties); + if(!o.type) o.type = this.constructor.type; @@ -1116,8 +1143,8 @@ LGraphNode.prototype.reducedObjectivize = function() var type = LiteGraph.getNodeType(o.type); - if(type.name == o.name) - delete o["name"]; + if(type.title == o.title) + delete o["title"]; if(type.size && compareObjects(o.size,type.size)) delete o["size"]; @@ -1141,6 +1168,18 @@ LGraphNode.prototype.toString = function() //LGraphNode.prototype.unserialize = function(info) {} //this cannot be done from within, must be done in LiteGraph +/** +* get the title string +* @method getTitle +*/ + +LGraphNode.prototype.getTitle = function() +{ + return this.title || this.constructor.title; +} + + + // Execution ************************* /** * sets the output data @@ -1274,6 +1313,29 @@ LGraphNode.prototype.addOutput = function(name,type,extra_info) this.size = this.computeSize(); } +/** +* add a new output slot to use in this node +* @method addOutputs +* @param {Array} array of triplets like [[name,type,extra_info],[...]] +*/ +LGraphNode.prototype.addOutputs = function(array) +{ + for(var i in array) + { + var info = array[i]; + var o = {name:info[0],type:info[1],link:null}; + if(array[2]) + for(var j in info[2]) + o[j] = info[2][j]; + + if(!this.outputs) + this.outputs = []; + this.outputs.push(o); + } + + this.size = this.computeSize(); +} + /** * remove an existing output slot * @method removeOutput @@ -1305,6 +1367,29 @@ LGraphNode.prototype.addInput = function(name,type,extra_info) this.size = this.computeSize(); } +/** +* add several new input slots in this node +* @method addInputs +* @param {Array} array of triplets like [[name,type,extra_info],[...]] +*/ +LGraphNode.prototype.addInputs = function(array) +{ + for(var i in array) + { + var info = array[i]; + var o = {name:info[0],type:info[1],link:null}; + if(array[2]) + for(var j in info[2]) + o[j] = info[2][j]; + + if(!this.inputs) + this.inputs = []; + this.inputs.push(o); + } + + this.size = this.computeSize(); +} + /** * remove an existing input slot * @method removeInput @@ -1520,7 +1605,7 @@ LGraphNode.prototype.disconnectOutput = function(slot, target_node) { output.links.splice(i,1); //remove here target_node.inputs[ link[4] ].link = null; //remove there - delete this.graph.links[link[0]]; + delete this.graph.links[link[0]]; //remove the link from the links pool break; } } @@ -1749,7 +1834,7 @@ LGraphNode.prototype.captureInput = function(v) //change c.node_capturing_input = v ? this : null; if(this.graph.debug) - console.log(this.name + ": Capturing input " + (v?"ON":"OFF")); + console.log(this.title + ": Capturing input " + (v?"ON":"OFF")); } } @@ -2328,8 +2413,8 @@ LGraphCanvas.prototype.processMouseMove = function(e) n.pos[0] += delta[0] / this.scale; n.pos[1] += delta[1] / this.scale; - n.pos[0] = Math.round(n.pos[0]); - n.pos[1] = Math.round(n.pos[1]); + //n.pos[0] = Math.round(n.pos[0]); + //n.pos[1] = Math.round(n.pos[1]); } this.dirty_canvas = true; @@ -2422,7 +2507,8 @@ LGraphCanvas.prototype.processMouseUp = function(e) { this.dirty_canvas = true; this.dirty_bgcanvas = true; - + this.node_dragged.pos[0] = Math.round(this.node_dragged.pos[0]); + this.node_dragged.pos[1] = Math.round(this.node_dragged.pos[1]); if(this.graph.config.align_to_grid) this.node_dragged.alignToGrid(); this.node_dragged = null; @@ -3009,10 +3095,10 @@ LGraphCanvas.prototype.drawNode = function(node, ctx ) } else if(this.render_shadows) { - ctx.shadowColor = "#111"; + ctx.shadowColor = "rgba(0,0,0,0.5)"; ctx.shadowOffsetX = 2; ctx.shadowOffsetY = 2; - ctx.shadowBlur = 4; + ctx.shadowBlur = 3; } else ctx.shadowColor = "transparent"; @@ -3269,10 +3355,11 @@ LGraphCanvas.prototype.drawNodeShape = function(node, ctx, size, fgcolor, bgcolo //title text ctx.font = this.title_text_font; - if(node.name != "" && this.scale > 0.8) + var title = node.getTitle(); + if(title && this.scale > 0.8) { ctx.fillStyle = "#222"; - ctx.fillText(node.name,16,13-title_height ); + ctx.fillText( title, 16, 13 - title_height ); } } } @@ -3385,7 +3472,7 @@ LGraphCanvas.prototype.renderLink = function(ctx,a,b,color) var dist = distance(a,b); - if(this.render_connections_border) + if(this.render_connections_border && this.scale > 0.6) ctx.lineWidth = this.connections_width + 4; ctx.beginPath(); @@ -3405,7 +3492,7 @@ LGraphCanvas.prototype.renderLink = function(ctx,a,b,color) ctx.lineTo(b[0]-10,b[1]); } - if(this.render_connections_border) + if(this.render_connections_border && this.scale > 0.6) { ctx.strokeStyle = "rgba(0,0,0,0.5)"; ctx.stroke(); @@ -3688,7 +3775,7 @@ LGraphCanvas.onMenuNodeOutputs = function(node, e, prev_menu) LGraphCanvas.onMenuNodeCollapse = function(node) { node.flags.collapsed = !node.flags.collapsed; - node.graph.canvas.setDirty(true,true); + node.setDirtyCanvas(true,true); } LGraphCanvas.onMenuNodePin = function(node) @@ -3715,7 +3802,7 @@ LGraphCanvas.onMenuNodeColors = function(node, e, prev_menu) { node.color = color.color; node.bgcolor = color.bgcolor; - node.graph.canvas.setDirty(true); + node.setDirtyCanvas(true); } } @@ -3730,7 +3817,7 @@ LGraphCanvas.onMenuNodeShapes = function(node,e) { if(!node) return; node.shape = v; - node.graph.canvas.setDirty(true); + node.setDirtyCanvas(true); } return false; @@ -3740,7 +3827,7 @@ LGraphCanvas.onMenuNodeRemove = function(node) { if(node.removable == false) return; node.graph.remove(node); - node.graph.canvas.setDirty(true,true); + node.setDirtyCanvas(true,true); } LGraphCanvas.onMenuNodeClone = function(node) @@ -3750,7 +3837,7 @@ LGraphCanvas.onMenuNodeClone = function(node) if(!newnode) return; newnode.pos = [node.pos[0]+5,node.pos[1]+5]; node.graph.add(newnode); - node.graph.canvas.setDirty(true,true); + node.setDirtyCanvas(true,true); } LGraphCanvas.node_colors = { diff --git a/src/nodes/base.js b/src/nodes/base.js new file mode 100644 index 000000000..49181b5b4 --- /dev/null +++ b/src/nodes/base.js @@ -0,0 +1,348 @@ +//basic nodes +(function(){ + + +//Input for a subgraph +function GlobalInput() +{ + this.addOutput("value",0); +} + +GlobalInput.prototype.onExecute = function() +{ + var name = this.title; + //read input + var value = node.graph.global_inputs[name]; + this.setOutputData(0,value); +} + +LiteGraph.registerNodeType("graph/input", GlobalInput); + + +//Output for a subgraph +function GlobalOutput() +{ + this.title = "Output"; + + //random name to avoid problems with other outputs when added + var genname = "input_" + (Math.random()*1000).toFixed(); + this.properties = { name: genname, type: "number" }; + this.addInput("value","number"); +} + +GlobalOutput.prototype.onAdded = function() +{ + var name = this.graph.addGlobalOutput( this.properties.name, this.properties.type ); +} + +GlobalOutput.prototype.onExecute = function() +{ + var value = this.getInputData(0); + this.graph.setGlobalOutputData( this.properties.name, value ); +} + +LiteGraph.registerNodeType("graph/output", GlobalOutput); + + +//Subgraph: a node that contains a graph +function Subgraph() +{ + this.subgraph = new LGraph(); + this.bgcolor = "#FA3"; +} + +Subgraph.prototype.onExecute = function() +{ + //send inputs to subgraph global inputs + for(var i in this.inputs) + { + var input = this.inputs[i]; + + //this.subgraph.setGlobalInputData( input.name, input.value ); + } + + //send subgraph global outputs to outputs +} + +Subgraph.prototype.configure = function(o) +{ + LGraph.prototype.configure.call(this, o); + //after configure, ... +} + +LiteGraph.registerNodeType("graph/subgraph", Subgraph); + + + +//Constant +function Constant() +{ + this.addOutput("value","number"); + this.properties = { value:1.0 }; + this.editable = { property:"value", type:"number" }; +} + +Constant.title = "Const"; +Constant.desc = "Constant value"; + + +Constant.prototype.setValue = function(v) +{ + if( typeof(v) == "string") v = parseFloat(v); + this.properties["value"] = v; + this.setDirtyCanvas(true); +}; + +Constant.prototype.onExecute = function() +{ + this.setOutputData(0, parseFloat( this.properties["value"] ) ); +} + +Constant.prototype.onDrawBackground = function(ctx) +{ + //show the current value + this.outputs[0].label = this.properties["value"].toFixed(3); +} + +Constant.prototype.onWidget = function(e,widget) +{ + if(widget.name == "value") + this.setValue(widget.value); +} + +LiteGraph.registerNodeType("basic/const", Constant); + + +//Watch a value in the editor +function Watch() +{ + this.size = [60,20]; + this.addInput("value",0,{label:""}); + this.addOutput("value",0,{label:""}); + this.properties = { value:"" }; +} + +Watch.title = "Watch"; +Watch.desc = "Show value of input"; + +Watch.prototype.onExecute = function() +{ + this.properties.value = this.getInputData(0); + this.setOutputData(0, this.properties.value); +} + +Watch.prototype.onDrawBackground = function(ctx) +{ + //show the current value + if(this.inputs[0] && this.properties["value"] != null) + { + if (this.properties["value"].constructor === Number ) + this.inputs[0].label = this.properties["value"].toFixed(3); + else + this.inputs[0].label = this.properties["value"]; + } +} + +LiteGraph.registerNodeType("basic/watch", Watch); + + + +/* +LiteGraph.registerNodeType("math/sinusoid",{ + title: "Sin", + desc: "Sinusoidal value generator", + bgImageUrl: "nodes/imgs/icon-sin.png", + + inputs: [["f",'number'],["q",'number'],["a",'number'],["t",'number']], + outputs: [["",'number']], + properties: {amplitude:1.0, freq: 1, phase:0}, + + onExecute: function() + { + var f = this.getInputData(0); + if(f != null) + this.properties["freq"] = f; + + var q = this.getInputData(1); + if(q != null) + this.properties["phase"] = q; + + var a = this.getInputData(2); + if(a != null) + this.properties["amplitude"] = a; + + var t = this.graph.getFixedTime(); + if(this.getInputData(3) != null) + t = this.getInputData(3); + // t = t/(2*Math.PI); t = (t-Math.floor(t))*(2*Math.PI); + + var v = this.properties["amplitude"] * Math.sin((2*Math.PI) * t * this.properties["freq"] + this.properties["phase"]); + this.setOutputData(0, v ); + }, + + onDragBackground: function(ctx) + { + this.boxcolor = colorToString(v > 0 ? [0.5,0.8,1,0.5] : [0,0,0,0.5]); + this.setDirtyCanvas(true); + }, +}); +*/ + +/* +LiteGraph.registerNodeType("basic/number",{ + title: "Number", + +// System vars ********************************* + +LiteGraph.registerNodeType("session/info",{ + title: "Time", + desc: "Seconds since start", + + outputs: [["secs",'number']], + properties: {scale:1.0}, + onExecute: function() + { + this.setOutputData(0, this.session.getTime() * this.properties.scale); + } +}); + +LiteGraph.registerNodeType("system/fixedtime",{ + title: "F.Time", + desc: "Constant time value", + + outputs: [["secs",'number']], + properties: {scale:1.0}, + onExecute: function() + { + this.setOutputData(0, this.session.getFixedTime() * this.properties.scale); + } +}); + + +LiteGraph.registerNodeType("system/elapsedtime",{ + title: "Elapsed", + desc: "Seconds elapsed since last execution", + + outputs: [["secs",'number']], + properties: {scale:1.0}, + onExecute: function() + { + this.setOutputData(0, this.session.getElapsedTime() * this.properties.scale); + } +}); + +LiteGraph.registerNodeType("system/iterations",{ + title: "Iterations", + desc: "Number of iterations (executions)", + + outputs: [["",'number']], + onExecute: function() + { + this.setOutputData(0, this.session.iterations ); + } +}); + +LiteGraph.registerNodeType("system/trace",{ + desc: "Outputs input to browser's console", + + inputs: [["",0]], + onExecute: function() + { + var data = this.getInputData(0); + if(data) + trace("DATA: "+data); + } +}); + +/* +LiteGraph.registerNodeType("math/not",{ + title: "Not", + desc: "0 -> 1 or 0 -> 1", + inputs: [["A",'number']], + outputs: [["!A",'number']], + size: [60,22], + onExecute: function() + { + var v = this.getInputData(0); + if(v != null) + this.setOutputData(0, v ? 0 : 1); + } +}); + + + +// Nodes for network in and out +LiteGraph.registerNodeType("network/general/network_input",{ + title: "N.Input", + desc: "Network Input", + outputs: [["",0]], + color: "#00ff96", + bgcolor: "#004327", + + setValue: function(v) + { + this.value = v; + }, + + onExecute: function() + { + this.setOutputData(0, this.value); + } +}); + +LiteGraph.registerNodeType("network/general/network_output",{ + title: "N.Output", + desc: "Network output", + inputs: [["",0]], + color: "#a8ff00", + bgcolor: "#293e00", + + properties: {value:null}, + + getValue: function() + { + return this.value; + }, + + onExecute: function() + { + this.value = this.getOutputData(0); + } +}); + +LiteGraph.registerNodeType("network/network_trigger",{ + title: "N.Trigger", + desc: "Network input trigger", + outputs: [["",0]], + color: "#ff9000", + bgcolor: "#522e00", + + onTrigger: function(v) + { + this.triggerOutput(0,v); + }, +}); + +LiteGraph.registerNodeType("network/network_callback",{ + title: "N.Callback", + desc: "Network callback output.", + outputs: [["",0]], + color: "#6A6", + bgcolor: "#363", + + setTrigger: function(func) + { + this.callback = func; + }, + + onTrigger: function(v) + { + if(this.callback) + this.callback(v); + }, +}); + +*/ + + +})(); \ No newline at end of file diff --git a/src/nodes/image.js b/src/nodes/image.js new file mode 100644 index 000000000..ee5fc6eac --- /dev/null +++ b/src/nodes/image.js @@ -0,0 +1,645 @@ +(function(){ + + +function ColorPalette() +{ + this.addInput("f","number"); + this.addOutput("Color","color"); + this.properties = {colorA:"#444444",colorB:"#44AAFF",colorC:"#44FFAA",colorD:"#FFFFFF"}; + +} + +ColorPalette.title = "Palette"; +ColorPalette.desc = "Generates a color"; + +ColorPalette.prototype.onExecute = function() +{ + var c = []; + + if (this.properties.colorA != null) + c.push( hex2num( this.properties.colorA ) ); + if (this.properties.colorB != null) + c.push( hex2num( this.properties.colorB ) ); + if (this.properties.colorC != null) + c.push( hex2num( this.properties.colorC ) ); + if (this.properties.colorD != null) + c.push( hex2num( this.properties.colorD ) ); + + var f = this.getInputData(0); + if(f == null) f = 0.5; + if (f > 1.0) + f = 1.0; + else if (f < 0.0) + f = 0.0; + + if(c.length == 0) + return; + + var result = [0,0,0]; + if(f == 0) + result = c[0]; + else if(f == 1) + result = c[ c.length - 1]; + else + { + var pos = (c.length - 1)* f; + var c1 = c[ Math.floor(pos) ]; + var c2 = c[ Math.floor(pos)+1 ]; + var t = pos - Math.floor(pos); + result[0] = c1[0] * (1-t) + c2[0] * (t); + result[1] = c1[1] * (1-t) + c2[1] * (t); + result[2] = c1[2] * (1-t) + c2[2] * (t); + } + + /* + c[0] = 1.0 - Math.abs( Math.sin( 0.1 * reModular.getTime() * Math.PI) ); + c[1] = Math.abs( Math.sin( 0.07 * reModular.getTime() * Math.PI) ); + c[2] = Math.abs( Math.sin( 0.01 * reModular.getTime() * Math.PI) ); + */ + + for(var i in result) + result[i] /= 255; + + this.boxcolor = colorToString(result); + this.setOutputData(0, result); +} + + +LiteGraph.registerNodeType("color/palette", ColorPalette ); + + +function ImageFrame() +{ + this.addInput("","image"); + this.size = [200,200]; +} + +ImageFrame.title = "Frame"; +ImageFrame.desc = "Frame viewerew"; +ImageFrame.widgets = [{name:"resize",text:"Resize box",type:"button"},{name:"view",text:"View Image",type:"button"}]; + + +ImageFrame.prototype.onDrawBackground = function(ctx) +{ + if(this.frame) + ctx.drawImage(this.frame, 0,0,this.size[0],this.size[1]); +} + +ImageFrame.prototype.onExecute = function() +{ + this.frame = this.getInputData(0); + this.setDirtyCanvas(true); +} + +ImageFrame.prototype.onWidget = function(e,widget) +{ + if(widget.name == "resize" && this.frame) + { + var width = this.frame.width; + var height = this.frame.height; + + if(!width && this.frame.videoWidth != null ) + { + width = this.frame.videoWidth; + height = this.frame.videoHeight; + } + + if(width && height) + this.size = [width, height]; + this.setDirtyCanvas(true,true); + } + else if(widget.name == "view") + this.show(); +} + +ImageFrame.prototype.show = function() +{ + //var str = this.canvas.toDataURL("image/png"); + if(showElement && this.frame) + showElement(this.frame); +} + + +LiteGraph.registerNodeType("graphics/frame", ImageFrame ); + + + +/* +LiteGraph.registerNodeType("visualization/graph", { + desc: "Shows a graph of the inputs", + + inputs: [["",0],["",0],["",0],["",0]], + size: [200,200], + properties: {min:-1,max:1,bgColor:"#000"}, + onDrawBackground: function(ctx) + { + var colors = ["#FFF","#FAA","#AFA","#AAF"]; + + if(this.properties.bgColor != null && this.properties.bgColor != "") + { + ctx.fillStyle="#000"; + ctx.fillRect(2,2,this.size[0] - 4, this.size[1]-4); + } + + if(this.data) + { + var min = this.properties["min"]; + var max = this.properties["max"]; + + for(var i in this.data) + { + var data = this.data[i]; + if(!data) continue; + + if(this.getInputInfo(i) == null) continue; + + ctx.strokeStyle = colors[i]; + ctx.beginPath(); + + var d = data.length / this.size[0]; + for(var j = 0; j < data.length; j += d) + { + var value = data[ Math.floor(j) ]; + value = (value - min) / (max - min); + if (value > 1.0) value = 1.0; + else if(value < 0) value = 0; + + if(j == 0) + ctx.moveTo( j / d, (this.size[1] - 5) - (this.size[1] - 10) * value); + else + ctx.lineTo( j / d, (this.size[1] - 5) - (this.size[1] - 10) * value); + } + + ctx.stroke(); + } + } + + //ctx.restore(); + }, + + onExecute: function() + { + if(!this.data) this.data = []; + + for(var i in this.inputs) + { + var value = this.getInputData(i); + + if(typeof(value) == "number") + { + value = value ? value : 0; + if(!this.data[i]) + this.data[i] = []; + this.data[i].push(value); + + if(this.data[i].length > (this.size[1] - 4)) + this.data[i] = this.data[i].slice(1,this.data[i].length); + } + else + this.data[i] = value; + } + + if(this.data.length) + this.setDirtyCanvas(true); + } + }); +*/ + +function ImageFade() +{ + this.addInputs([["img1","image"],["img2","image"],["fade","number"]]); + this.addInput("","image"); + this.properties = {fade:0.5,width:512,height:512}; +} + +ImageFade.title = "Image fade"; +ImageFade.desc = "Fades between images"; +ImageFade.widgets = [{name:"resizeA",text:"Resize to A",type:"button"},{name:"resizeB",text:"Resize to B",type:"button"}]; + +ImageFade.prototype.onAdded = function() +{ + this.createCanvas(); + var ctx = this.canvas.getContext("2d"); + ctx.fillStyle = "#000"; + ctx.fillRect(0,0,this.properties["width"],this.properties["height"]); +} + +ImageFade.prototype.createCanvas = function() +{ + this.canvas = document.createElement("canvas"); + this.canvas.width = this.properties["width"]; + this.canvas.height = this.properties["height"]; +} + +ImageFade.prototype.onExecute = function() +{ + var ctx = this.canvas.getContext("2d"); + this.canvas.width = this.canvas.width; + + var A = this.getInputData(0); + if (A != null) + { + ctx.drawImage(A,0,0,this.canvas.width, this.canvas.height); + } + + var fade = this.getInputData(2); + if(fade == null) + fade = this.properties["fade"]; + else + this.properties["fade"] = fade; + + ctx.globalAlpha = fade; + var B = this.getInputData(1); + if (B != null) + { + ctx.drawImage(B,0,0,this.canvas.width, this.canvas.height); + } + ctx.globalAlpha = 1.0; + + this.setOutputData(0,this.canvas); + this.setDirtyCanvas(true); +} + +LiteGraph.registerNodeType("graphics/imagefade", ImageFade); + + +function GraphicsImage() +{ + this.inputs = []; + this.addOutput("frame","image"); + this.properties = {"url":""}; +} + +GraphicsImage.title = "Image"; +GraphicsImage.desc = "Image loader"; +GraphicsImage.widgets = [{name:"load",text:"Load",type:"button"}]; + + +GraphicsImage.prototype.onAdded = function() +{ + if(this.properties["url"] != "" && this.img == null) + { + this.loadImage(this.properties["url"]); + } +} + + +GraphicsImage.prototype.onExecute = function() +{ + if(!this.img) + this.boxcolor = "#000"; + if(this.img && this.img.width) + this.setOutputData(0,this.img); + else + this.setOutputData(0,null); + if(this.img.dirty) + this.img.dirty = false; +} + +GraphicsImage.prototype.onPropertyChange = function(name,value) +{ + this.properties[name] = value; + if (name == "url" && value != "") + this.loadImage(value); + + return true; +} + +GraphicsImage.prototype.loadImage = function(url) +{ + if(url == "") + { + this.img = null; + return; + } + + this.trace("loading image..."); + this.img = document.createElement("img"); + this.img.src = "miniproxy.php?url=" + url; + this.boxcolor = "#F95"; + var that = this; + this.img.onload = function() + { + that.trace("Image loaded, size: " + that.img.width + "x" + that.img.height ); + this.dirty = true; + that.boxcolor = "#9F9"; + that.setDirtyCanvas(true); + } +} + +GraphicsImage.prototype.onWidget = function(e,widget) +{ + if(widget.name == "load") + { + this.loadImage(this.properties["url"]); + } +} + +LiteGraph.registerNodeType("graphics/image", GraphicsImage); + + + +function ImageCrop() +{ + this.addInput("","image"); + this.addOutputs("","image"); + this.properties = {width:256,height:256,x:0,y:0,scale:1.0 }; + this.size = [50,20]; +} + +ImageCrop.title = "Crop"; +ImageCrop.desc = "Crop Image"; + +ImageCrop.prototype.onAdded = function() +{ + this.createCanvas(); +} + +ImageCrop.prototype.createCanvas = function() +{ + this.canvas = document.createElement("canvas"); + this.canvas.width = this.properties["width"]; + this.canvas.height = this.properties["height"]; +} + +ImageCrop.prototype.onExecute = function() +{ + var input = this.getInputData(0); + if(!input) return; + + if(input.width) + { + var ctx = this.canvas.getContext("2d"); + + ctx.drawImage(input, -this.properties["x"],-this.properties["y"], input.width * this.properties["scale"], input.height * this.properties["scale"]); + this.setOutputData(0,this.canvas); + } + else + this.setOutputData(0,null); +} + +ImageCrop.prototype.onPropertyChange = function(name,value) +{ + this.properties[name] = value; + + if(name == "scale") + { + this.properties[name] = parseFloat(value); + if(this.properties[name] == 0) + { + this.trace("Error in scale"); + this.properties[name] = 1.0; + } + } + else + this.properties[name] = parseInt(value); + + this.createCanvas(); + + return true; +} + +LiteGraph.registerNodeType("graphics/cropImage", ImageFade ); + + +function ImageVideo() +{ + this.addInput("t","number"); + this.addOutputs([["frame","image"],["t","number"],["d","number"]]); + this.properties = {"url":""}; +} + +ImageVideo.title = "Video"; +ImageVideo.desc = "Video playback"; +ImageVideo.widgets = [{name:"play",text:"PLAY",type:"minibutton"},{name:"stop",text:"STOP",type:"minibutton"},{name:"demo",text:"Demo video",type:"button"},{name:"mute",text:"Mute video",type:"button"}]; + +ImageVideo.prototype.onExecute = function() +{ + if(!this.properties.url) + return; + + if(this.properties.url != this._video_url) + this.loadVideo(this.properties.url); + + if(!this._video || this._video.width == 0) + return; + + var t = this.getInputData(0); + if(t && t >= 0 && t <= 1.0) + { + this._video.currentTime = t * this._video.duration; + this._video.pause(); + } + + this._video.dirty = true; + this.setOutputData(0,this._video); + this.setOutputData(1,this._video.currentTime); + this.setOutputData(2,this._video.duration); + this.setDirtyCanvas(true); +} + +ImageVideo.prototype.onStart = function() +{ + this.play(); +} + +ImageVideo.prototype.onStop = function() +{ + this.stop(); +} + +ImageVideo.prototype.loadVideo = function(url) +{ + this._video_url = url; + + this._video = document.createElement("video"); + this._video.src = url; + this._video.type = "type=video/mp4"; + + this._video.muted = true; + this._video.autoplay = true; + + var that = this; + this._video.addEventListener("loadedmetadata",function(e) { + //onload + that.trace("Duration: " + this.duration + " seconds"); + that.trace("Size: " + this.videoWidth + "," + this.videoHeight); + that.setDirtyCanvas(true); + this.width = this.videoWidth; + this.height = this.videoHeight; + }); + this._video.addEventListener("progress",function(e) { + //onload + //that.trace("loading..."); + }); + this._video.addEventListener("error",function(e) { + console.log("Error loading video: " + this.src); + that.trace("Error loading video: " + this.src); + if (this.error) { + switch (this.error.code) { + case this.error.MEDIA_ERR_ABORTED: + that.trace("You stopped the video."); + break; + case this.error.MEDIA_ERR_NETWORK: + that.trace("Network error - please try again later."); + break; + case this.error.MEDIA_ERR_DECODE: + that.trace("Video is broken.."); + break; + case this.error.MEDIA_ERR_SRC_NOT_SUPPORTED: + that.trace("Sorry, your browser can't play this video."); + break; + } + } + }); + + this._video.addEventListener("ended",function(e) { + that.trace("Ended."); + this.play(); //loop + }); + + //document.body.appendChild(this.video); +} + +ImageVideo.prototype.onPropertyChange = function(name,value) +{ + this.properties[name] = value; + if (name == "url" && value != "") + this.loadVideo(value); + + return true; +} + +ImageVideo.prototype.play = function() +{ + if(this._video) + this._video.play(); +} + +ImageVideo.prototype.playPause = function() +{ + if(!this._video) + return; + if(this._video.paused) + this.play(); + else + this.pause(); +} + +ImageVideo.prototype.stop = function() +{ + if(!this._video) + return; + this._video.pause(); + this._video.currentTime = 0; +} + +ImageVideo.prototype.pause = function() +{ + if(!this._video) + return; + this.trace("Video paused"); + this._video.pause(); +} + +ImageVideo.prototype.onWidget = function(e,widget) +{ + /* + if(widget.name == "demo") + { + this.loadVideo(); + } + else if(widget.name == "play") + { + if(this._video) + this.playPause(); + } + if(widget.name == "stop") + { + this.stop(); + } + else if(widget.name == "mute") + { + if(this._video) + this._video.muted = !this._video.muted; + } + */ +} + +LiteGraph.registerNodeType("graphics/video", ImageVideo ); + + +// Texture Webcam ***************************************** +function ImageWebcam() +{ + this.addOutput("Webcam","image"); + this.properties = {}; +} + +ImageWebcam.title = "Webcam"; +ImageWebcam.desc = "Webcam image"; + + +ImageWebcam.prototype.openStream = function() +{ + //Vendor prefixes hell + navigator.getUserMedia = (navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia); + window.URL = window.URL || window.webkitURL; + + if (!navigator.getUserMedia) { + //console.log('getUserMedia() is not supported in your browser, use chrome and enable WebRTC from about://flags'); + return; + } + + this._waiting_confirmation = true; + + // Not showing vendor prefixes. + navigator.getUserMedia({video: true}, this.streamReady.bind(this), onFailSoHard); + + var that = this; + function onFailSoHard(e) { + trace('Webcam rejected', e); + that._webcam_stream = false; + that.box_color = "red"; + }; +} + +ImageWebcam.prototype.streamReady = function(localMediaStream) +{ + this._webcam_stream = localMediaStream; + //this._waiting_confirmation = false; + + var video = this._video; + if(!video) + { + video = document.createElement("video"); + video.autoplay = true; + video.src = window.URL.createObjectURL(localMediaStream); + this._video = video; + //document.body.appendChild( video ); //debug + //when video info is loaded (size and so) + video.onloadedmetadata = function(e) { + // Ready to go. Do some stuff. + console.log(e); + }; + } + + +}, + +ImageWebcam.prototype.onExecute = function() +{ + if(this._webcam_stream == null && !this._waiting_confirmation) + this.openStream(); + + if(!this._video || !this._video.videoWidth) return; + + this._video.width = this._video.videoWidth; + this._video.hieght = this._video.videoHeight; + this.setOutputData(0, this._video); +} + +LiteGraph.registerNodeType("graphics/webcam", ImageWebcam ); + + +})(); \ No newline at end of file diff --git a/src/nodes/interface.js b/src/nodes/interface.js new file mode 100644 index 000000000..623c6d09c --- /dev/null +++ b/src/nodes/interface.js @@ -0,0 +1,720 @@ +//widgets +(function(){ + + function WidgetKnob() + { + this.size = [64,84]; + this.addOutput("",'number'); + this.properties = {min:0,max:1,value:0.5,wcolor:"#7AF",size:50}; + } + + WidgetKnob.title = "Knob"; + WidgetKnob.desc = "Circular controller"; + WidgetKnob.widgets = [{name:"increase",text:"+",type:"minibutton"},{name:"decrease",text:"-",type:"minibutton"}]; + + + WidgetKnob.prototype.onAdded = 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"); + } + + WidgetKnob.prototype.onDrawImageKnob = function(ctx) + { + if(!this.imgfg || !this.imgfg.width) return; + + var d = this.imgbg.width*0.5; + var scale = this.size[0] / this.imgfg.width; + + ctx.save(); + ctx.translate(0,20); + ctx.scale(scale,scale); + ctx.drawImage(this.imgbg,0,0); + //ctx.drawImage(this.imgfg,0,20); + + ctx.translate(d,d); + ctx.rotate(this.value * (Math.PI*2) * 6/8 + Math.PI * 10/8); + //ctx.rotate(this.value * (Math.PI*2)); + ctx.translate(-d,-d); + ctx.drawImage(this.imgfg,0,0); + + ctx.restore(); + + ctx.font = "bold 16px Criticized,Tahoma"; + ctx.fillStyle="rgba(100,100,100,0.8)"; + ctx.textAlign = "center"; + + ctx.fillText(this.name.toUpperCase(), this.size[0] * 0.5, 18 ); + ctx.textAlign = "left"; + } + + WidgetKnob.prototype.onDrawVectorKnob = function(ctx) + { + if(!this.imgfg || !this.imgfg.width) return; + + //circle around + ctx.lineWidth = 1; + ctx.strokeStyle= this.mouseOver ? "#FFF" : "#AAA"; + ctx.fillStyle="#000"; + ctx.beginPath(); + ctx.arc(this.size[0] * 0.5,this.size[1] * 0.5 + 10,this.properties.size * 0.5,0,Math.PI*2,true); + ctx.stroke(); + + if(this.value > 0) + { + ctx.strokeStyle=this.properties["wcolor"]; + ctx.lineWidth = (this.properties.size * 0.2); + ctx.beginPath(); + ctx.arc(this.size[0] * 0.5,this.size[1] * 0.5 + 10,this.properties.size * 0.35,Math.PI * -0.5 + Math.PI*2 * this.value,Math.PI * -0.5,true); + ctx.stroke(); + ctx.lineWidth = 1; + } + + ctx.font = (this.properties.size * 0.2) + "px Arial"; + ctx.fillStyle="#AAA"; + ctx.textAlign = "center"; + + var str = this.properties["value"]; + if(typeof(str) == 'number') + str = str.toFixed(2); + + ctx.fillText(str,this.size[0] * 0.5,this.size[1]*0.65); + ctx.textAlign = "left"; + } + + WidgetKnob.prototype.onDrawForeground = function(ctx) + { + this.onDrawImageKnob(ctx); + } + + WidgetKnob.prototype.onExecute = function() + { + this.setOutputData(0, this.properties["value"] ); + + this.boxcolor = colorToString([this.value,this.value,this.value]); + } + + WidgetKnob.prototype.onMouseDown = function(e) + { + if(!this.imgfg || !this.imgfg.width) return; + + //this.center = [this.imgbg.width * 0.5, this.imgbg.height * 0.5 + 20]; + //this.radius = this.imgbg.width * 0.5; + this.center = [this.size[0] * 0.5, this.size[1] * 0.5 + 20]; + this.radius = this.size[0] * 0.5; + + if(e.canvasY - this.pos[1] < 20 || distance([e.canvasX,e.canvasY],[this.pos[0] + this.center[0],this.pos[1] + this.center[1]]) > this.radius) + return false; + + this.oldmouse = [ e.canvasX - this.pos[0], e.canvasY - this.pos[1] ]; + this.captureInput(true); + + /* + var tmp = this.localToScreenSpace(0,0); + this.trace(tmp[0] + "," + tmp[1]); */ + return true; + } + + WidgetKnob.prototype.onMouseMove = function(e) + { + if(!this.oldmouse) return; + + var m = [ e.canvasX - this.pos[0], e.canvasY - this.pos[1] ]; + + var v = this.value; + v -= (m[1] - this.oldmouse[1]) * 0.01; + if(v > 1.0) v = 1.0; + else if(v < 0.0) v = 0.0; + + this.value = v; + this.properties["value"] = this.properties["min"] + (this.properties["max"] - this.properties["min"]) * this.value; + + this.oldmouse = m; + this.setDirtyCanvas(true); + } + + WidgetKnob.prototype.onMouseUp = function(e) + { + if(this.oldmouse) + { + this.oldmouse = null; + this.captureInput(false); + } + } + + WidgetKnob.prototype.onMouseLeave = function(e) + { + //this.oldmouse = null; + } + + WidgetKnob.prototype.onWidget = function(e,widget) + { + if(widget.name=="increase") + this.onPropertyChange("size", this.properties.size + 10); + else if(widget.name=="decrease") + this.onPropertyChange("size", this.properties.size - 10); + } + + WidgetKnob.prototype.onPropertyChange = function(name,value) + { + if(name=="wcolor") + this.properties[name] = value; + else if(name=="size") + { + value = parseInt(value); + this.properties[name] = value; + this.size = [value+4,value+24]; + this.setDirtyCanvas(true,true); + } + else if(name=="min" || name=="max" || name=="value") + { + this.properties[name] = parseFloat(value); + } + else + return false; + return true; + } + + LiteGraph.registerNodeType("widget/knob", WidgetKnob); + + //Widget H SLIDER + function WidgetHSlider() + { + this.size = [160,26]; + this.addOutput("",'number'); + this.properties = {wcolor:"#7AF",min:0,max:1,value:0.5}; + } + + WidgetHSlider.title = "H.Slider"; + WidgetHSlider.desc = "Linear slider controller"; + + WidgetHSlider.prototype.onInit = function() + { + this.value = 0.5; + this.imgfg = this.loadImage("imgs/slider_fg.png"); + } + + WidgetHSlider.prototype.onDrawVectorial = function(ctx) + { + if(!this.imgfg || !this.imgfg.width) return; + + //border + ctx.lineWidth = 1; + ctx.strokeStyle= this.mouseOver ? "#FFF" : "#AAA"; + ctx.fillStyle="#000"; + ctx.beginPath(); + ctx.rect(2,0,this.size[0]-4,20); + ctx.stroke(); + + ctx.fillStyle=this.properties["wcolor"]; + ctx.beginPath(); + ctx.rect(2+(this.size[0]-4-20)*this.value,0, 20,20); + ctx.fill(); + } + + WidgetHSlider.prototype.onDrawImage = function(ctx) + { + if(!this.imgfg || !this.imgfg.width) + return; + + //border + ctx.lineWidth = 1; + ctx.fillStyle="#000"; + ctx.fillRect(2,9,this.size[0]-4,2); + + ctx.strokeStyle= "#333"; + ctx.beginPath(); + ctx.moveTo(2,9); + ctx.lineTo(this.size[0]-4,9); + ctx.stroke(); + + ctx.strokeStyle= "#AAA"; + ctx.beginPath(); + ctx.moveTo(2,11); + ctx.lineTo(this.size[0]-4,11); + ctx.stroke(); + + ctx.drawImage(this.imgfg, 2+(this.size[0]-4)*this.value - this.imgfg.width*0.5,-this.imgfg.height*0.5 + 10); + }, + + WidgetHSlider.prototype.onDrawForeground = function(ctx) + { + this.onDrawImage(ctx); + } + + WidgetHSlider.prototype.onExecute = function() + { + this.properties["value"] = this.properties["min"] + (this.properties["max"] - this.properties["min"]) * this.value; + this.setOutputData(0, this.properties["value"] ); + this.boxcolor = colorToString([this.value,this.value,this.value]); + } + + WidgetHSlider.prototype.onMouseDown = function(e) + { + if(e.canvasY - this.pos[1] < 0) + return false; + + this.oldmouse = [ e.canvasX - this.pos[0], e.canvasY - this.pos[1] ]; + this.captureInput(true); + return true; + } + + WidgetHSlider.prototype.onMouseMove = function(e) + { + if(!this.oldmouse) return; + + var m = [ e.canvasX - this.pos[0], e.canvasY - this.pos[1] ]; + + var v = this.value; + var delta = (m[0] - this.oldmouse[0]); + v += delta / this.size[0]; + if(v > 1.0) v = 1.0; + else if(v < 0.0) v = 0.0; + + this.value = v; + + this.oldmouse = m; + this.setDirtyCanvas(true); + } + + WidgetHSlider.prototype.onMouseUp = function(e) + { + this.oldmouse = null; + this.captureInput(false); + } + + WidgetHSlider.prototype.onMouseLeave = function(e) + { + //this.oldmouse = null; + } + + WidgetHSlider.prototype.onPropertyChange = function(name,value) + { + if(name=="wcolor") + this.properties[name] = value; + else + return false; + return true; + } + + LiteGraph.registerNodeType("widget/hslider", WidgetHSlider ); + + + function WidgetProgress() + { + this.size = [160,26]; + this.addInput("",'number'); + this.properties = {min:0,max:1,value:0,wcolor:"#AAF"}; + } + + WidgetProgress.title = "Progress"; + WidgetProgress.desc = "Shows data in linear progress"; + + WidgetProgress.prototype.onExecute = function() + { + var v = this.getInputData(0); + if( v != undefined ) + this.properties["value"] = v; + } + + WidgetProgress.prototype.onDrawForeground = function(ctx) + { + //border + ctx.lineWidth = 1; + ctx.fillStyle=this.properties.wcolor; + var v = (this.properties.value - this.properties.min) / (this.properties.max - this.properties.min); + v = Math.min(1,v); + v = Math.max(0,v); + ctx.fillRect(2,2,(this.size[0]-4)*v,this.size[1]-4); + } + + LiteGraph.registerNodeType("widget/progress", WidgetProgress); + + + /* + 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(ctx) + { + this.lineargradient = ctx.createLinearGradient(0,0,0,this.size[1]); + this.lineargradient.addColorStop(0,this.properties["bgcolorTop"]); + this.lineargradient.addColorStop(1,this.properties["bgcolorBottom"]); + }, + + onDrawBackground: function(ctx) + { + if(!this.lineargradient) + this.createGradient(ctx); + + ctx.lineWidth = 1; + ctx.strokeStyle = this.properties["borderColor"]; + //ctx.fillStyle = "#ebebeb"; + ctx.fillStyle = this.lineargradient; + + ctx.shadowColor = "#000"; + ctx.shadowOffsetX = 0; + ctx.shadowOffsetY = 0; + ctx.shadowBlur = this.properties["shadowSize"]; + ctx.roundRect(0,0,this.size[0],this.size[1],this.properties["shadowSize"]); + ctx.fill(); + ctx.shadowColor = "rgba(0,0,0,0)"; + ctx.stroke(); + + ctx.fillStyle = "#A00"; + ctx.fillRect(this.size[0] * this.properties["x"] - 5, this.size[1] * this.properties["y"] - 5,10,10); + }, + + onWidget: function(e,widget) + { + if(widget.name == "update") + { + this.lineargradient = null; + this.setDirtyCanvas(true); + } + }, + + onExecute: function() + { + this.setOutputData(0, this.properties["x"] ); + this.setOutputData(1, this.properties["y"] ); + }, + + onMouseDown: function(e) + { + if(e.canvasY - this.pos[1] < 0) + return false; + + this.oldmouse = [ e.canvasX - this.pos[0], e.canvasY - this.pos[1] ]; + this.captureInput(true); + return true; + }, + + onMouseMove: function(e) + { + if(!this.oldmouse) return; + + var m = [ e.canvasX - this.pos[0], e.canvasY - this.pos[1] ]; + + this.properties.x = m[0] / this.size[0]; + this.properties.y = m[1] / this.size[1]; + + if(this.properties.x > 1.0) this.properties.x = 1.0; + else if(this.properties.x < 0.0) this.properties.x = 0.0; + + if(this.properties.y > 1.0) this.properties.y = 1.0; + else if(this.properties.y < 0.0) this.properties.y = 0.0; + + this.oldmouse = m; + this.setDirtyCanvas(true); + }, + + onMouseUp: function(e) + { + if(this.oldmouse) + { + this.oldmouse = null; + this.captureInput(false); + } + }, + + onMouseLeave: function(e) + { + //this.oldmouse = null; + } + }); + + + + 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(ctx) + { + this.lineargradient = ctx.createLinearGradient(0,0,0,this.size[1]); + this.lineargradient.addColorStop(0,this.properties["bgcolorTop"]); + this.lineargradient.addColorStop(1,this.properties["bgcolorBottom"]); + }, + + drawVectorShape: function(ctx) + { + ctx.fillStyle = this.mouseOver ? this.properties["color"] : "#AAA"; + + if(this.clicking) + ctx.fillStyle = "#FFF"; + + ctx.strokeStyle = "#AAA"; + ctx.roundRect(5,5,this.size[0] - 10,this.size[1] - 10,4); + ctx.stroke(); + + if(this.mouseOver) + ctx.fill(); + + //ctx.fillRect(5,20,this.size[0] - 10,this.size[1] - 30); + + ctx.fillStyle = this.mouseOver ? "#000" : "#AAA"; + ctx.font = "bold " + this.properties["fontsize"] + "px Criticized,Tahoma"; + ctx.textAlign = "center"; + ctx.fillText(this.properties["text"],this.size[0]*0.5,this.size[1]*0.5 + 0.5*parseInt(this.properties["fontsize"])); + ctx.textAlign = "left"; + }, + + drawBevelShape: function(ctx) + { + ctx.shadowColor = "#000"; + ctx.shadowOffsetX = 0; + ctx.shadowOffsetY = 0; + ctx.shadowBlur = this.properties["shadowSize"]; + + if(!this.lineargradient) + this.createGradient(ctx); + + ctx.fillStyle = this.mouseOver ? this.properties["color"] : this.lineargradient; + if(this.clicking) + ctx.fillStyle = "#444"; + + ctx.strokeStyle = "#FFF"; + ctx.roundRect(5,5,this.size[0] - 10,this.size[1] - 10,4); + ctx.fill(); + ctx.shadowColor = "rgba(0,0,0,0)"; + ctx.stroke(); + + ctx.fillStyle = this.mouseOver ? "#000" : "#444"; + ctx.font = "bold " + this.properties["fontsize"] + "px Century Gothic"; + ctx.textAlign = "center"; + ctx.fillText(this.properties["text"],this.size[0]*0.5,this.size[1]*0.5 + 0.40*parseInt(this.properties["fontsize"])); + ctx.textAlign = "left"; + }, + + onDrawForeground: function(ctx) + { + this.drawBevelShape(ctx); + }, + + clickButton: function() + { + var module = this.getOutputModule(0); + if(this.properties["command"] && this.properties["command"] != "") + { + if (! module.executeAction(this.properties["command"]) ) + this.trace("Error executing action in other module"); + } + else if(module && module.onTrigger) + { + module.onTrigger(); + } + }, + + onMouseDown: function(e) + { + if(e.canvasY - this.pos[1] < 2) + return false; + this.clickButton(); + this.clicking = true; + return true; + }, + + onMouseUp: function(e) + { + this.clicking = false; + }, + + onExecute: function() + { + }, + + onWidget: function(e,widget) + { + if(widget.name == "test") + { + this.clickButton(); + } + }, + + onPropertyChange: function(name,value) + { + this.properties[name] = value; + return true; + } + }); + */ + + + function WidgetText() + { + this.addInputs("",0); + this.properties = { value:"...",font:"Arial", fontsize:18, color:"#AAA", align:"left", glowSize:0, decimals:1 }; + } + + WidgetText.title = "Text"; + WidgetText.desc = "Shows the input value"; + WidgetText.widgets = [{name:"resize",text:"Resize box",type:"button"},{name:"led_text",text:"LED",type:"minibutton"},{name:"normal_text",text:"Normal",type:"minibutton"}]; + + WidgetText.prototype.onDrawForeground = function(ctx) + { + //ctx.fillStyle="#000"; + //ctx.fillRect(0,0,100,60); + ctx.fillStyle = this.properties["color"]; + var v = this.properties["value"]; + + if(this.properties["glowSize"]) + { + ctx.shadowColor = this.properties["color"]; + ctx.shadowOffsetX = 0; + ctx.shadowOffsetY = 0; + ctx.shadowBlur = this.properties["glowSize"]; + } + else + ctx.shadowColor = "transparent"; + + var fontsize = this.properties["fontsize"]; + + ctx.textAlign = this.properties["align"]; + ctx.font = fontsize.toString() + "px " + this.properties["font"]; + this.str = typeof(v) == 'number' ? v.toFixed(this.properties["decimals"]) : v; + + if( typeof(this.str) == 'string') + { + var lines = this.str.split("\\n"); + for(var i in lines) + ctx.fillText(lines[i],this.properties["align"] == "left" ? 15 : this.size[0] - 15, fontsize * -0.15 + fontsize * (parseInt(i)+1) ); + } + + ctx.shadowColor = "transparent"; + this.last_ctx = ctx; + ctx.textAlign = "left"; + } + + WidgetText.prototype.onExecute = function() + { + var v = this.getInputData(0); + if(v != null) + this.properties["value"] = v; + else + this.properties["value"] = ""; + this.setDirtyCanvas(true); + } + + WidgetText.prototype.resize = function() + { + if(!this.last_ctx) return; + + var lines = this.str.split("\\n"); + this.last_ctx.font = this.properties["fontsize"] + "px " + this.properties["font"]; + var max = 0; + for(var i in lines) + { + var w = this.last_ctx.measureText(lines[i]).width; + if(max < w) max = w; + } + this.size[0] = max + 20; + this.size[1] = 4 + lines.length * this.properties["fontsize"]; + + this.setDirtyCanvas(true); + } + + WidgetText.prototype.onWidget = function(e,widget) + { + if(widget.name == "resize") + this.resize(); + else if (widget.name == "led_text") + { + this.properties["font"] = "Digital"; + this.properties["glowSize"] = 4; + this.setDirtyCanvas(true); + } + else if (widget.name == "normal_text") + { + this.properties["font"] = "Arial"; + this.setDirtyCanvas(true); + } + } + + WidgetText.prototype.onPropertyChange = function(name,value) + { + this.properties[name] = value; + this.str = typeof(value) == 'number' ? value.toFixed(3) : value; + //this.resize(); + return true; + } + + LiteGraph.registerNodeType("widget/text", WidgetText ); + + + function WidgetPanel() + { + this.size = [200,100]; + this.properties = {borderColor:"#ffffff",bgcolorTop:"#f0f0f0",bgcolorBottom:"#e0e0e0",shadowSize:2, borderRadius:3}; + } + + WidgetPanel.title = "Panel"; + WidgetPanel.desc = "Non interactive panel"; + WidgetPanel.widgets = [{name:"update",text:"Update",type:"button"}]; + + + WidgetPanel.prototype.createGradient = function(ctx) + { + if(this.properties["bgcolorTop"] == "" || this.properties["bgcolorBottom"] == "") + { + this.lineargradient = 0; + return; + } + + this.lineargradient = ctx.createLinearGradient(0,0,0,this.size[1]); + this.lineargradient.addColorStop(0,this.properties["bgcolorTop"]); + this.lineargradient.addColorStop(1,this.properties["bgcolorBottom"]); + } + + WidgetPanel.prototype.onDrawForeground = function(ctx) + { + if(this.lineargradient == null) + this.createGradient(ctx); + + if(!this.lineargradient) + return; + + ctx.lineWidth = 1; + ctx.strokeStyle = this.properties["borderColor"]; + //ctx.fillStyle = "#ebebeb"; + ctx.fillStyle = this.lineargradient; + + if(this.properties["shadowSize"]) + { + ctx.shadowColor = "#000"; + ctx.shadowOffsetX = 0; + ctx.shadowOffsetY = 0; + ctx.shadowBlur = this.properties["shadowSize"]; + } + else + ctx.shadowColor = "transparent"; + + ctx.roundRect(0,0,this.size[0]-1,this.size[1]-1,this.properties["shadowSize"]); + ctx.fill(); + ctx.shadowColor = "transparent"; + ctx.stroke(); + } + + WidgetPanel.prototype.onWidget = function(e,widget) + { + if(widget.name == "update") + { + this.lineargradient = null; + this.setDirtyCanvas(true); + } + } + + LiteGraph.registerNodeType("widget/panel", WidgetPanel ); + +})(); \ No newline at end of file diff --git a/src/nodes/math.js b/src/nodes/math.js new file mode 100644 index 000000000..9a871df8e --- /dev/null +++ b/src/nodes/math.js @@ -0,0 +1,472 @@ +(function(){ + + +function MathRand() +{ + this.addOutput("value","number"); + this.properties = { min:0, max:1 }; + this.size = [60,20]; +} + +MathRand.title = "Rand"; +MathRand.desc = "Random number"; + +MathRand.prototype.onExecute = function() +{ + var min = this.properties.min; + var max = this.properties.max; + this._last_v = Math.random() * (max-min) + min; + this.setOutputData(0, this._last_v ); +} + +MathRand.prototype.onDrawBackground = function(ctx) +{ + //show the current value + if(this._last_v) + this.outputs[0].label = this._last_v.toFixed(3); + else + this.outputs[0].label = "?"; +} + +LiteGraph.registerNodeType("math/rand", MathRand); + +//Math clamp +function MathClamp() +{ + this.addInput("in","number"); + this.addOutput("out","number"); + this.size = [60,20]; + this.properties = {min:0, max:1}; +} + +MathClamp.title = "Clamp"; +MathClamp.desc = "Clamp number between min and max"; + +MathClamp.prototype.onExecute = function() +{ + var v = this.getInputData(0); + if(v == null) return; + v = Math.max(this.properties.min,v); + v = Math.min(this.properties.max,v); + this.setOutputData(0, v ); +} + +LiteGraph.registerNodeType("math/clamp", MathClamp ); + + +//Math ABS +function MathAbs() +{ + this.addInput("in","number"); + this.addOutput("out","number"); + this.size = [60,20]; +} + +MathAbs.title = "Abs"; +MathAbs.desc = "Absolute"; + +MathAbs.prototype.onExecute = function() +{ + var v = this.getInputData(0); + if(v == null) return; + this.setOutputData(0, Math.abs(v) ); +} + +LiteGraph.registerNodeType("math/abs", MathAbs); + + +//Math Floor +function MathFloor() +{ + this.addInput("in","number"); + this.addOutput("out","number"); + this.size = [60,20]; +} + +MathFloor.title = "Floor"; +MathFloor.desc = "Floor number to remove fractional part"; + +MathFloor.prototype.onExecute = function() +{ + var v = this.getInputData(0); + if(v == null) return; + this.setOutputData(0, v|1 ); +} + +LiteGraph.registerNodeType("math/floor", MathFloor ); + + +//Math frac +function MathFrac() +{ + this.addInput("in","number"); + this.addOutput("out","number"); + this.size = [60,20]; +} + +MathFrac.title = "Frac"; +MathFrac.desc = "Returns fractional part"; + +MathFrac.prototype.onExecute = function() +{ + var v = this.getInputData(0); + if(v == null) + return; + this.setOutputData(0, v%1 ); +} + +LiteGraph.registerNodeType("math/frac",MathFrac); + + +//Math scale +function MathScale() +{ + this.addInput("in","number",{label:""}); + this.addOutput("out","number",{label:""}); + this.size = [60,20]; + this.properties = {"factor":1}; +} + +MathScale.title = "Scale"; +MathScale.desc = "v * factor"; + +MathScale.prototype.onExecute = function() +{ + var value = this.getInputData(0); + if(value != null) + this.setOutputData(0, value * this.properties.factor ); +} + +LiteGraph.registerNodeType("math/scale", MathScale ); + + +//Math operation +function MathOperation() +{ + this.addInput("A","number"); + this.addInput("B","number"); + this.addOutput("A+B","number"); + this.size = [80,20]; + this.properties = {A:1.0, B:1.0}; +} + +MathOperation.title = "Operation"; +MathOperation.desc = "Easy math operators"; + +MathOperation.prototype.setValue = function(v) +{ + if( typeof(v) == "string") v = parseFloat(v); + this.properties["value"] = v; + this.setDirtyCanvas(true); +} + +MathOperation.prototype.onExecute = function() +{ + var A = this.getInputData(0); + var B = this.getInputData(1); + if(A!=null) + this.properties["A"] = A; + else + A = this.properties["A"]; + + if(B!=null) + this.properties["B"] = B; + else + B = this.properties["B"]; + + for(var i = 0, l = this.outputs.length; i < l; ++i) + { + var output = this.outputs[i]; + if(!output.links || !output.links.length) + continue; + var value = 0; + switch( output.name ) + { + case "A+B": value = A+B; break; + case "A-B": value = A-B; break; + case "A*B": value = A*B; break; + case "A/B": value = A/B; break; + } + this.setOutputData(i, value ); + } +} + +MathOperation.prototype.onGetOutputs = function() +{ + return [["A-B","number"],["A*B","number"],["A/B","number"]]; +} + +LiteGraph.registerNodeType("math/operation", MathOperation ); + + +//Math compare +function MathCompare() +{ + this.addInputs( "A","number" ); + this.addInputs( "B","number" ); + this.addOutputs("A==B","number"); + this.addOutputs("A!=B","number"); + this.properties = {A:0,B:0}; +} + + +MathCompare.title = "Compare"; +MathCompare.desc = "compares between two values"; + +MathCompare.prototype.onExecute = function() +{ + var A = this.getInputData(0); + var B = this.getInputData(1); + if(A!=null) + this.properties["A"] = A; + else + A = this.properties["A"]; + + if(B!=null) + this.properties["B"] = B; + else + B = this.properties["B"]; + + for(var i = 0, l = this.outputs.length; i < l; ++i) + { + var output = this.outputs[i]; + if(!output.links || !output.links.length) + continue; + switch( output.name ) + { + case "A==B": value = A==B; break; + case "A!=B": value = A!=B; break; + case "A>B": value = A>B; break; + case "A=B": value = A>=B; break; + } + this.setOutputData(i, value ); + } +}; + +MathCompare.prototype.onGetOutputs = function() +{ + return [["A==B","number"],["A!=B","number"],["A>B","number"],["A=B","number"],["A<=B","number"]]; +} + +LiteGraph.registerNodeType("math/compare",MathCompare); + +//Math Trigonometry +function MathTrigonometry() +{ + this.addInputs("v","number"); + this.addOutputs("sin","number"); + this.properties = {amplitude:1.0}; + this.size = [100,20]; + this.bgImageUrl = "nodes/imgs/icon-sin.png"; +} + +MathTrigonometry.title = "Trigonometry"; +MathTrigonometry.desc = "Sin Cos Tan"; + +MathTrigonometry.prototype.onExecute = function() +{ + var v = this.getInputData(0); + var amp = this.properties["amplitude"]; + for(var i = 0, l = this.outputs.length; i < l; ++i) + { + var output = this.outputs[i]; + switch( output.name ) + { + case "sin": value = Math.sin(v); break; + case "cos": value = Math.cos(v); break; + case "tan": value = Math.tan(v); break; + case "asin": value = Math.asin(v); break; + case "acos": value = Math.acos(v); break; + case "atan": value = Math.atan(v); break; + } + this.setOutputData(i, amp * value ); + } +} + +MathTrigonometry.prototype.onGetOutputs = function() +{ + return [["sin","number"],["cos","number"],["tan","number"],["asin","number"],["acos","number"],["atan","number"]]; +} + + +LiteGraph.registerNodeType("math/trigonometry", MathTrigonometry ); + + + +//math library for safe math operations without eval +if(window.math) +{ + function MathFormula() + { + this.addInputs("x","number"); + this.addInputs("y","number"); + this.addOutputs("","number"); + this.properties = {x:1.0, y:1.0, formula:"x+y"}; + } + + MathFormula.title = "Formula"; + MathFormula.desc = "Compute safe formula"; + + MathFormula.prototype.onExecute = function() + { + var x = this.getInputData(0); + var y = this.getInputData(1); + if(x != null) + this.properties["x"] = x; + else + x = this.properties["x"]; + + if(y!=null) + this.properties["y"] = y; + else + y = this.properties["y"]; + + var f = this.properties["formula"]; + var value = math.eval(f,{x:x,y:y,T: this.graph.globaltime }); + this.setOutputData(0, value ); + } + + MathFormula.prototype.onDrawBackground = function() + { + var f = this.properties["formula"]; + this.outputs[0].label = f; + } + + MathFormula.prototype.onGetOutputs = function() + { + return [["A-B","number"],["A*B","number"],["A/B","number"]]; + } + + LiteGraph.registerNodeType("math/formula", MathFormula ); +} + + +//if glMatrix is installed... +if(window.glMatrix) +{ + function Math3DVec3ToXYZ() + { + this.addInput("vec3","vec3"); + this.addOutput("x","number"); + this.addOutput("y","number"); + this.addOutput("z","number"); + } + + Math3DVec3ToXYZ.title = "Vec3->XYZ"; + Math3DVec3ToXYZ.desc = "vector 3 to components"; + + Math3DVec3ToXYZ.prototype.onExecute = function() + { + var v = this.getInputData(0); + if(v == null) return; + + this.setOutputData( 0, v[0] ); + this.setOutputData( 1, v[1] ); + this.setOutputData( 2, v[2] ); + } + + LiteGraph.registerNodeType("math3d/vec3-to-xyz", Math3DVec3ToXYZ ); + + + function Math3DXYZToVec3() + { + this.addInputs([["x","number"],["y","number"],["z","number"]]); + this.addOutput("vec3","vec3"); + } + + Math3DXYZToVec3.title = "XYZ->Vec3"; + Math3DXYZToVec3.desc = "components to vector3"; + + Math3DXYZToVec3.prototype.onExecute = function() + { + var x = this.getInputData(0); + if(x == null) x = 0; + var y = this.getInputData(1); + if(y == null) y = 0; + var z = this.getInputData(2); + if(z == null) z = 0; + + this.setOutputData( 0, vec3.fromValues(x,y,z) ); + } + + LiteGraph.registerNodeType("math3d/xyz-to-vec3", Math3DXYZToVec3 ); + + + function Math3DRotation() + { + this.addInputs([["degrees","number"],["axis","vec3"]]); + this.addOutput("quat","quat"); + this.properties = { angle:90.0, axis: vec3.fromValues(0,1,0) }; + } + + Math3DRotation.title = "Rotation"; + Math3DRotation.desc = "quaternion rotation"; + + Math3DRotation.prototype.onExecute = function() + { + var angle = this.getInputData(0); + if(angle == null) angle = this.properties.angle; + var axis = this.getInputData(1); + if(axis == null) axis = this.properties.axis; + + var R = quat.setAxisAngle(quat.create(), axis, angle * 0.0174532925 ); + this.setOutputData( 0, R ); + } + + + LiteGraph.registerNodeType("math3d/rotation", Math3DRotation ); + + + //Math3D rotate vec3 + function Math3DRotateVec3() + { + this.addInputs([["vec3","vec3"],["quat","quat"]]); + this.addOutput("result","vec3"); + this.properties = { vec: [0,0,1] }; + } + + Math3DRotateVec3.title = "Rot. Vec3"; + Math3DRotateVec3.desc = "rotate a point"; + + Math3DRotateVec3.prototype.onExecute = function() + { + var vec = this.getInputData(0); + if(vec == null) vec = this.properties.vec; + var quat = this.getInputData(1); + if(quat == null) + this.setOutputData(vec); + else + this.setOutputData( 0, vec3.transformQuat( vec3.create(), vec, quat ) ); + } + + LiteGraph.registerNodeType("math3d/rotate_vec3", Math3DRotateVec3); + + + + function Math3DMultQuat() + { + this.addInputs( [["A","quat"],["B","quat"]] ); + this.addOutput( "A*B","quat" ); + } + + Math3DMultQuat.title = "Mult. Quat"; + Math3DMultQuat.desc = "rotate quaternion"; + + Math3DMultQuat.prototype.onExecute = function() + { + var A = this.getInputData(0); + if(A == null) return; + var B = this.getInputData(1); + if(B == null) return; + + var R = quat.multiply(quat.create(), A,B); + this.setOutputData( 0, R ); + } + + LiteGraph.registerNodeType("math3d/mult-quat", Math3DMultQuat ); + +} //glMatrix + +})(); \ No newline at end of file diff --git a/src/nodes/others.js b/src/nodes/others.js new file mode 100644 index 000000000..372e1d704 --- /dev/null +++ b/src/nodes/others.js @@ -0,0 +1,118 @@ +/* +LiteGraph.registerNodeType("graphics/supergraph", { + title: "Supergraph", + desc: "Shows a nice circular graph", + + inputs: [["x","number"],["y","number"],["c","color"]], + outputs: [["","image"]], + widgets: [{name:"clear_alpha",text:"Clear Alpha",type:"minibutton"},{name:"clear_color",text:"Clear color",type:"minibutton"}], + properties: {size:256,bgcolor:"#000",lineWidth:1}, + bgcolor: "#000", + flags: {allow_fastrender:true}, + onLoad: function() + { + this.createCanvas(); + }, + + createCanvas: function() + { + this.canvas = document.createElement("canvas"); + this.canvas.width = this.properties["size"]; + this.canvas.height = this.properties["size"]; + this.oldpos = null; + this.clearCanvas(true); + }, + + onExecute: function() + { + var x = this.getInputData(0); + var y = this.getInputData(1); + var c = this.getInputData(2); + + if(x == null && y == null) return; + + if(!x) x = 0; + if(!y) y = 0; + x*= 0.95; + y*= 0.95; + + var size = this.properties["size"]; + if(size != this.canvas.width || size != this.canvas.height) + this.createCanvas(); + + if (!this.oldpos) + { + this.oldpos = [ (x * 0.5 + 0.5) * size, (y*0.5 + 0.5) * size]; + return; + } + + var ctx = this.canvas.getContext("2d"); + + if(c == null) + c = "rgba(255,255,255,0.5)"; + else if(typeof(c) == "object") //array + c = colorToString(c); + + //stroke line + ctx.strokeStyle = c; + ctx.beginPath(); + ctx.moveTo( this.oldpos[0], this.oldpos[1] ); + this.oldpos = [ (x * 0.5 + 0.5) * size, (y*0.5 + 0.5) * size]; + ctx.lineTo( this.oldpos[0], this.oldpos[1] ); + ctx.stroke(); + + this.canvas.dirty = true; + this.setOutputData(0,this.canvas); + }, + + clearCanvas: function(alpha) + { + var ctx = this.canvas.getContext("2d"); + if(alpha) + { + ctx.clearRect(0,0,this.canvas.width,this.canvas.height); + this.trace("Clearing alpha"); + } + else + { + ctx.fillStyle = this.properties["bgcolor"]; + ctx.fillRect(0,0,this.canvas.width,this.canvas.height); + } + }, + + onWidget: function(e,widget) + { + if(widget.name == "clear_color") + { + this.clearCanvas(false); + } + else if(widget.name == "clear_alpha") + { + this.clearCanvas(true); + } + }, + + onPropertyChange: function(name,value) + { + if(name == "size") + { + this.properties["size"] = parseInt(value); + this.createCanvas(); + } + else if(name == "bgcolor") + { + this.properties["bgcolor"] = value; + this.createCanvas(); + } + else if(name == "lineWidth") + { + this.properties["lineWidth"] = parseInt(value); + this.canvas.getContext("2d").lineWidth = this.properties["lineWidth"]; + } + else + return false; + + return true; + } + }); +*/ \ No newline at end of file diff --git a/utils/deploy_files.txt b/utils/deploy_files.txt index dc1bf6c43..93aa7d2a4 100644 --- a/utils/deploy_files.txt +++ b/utils/deploy_files.txt @@ -1,4 +1,5 @@ ../src/litegraph.js -../src/nodes/basicnodes.js -../src/nodes/uinodes.js -../src/nodes/imagenodes.js \ No newline at end of file +../src/nodes/base.js +../src/nodes/interface.js +../src/nodes/math.js +../src/nodes/image.js \ No newline at end of file