diff --git a/.eslintignore b/.eslintignore index 796b96d1c..7b24edc49 100755 --- a/.eslintignore +++ b/.eslintignore @@ -1 +1,5 @@ /build +/coverage +/csharp +/docs +/node_modules \ No newline at end of file diff --git a/.eslintrc.js b/.eslintrc.js deleted file mode 100755 index 81e689e02..000000000 --- a/.eslintrc.js +++ /dev/null @@ -1,56 +0,0 @@ -module.exports = { - "env": { - "browser": true, - "es2021": true, - "node": true, - "jest/globals": true - }, - "extends": "eslint:recommended", - "overrides": [ - ], - "parserOptions": { - "ecmaVersion": "latest", - "sourceType": "module" - }, - "plugins": ["jest"], - "globals": { - "gl": true, - "GL": true, - "LS": true, - "Uint8Array": true, - "Uint32Array": true, - "Float32Array": true, - "LGraphCanvas": true, - "LGraph": true, - "LGraphNode": true, - "LiteGraph": true, - "LGraphTexture": true, - "Mesh": true, - "Shader": true, - "enableWebGLCanvas": true, - "vec2": true, - "vec3": true, - "vec4": true, - "DEG2RAD": true, - "isPowerOfTwo": true, - "cloneCanvas": true, - "createCanvas": true, - "hex2num": true, - "colorToString": true, - "showElement": true, - "quat": true, - "AudioSynth": true, - "SillyClient": true - }, - "rules": { - "no-console": "off", - "no-empty": "warn", - "no-redeclare": "warn", - "no-inner-declarations": "warn", - "no-constant-condition": "warn", - "no-unused-vars": "warn", - "no-mixed-spaces-and-tabs": "warn", - "no-unreachable": "warn", - "curly": ["warn", "all"] - } -} diff --git a/.gitignore b/.gitignore index cade06665..5cd553182 100755 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ npm-debug.log temp/ temp/* coverage/ +coverage/* # Editors /.vscode/* diff --git a/.npmrc b/.npmrc deleted file mode 100755 index 9cf949503..000000000 --- a/.npmrc +++ /dev/null @@ -1 +0,0 @@ -package-lock=false \ No newline at end of file diff --git a/.prettierrc b/.prettierrc deleted file mode 100755 index f86253e22..000000000 --- a/.prettierrc +++ /dev/null @@ -1,5 +0,0 @@ -{ - "singleQuote": false, - "semi": true, - "tabWidth": 4 -} diff --git a/.vscode/extensions.json b/.vscode/extensions.json deleted file mode 100755 index 62271a5d0..000000000 --- a/.vscode/extensions.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - // See http://go.microsoft.com/fwlink/?LinkId=827846 to learn about workspace recommendations. - // Extension identifier format: ${publisher}.${name}. Example: vscode.csharp - - // List of extensions which should be recommended for users of this workspace. - "recommendations": ["esbenp.prettier-vscode", "dbaeumer.vscode-eslint"], - // List of extensions recommended by VS Code that should not be recommended for users of this workspace. - "unwantedRecommendations": [] -} diff --git a/CHANGES.md b/CHANGES.md new file mode 100644 index 000000000..7440ef253 --- /dev/null +++ b/CHANGES.md @@ -0,0 +1,58 @@ + +This fork takes the 2011-2014 code for LiteGraph and renews it. + +# For 0.8.x "The Middle Class": + +## Non-Breaking + +* Replacing/revising alot of old event handler code +* Cleaned up alot of loops and condition logic +* Fixed over 400 linting errors + +## Breaking + +* Replaced the IIFE with ES6 modules +* Replaced ES5 classes with ES6 ones +* Replaced LiteGraph.*class* with just *class* +* SubgraphOutput's location on screen is glitched + +# For 0.9.x "On Lint Bunnies": + +## Non-Breaking + +* Fixed multiscreen +* Fixed fullscreen close button +* Fixed low FPS handling +* Fixed dialog CSS mistake +* HttpRequestNode input is acknowledged +* Fix links sometimes not being correct when copy pasting nodes +* Added favicon +* Fixed SubgraphOutput location glitch +* ESLint down to 24 errors so far + +## Breaking + +* Removed LiteGraph.closeAllContextMenus in favor of ContextMenu.closeAll() +* Removed LiteGraph.pointerAddListener in favor of addEventListener() +* Removed LiteGraph.pointerRemoveListener in favor of removeEventListener() +* Removed some unused/blank methods +* Removed Mesh.compile in favor of Mesh.upload +* Removed LiteGraph.pointerevents_method +* All mouse events are now *pointer* events + +# For "ForwardCompatible" branch + +This is where the fun development happens, but isn't suitable for immediate pull to working +code. + +Integrated Atlasan's fork (admittedly imperfectly) and realized we need to separate a 1.x pathway +and a 2.x pathway. This one is loaded with features that will ultimately belong in 2.x + +# For "Reversion" branch + +* At present, I believe 0.11.x presents from Javi's repo except +blocking SillyClient.js from calling home. + +* This is an ES5/CommonJS upgrade from original that implements PRs to Javi's repo, +bugfixes, compatibility fixes, passes linting, passes audit, fixed example JSON so there's +no weird overflow happening, play/stop works, fullscreen works. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md deleted file mode 100755 index 45043616b..000000000 --- a/CONTRIBUTING.md +++ /dev/null @@ -1,9 +0,0 @@ -# Contribution Rules -There are some simple rules that everyone should follow: - -### Do not commit files from build folder -> I usually have horrible merge conflicts when I upload the build version that take me too much time to solve, but I want to keep the build version in the repo, so I guess it would be better if only one of us does the built, which would be me. -> https://github.com/jagenjo/litegraph.js/pull/155#issuecomment-656602861 -Those files will be updated by owner. - - diff --git a/LICENSE b/LICENSE index 1b52aa11c..2eb5015c4 100755 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,7 @@ +MIT License + Copyright (C) 2013 by Javi Agenjo +Copyright (C) 2024 by Daniel Lewis Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index 6ab541dd5..e606e7c62 100755 --- a/README.md +++ b/README.md @@ -1,10 +1,11 @@ + # litegraph.js A library in Javascript to create graphs in the browser similar to Unreal Blueprints. Nodes can be programmed easily and it includes an editor to construct and tests the graphs. It can be integrated easily in any existing web applications and graphs can be run without the need of the editor. -Try it in the [demo site](https://tamats.com/projects/litegraph/editor). +Try (original) in the [demo site](https://tamats.com/projects/litegraph/editor). ![Node Graph](imgs/node_graph_example.png "WebGLStudio") @@ -33,7 +34,15 @@ Although it is easy to create new node types, LiteGraph comes with some default You can install it using npm ``` -npm install litegraph.js +git clone https://github.com/daniel-lewis-ab/litegraph.js.git +cd litegraph.js +npm i +``` + +or + +``` +npm i @mr_pebble/litegraph ``` Or downloading the ```build/litegraph.js``` and ```css/litegraph.css``` version from this repository. @@ -44,7 +53,7 @@ Or downloading the ```build/litegraph.js``` and ```css/litegraph.css``` version - + @@ -120,9 +129,9 @@ LiteGraph.wrapFunctionAsNode("math/sum",sum, ["Number","Number"],"Number"); It also works server-side using NodeJS although some nodes do not work in server (audio, graphics, input, etc). ```js -var LiteGraph = require("./litegraph.js").LiteGraph; +import { LiteGraph, LGraph } from "./litegraph.js"; -var graph = new LiteGraph.LGraph(); +var graph = new LGraph(); var node_time = LiteGraph.createNode("basic/time"); graph.add(node_time); @@ -137,7 +146,7 @@ graph.start() ``` -## Projects using it +## Projects using original litegraph.js ### [comfyUI](https://github.com/comfyanonymous/ComfyUI) ![screenshot](https://github.com/comfyanonymous/ComfyUI/blob/6efe561c2a7321501b1b27f47039c7616dda1860/comfyui_screenshot.png) @@ -157,7 +166,19 @@ graph.start() ## Utils ----- -It includes several commands in the utils folder to generate doc, check errors and build minifyed version. +``` +These all work to some extent right now: +npm run docs +npm run prettifier +npm run build +npm run test +npm run lint +npm run server +``` + +I have also run: + +`npm audit` ## Demo @@ -166,19 +187,20 @@ The demo includes some examples of graphs. In order to try them you can visit [d ```sh $ git clone https://github.com/jagenjo/litegraph.js.git $ cd litegraph.js -$ npm install -$ node utils/server.js -Example app listening on port 80! +$ npm i +$ npm run server +Example app listening on port 8000! ``` Open your browser and point it to http://localhost:8000/. You can select a demo from the dropdown at the top of the page. ## Feedback -------- -You can write any feedback to javi.agenjo@gmail.com +You can write any feedback to daniel.lewis.ab@gmail.com ## Contributors +- Javi (Javengo) is original author - atlasan - kriffe - rappestad diff --git a/TODO.md b/TODO.md new file mode 100644 index 000000000..5b1bc87e7 --- /dev/null +++ b/TODO.md @@ -0,0 +1,45 @@ + +# Goals for 1.0 + +There are in fact already many other flow graph tools in the world, but they aren't compatible code to +existing and are often not anywhere close to the full feature set of litegraph. + +## Drop In Compatibility with OG + +litegraph 1.0 can only be achieved when we have API compatibility with OG litegraph. Effectively, while +it was viewed as a living codebase back when Javi had it, it has now been widely extended on and is +foundation code for some things. + +## Near-Feature Compatibility with OG + +Our goal for 1.0 is have a 'sufficiently' compatible feature set, while integrating 'bugfix' improvements +and any 'completion of feature' improvements as we imagine it. + +## Unit Testing and Debugging + +LiteGraph only had 12 basic tests for core. Our goal for the 1.x line is to expand the tests +to eventually have decent unit testing. These should test that functionalities still exist +rather than becoming binding to detail. + +## Documentation and Readability + +A primary goal is to complete Documentation and Testing, as well as +A secondary goal is to use modern JS features to make the code easier to read, smaller, and more streamlined internally. + +## What We Do Let Break for 1.0 + +* Anything that relies on arcane details within methods or something not existing etc. If you need exactly + LiteGraph 0.4.0 then that's available there. +* Deprecated 3rd Party endpoints like JQuery 1.6.2, or DOMMouseScroll or document.createElement('CustomEvent') +* Tooling has dramatically changed +* We have the intent to port 1.0 forward to ES6 +* We are planning to extend debug functionality in a way that ought to be non-breaking. +* We may backport tooltips into 1.0 + +# Plans for 2.0 + +* Conversion to ES2022 and TypeScript +* A full set of control logic nodes based on subgraph. +* A server query nodepack. +* Settings objects for easier configuration. +* CustomEvent generation for major events. diff --git a/build.py b/build.py new file mode 100644 index 000000000..a5b4ed98d --- /dev/null +++ b/build.py @@ -0,0 +1,159 @@ +import os +import subprocess +import json +import sys +import re + +# Define the lists of JS files to concatenate +js_files_lists = [ + { + "output_filename": "litegraph.core.js", + "pack_filename": "litegraph.core.pack.js", + "js_files": [ + "./src/litegraph.js", + ] + }, + { + "output_filename": "litegraph.mini.js", + "pack_filename": "litegraph.mini.pack.js", + "js_files": [ + "./src/litegraph.js", + + "./src/nodes/base.js", + "./src/nodes/events.js", + "./src/nodes/input.js", + "./src/nodes/math.js", + "./src/nodes/strings.js", + "./src/nodes/logic.js", + "./src/nodes/network.js", + ] + }, + { + "output_filename": "litegraph.full.js", + "pack_filename": "litegraph.full.pack.js", + "js_files": [ + "./src/litegraph.js", + + "./src/nodes/base.js", + "./src/nodes/events.js", + "./src/nodes/interface.js", + "./src/nodes/input.js", + "./src/nodes/math.js", + "./src/nodes/math3d.js", + "./src/nodes/strings.js", + "./src/nodes/logic.js", + "./src/nodes/graphics.js", + "./src/nodes/gltextures.js", + "./src/nodes/glshaders.js", + "./src/nodes/geometry.js", + "./src/nodes/glfx.js", + "./src/nodes/midi.js", + "./src/nodes/audio.js", + "./src/nodes/network.js", + ] + }, +] + +# Specify the build folder +build_folder = "build" +# Function to concatenate JS files +def concatenate_js_files(js_files, output_filename): + concatenated_data = "// readable version" + for js_file in js_files: + print("Processing " + js_file + " ", end="") + try: + with open(js_file, "r") as f: + concatenated_data += f.read() + "\n" + print("\033[92mOK\033[0m") + except FileNotFoundError: + print("\033[91mJS File not found\033[0m") + + # Write the concatenated data to the output file + with open(os.path.join(build_folder, output_filename), "w") as output_file: + output_file.write(concatenated_data) + print("Concatenated JS files saved to: " + output_filename) + +def pack_js_files(js_files, output_filename): + concatenated_data = "/*packed*/" + uglifyjs_path = os.path.join(".", "node_modules", ".bin", "uglifyjs" + (".cmd" if sys.platform.startswith('win') else "")) + for js_file in js_files: + print("Processing " + js_file + " ", end="") + + if os.path.exists(js_file): + # Minify the JS file using uglifyjs + minified_js = subprocess.run([uglifyjs_path, js_file, "-c"], stdout=subprocess.PIPE, text=True) + concatenated_data += minified_js.stdout + "\n" + print("\033[92mMinified\033[0m") + else: + print("\033[91mJS File not found\033[0m") + + # Write the concatenated data to the output file + with open(os.path.join(build_folder, output_filename), "w") as output_file: + output_file.write(concatenated_data) + print("Concatenated and minified JS files saved to: " + output_filename) + +def update_version(version, command=""): + if "--minor" in command: + version_parts = version.split('.') + version_parts[1] = str(int(version_parts[1]) + 1) + version_parts[2] = '0' + version = '.'.join(version_parts) + elif "--major" in command: + version_parts = version.split('.') + version_parts[0] = str(int(version_parts[0]) + 1) + version_parts[1] = '0' + version_parts[2] = '0' + version = '.'.join(version_parts) + else: + version_parts = version.split('.') + version_parts[2] = str(int(version_parts[2]) + 1) + version = '.'.join(version_parts) + + return version + +def update_version_in_files(version, files): + for file_path in files: + try: + with open(file_path, 'r') as file: + data = file.read() + data = re.sub(r'("version": )"\d+.\d+.\d+"', r'\1"{}"'.format(version), data) + data = re.sub(r'(this.VERSION = )"\d+.\d+.\d+";', r'\1"{}";'.format(version), data) + with open(file_path, 'w') as file: + file.write(data) + print("Version updated in file: " + file_path) + except FileNotFoundError: + print("File not found: " + file_path) + +def get_version_from_package_json(): + try: + with open('package.json', 'r') as file: + data = json.load(file) + return data.get('version') + except (FileNotFoundError, json.JSONDecodeError): + sys.exit() + +def get_new_version(): + # Specify the version number and files to update + version_number = get_version_from_package_json() + + if "--minor" in sys.argv: + version_number = update_version(version_number, "--minor") + elif "--major" in sys.argv: + version_number = update_version(version_number, "--major") + else: + version_number = update_version(version_number) + return version_number + +# Create build folder if it does not exist +if not os.path.exists(build_folder): + os.makedirs(build_folder) + +# Concatenate JS files from each list and save to the respective output filenames +for js_files_list in js_files_lists: + concatenate_js_files(js_files_list["js_files"], js_files_list["output_filename"]) + pack_js_files(js_files_list["js_files"], js_files_list["pack_filename"]) + +files_to_update = ["src/litegraph.js", "package.json"] + +# Update the version number in the specified files +update_version_in_files(get_new_version(), files_to_update) \ No newline at end of file diff --git a/build/litegraph.core.js b/build/litegraph.core.js index d8bbe6b84..08f4e3735 100644 --- a/build/litegraph.core.js +++ b/build/litegraph.core.js @@ -1,6 +1,4 @@ -//packer version - - +// readable version (function(global) { // ************************************************************* // LiteGraph CLASS ******* @@ -48,11 +46,11 @@ EVENT_LINK_COLOR: "#A86", CONNECTING_LINK_COLOR: "#AFA", - MAX_NUMBER_OF_NODES: 1000, //avoid infinite loops - DEFAULT_POSITION: [100, 100], //default node position - VALID_SHAPES: ["default", "box", "round", "card"], //,"circle" + MAX_NUMBER_OF_NODES: 1000, // avoid infinite loops + DEFAULT_POSITION: [100, 100], // default node position + VALID_SHAPES: ["default", "box", "round", "card"], // ,"circle" - //shapes are used for nodes but also for slots + // shapes are used for nodes but also for slots BOX_SHAPE: 1, ROUND_SHAPE: 2, CIRCLE_SHAPE: 3, @@ -60,15 +58,15 @@ ARROW_SHAPE: 5, GRID_SHAPE: 6, // intended for slot arrays - //enums + // enums INPUT: 1, OUTPUT: 2, - EVENT: -1, //for outputs - ACTION: -1, //for inputs + EVENT: -1, // for outputs + ACTION: -1, // for inputs NODE_MODES: ["Always", "On Event", "Never", "On Trigger"], // helper, will add "On Request" and more in the future - NODE_MODES_COLORS:["#666","#422","#333","#224","#626"], // use with node_box_coloured_by_mode + NODE_MODES_COLORS: ["#666","#422","#333","#224","#626"], // use with node_box_coloured_by_mode ALWAYS: 0, ON_EVENT: 1, NEVER: 2, @@ -91,59 +89,59 @@ AUTOHIDE_TITLE: 3, VERTICAL_LAYOUT: "vertical", // arrange nodes vertically - proxy: null, //used to redirect calls + proxy: null, // used to redirect calls node_images_path: "", debug: false, catch_exceptions: true, throw_errors: true, - allow_scripts: false, //if set to true some nodes like Formula would be allowed to evaluate code that comes from unsafe sources (like node configuration), which could lead to exploits - use_deferred_actions: true, //executes actions during the graph execution flow - registered_node_types: {}, //nodetypes by string - node_types_by_file_extension: {}, //used for dropping files in the canvas - Nodes: {}, //node types by classname - Globals: {}, //used to store vars between graphs - - searchbox_extras: {}, //used to add extra features to the search box + allow_scripts: false, // if set to true some nodes like Formula would be allowed to evaluate code that comes from unsafe sources (like node configuration), which could lead to exploits + use_deferred_actions: true, // executes actions during the graph execution flow + registered_node_types: {}, // nodetypes by string + node_types_by_file_extension: {}, // used for dropping files in the canvas + Nodes: {}, // node types by classname + Globals: {}, // used to store vars between graphs + + searchbox_extras: {}, // used to add extra features to the search box auto_sort_node_types: false, // [true!] If set to true, will automatically sort node types / categories in the context menus - - node_box_coloured_when_on: false, // [true!] this make the nodes box (top left circle) coloured when triggered (execute/action), visual feedback + + node_box_coloured_when_on: false, // [true!] this make the nodes box (top left circle) coloured when triggered (execute/action), visual feedback node_box_coloured_by_mode: false, // [true!] nodebox based on node mode, visual feedback - + dialog_close_on_mouse_leave: true, // [false on mobile] better true if not touch device, TODO add an helper/listener to close if false dialog_close_on_mouse_leave_delay: 500, - + shift_click_do_break_link_from: false, // [false!] prefer false if results too easy to break links - implement with ALT or TODO custom keys click_do_break_link_to: false, // [false!]prefer false, way too easy to break links - + search_hide_on_mouse_leave: true, // [false on mobile] better true if not touch device, TODO add an helper/listener to close if false search_filter_enabled: false, // [true!] enable filtering slots type in the search widget, !requires auto_load_slot_types or manual set registered_slot_[in/out]_types and slot_types_[in/out] search_show_all_on_open: true, // [true!] opens the results list when opening the search widget - + auto_load_slot_types: false, // [if want false, use true, run, get vars values to be statically set, than disable] nodes types and nodeclass association with node types need to be calculated, if dont want this, calculate once and set registered_slot_[in/out]_types and slot_types_[in/out] - - // set these values if not using auto_load_slot_types + + // set these values if not using auto_load_slot_types registered_slot_in_types: {}, // slot types for nodeclass registered_slot_out_types: {}, // slot types for nodeclass slot_types_in: [], // slot types IN slot_types_out: [], // slot types OUT slot_types_default_in: [], // specify for each IN slot type a(/many) default node(s), use single string, array, or object (with node, title, parameters, ..) like for search - slot_types_default_out: [], // specify for each OUT slot type a(/many) default node(s), use single string, array, or object (with node, title, parameters, ..) like for search - - alt_drag_do_clone_nodes: false, // [true!] very handy, ALT click to clone and drag the new node - - do_add_triggers_slots: false, // [true!] will create and connect event slots when using action/events connections, !WILL CHANGE node mode when using onTrigger (enable mode colors), onExecuted does not need this - - allow_multi_output_for_events: true, // [false!] being events, it is strongly reccomended to use them sequentially, one by one - - middle_click_slot_add_default_node: false, //[true!] allows to create and connect a ndoe clicking with the third button (wheel) - - release_link_on_empty_shows_menu: false, //[true!] dragging a link to empty space will open a menu, add from list, search or defaults - + slot_types_default_out: [], // specify for each OUT slot type a(/many) default node(s), use single string, array, or object (with node, title, parameters, ..) like for search + + alt_drag_do_clone_nodes: false, // [true!] very handy, ALT click to clone and drag the new node + + do_add_triggers_slots: false, // [true!] will create and connect event slots when using action/events connections, !WILL CHANGE node mode when using onTrigger (enable mode colors), onExecuted does not need this + + allow_multi_output_for_events: true, // [false!] being events, it is strongly reccomended to use them sequentially, one by one + + middle_click_slot_add_default_node: false, // [true!] allows to create and connect a ndoe clicking with the third button (wheel) + + release_link_on_empty_shows_menu: false, // [true!] dragging a link to empty space will open a menu, add from list, search or defaults + pointerevents_method: "mouse", // "mouse"|"pointer" use mouse for retrocompatibility issues? (none found @ now) // TODO implement pointercancel, gotpointercapture, lostpointercapture, (pointerover, pointerout if necessary) - ctrl_shift_v_paste_connect_unselected_outputs: false, //[true!] allows ctrl + shift + v to paste nodes with the outputs of the unselected nodes connected with the inputs of the newly pasted nodes + ctrl_shift_v_paste_connect_unselected_outputs: false, // [true!] allows ctrl + shift + v to paste nodes with the outputs of the unselected nodes connected with the inputs of the newly pasted nodes // if true, all newly created nodes/links will use string UUIDs for their id fields instead of integers. // use this if you must have node IDs that are unique across all graphs and subgraphs. @@ -175,7 +173,7 @@ base_class.title = classname; } - //extend class + // extend class for (var i in LGraphNode.prototype) { if (!base_class.prototype[i]) { base_class.prototype[i] = LGraphNode.prototype[i]; @@ -213,16 +211,16 @@ return this._shape; }, enumerable: true, - configurable: true + configurable: true, }); - - //used to know which nodes to create when dragging files to the canvas + + // used to know which nodes to create when dragging files to the canvas if (base_class.supported_extensions) { for (let i in base_class.supported_extensions) { const ext = base_class.supported_extensions[i]; if(ext && ext.constructor === String) { - this.node_types_by_file_extension[ ext.toLowerCase() ] = base_class; + this.node_types_by_file_extension[ext.toLowerCase()] = base_class; } } } @@ -239,15 +237,13 @@ LiteGraph.onNodeTypeReplaced(type, base_class, prev); } - //warnings + // warnings if (base_class.prototype.onPropertyChange) { - console.warn( - "LiteGraph node class " + + console.warn("LiteGraph node class " + type + - " has onPropertyChange method, it must be called onPropertyChanged with d at the end" - ); + " has onPropertyChange method, it must be called onPropertyChanged with d at the end"); } - + // TODO one would want to know input and ouput :: this would allow through registerNodeAndSlotType to get all the slots types if (this.auto_load_slot_types) { new base_class(base_class.title || "tmpnode"); @@ -279,7 +275,7 @@ * @param {String|Object} type name of the node or the node constructor itself * @param {String} slot_type name of the slot type (variable type), eg. string, number, array, boolean, .. */ - registerNodeAndSlotType: function(type, slot_type, out){ + registerNodeAndSlotType: function(type, slot_type, out) { out = out || false; const base_class = type.constructor === String && @@ -327,7 +323,7 @@ } } }, - + /** * Create a new nodetype by passing an object with some properties * like onCreate, inputs:Array, outputs:Array, properties, onExecute @@ -335,37 +331,34 @@ * @param {String} name node name with namespace (p.e.: 'math/sum') * @param {Object} object methods expected onCreate, inputs, outputs, properties, onExecute */ - buildNodeClassFromObject: function( + buildNodeClassFromObject: function( name, - object + object, ) { var ctor_code = ""; if(object.inputs) - for(var i=0; i < object.inputs.length; ++i) - { - var _name = object.inputs[i][0]; - var _type = object.inputs[i][1]; - if(_type && _type.constructor === String) - _type = '"'+_type+'"'; - ctor_code += "this.addInput('"+_name+"',"+_type+");\n"; - } + for(var i=0; i < object.inputs.length; ++i) { + var _name = object.inputs[i][0]; + var _type = object.inputs[i][1]; + if(_type && _type.constructor === String) + _type = '"'+_type+'"'; + ctor_code += "this.addInput('"+_name+"',"+_type+");\n"; + } if(object.outputs) - for(var i=0; i < object.outputs.length; ++i) - { - var _name = object.outputs[i][0]; - var _type = object.outputs[i][1]; - if(_type && _type.constructor === String) - _type = '"'+_type+'"'; - ctor_code += "this.addOutput('"+_name+"',"+_type+");\n"; - } + for(var i=0; i < object.outputs.length; ++i) { + var _name = object.outputs[i][0]; + var _type = object.outputs[i][1]; + if(_type && _type.constructor === String) + _type = '"'+_type+'"'; + ctor_code += "this.addOutput('"+_name+"',"+_type+");\n"; + } if(object.properties) - for(var i in object.properties) - { - var prop = object.properties[i]; - if(prop && prop.constructor === String) - prop = '"'+prop+'"'; - ctor_code += "this.addProperty('"+i+"',"+prop+");\n"; - } + for(var i in object.properties) { + var prop = object.properties[i]; + if(prop && prop.constructor === String) + prop = '"'+prop+'"'; + ctor_code += "this.addProperty('"+i+"',"+prop+");\n"; + } ctor_code += "if(this.onCreate)this.onCreate()"; var classobj = Function(ctor_code); for(var i in object) @@ -376,7 +369,7 @@ this.registerNodeType(name, classobj); return classobj; }, - + /** * Create a new nodetype by passing a function, it wraps it with a proper class and generates inputs according to the parameters of the function. * Useful to wrap simple methods that do not require properties, and that only process some input to generate an output. @@ -392,23 +385,21 @@ func, param_types, return_type, - properties + properties, ) { var params = Array(func.length); var code = ""; - if(param_types !== null) //null means no inputs - { + if(param_types !== null) { // null means no inputs var names = LiteGraph.getParameterNames(func); for (var i = 0; i < names.length; ++i) { var type = 0; - if(param_types) - { - //type = param_types[i] != null ? "'" + param_types[i] + "'" : "0"; + if(param_types) { + // type = param_types[i] != null ? "'" + param_types[i] + "'" : "0"; if( param_types[i] != null && param_types[i].constructor === String ) type = "'" + param_types[i] + "'" ; else if( param_types[i] != null ) type = param_types[i]; - } + } code += "this.addInput('" + names[i] + @@ -417,8 +408,8 @@ ");\n"; } } - if(return_type !== null) //null means no output - code += + if(return_type !== null) // null means no output + code += "this.addOutput('out'," + (return_type != null ? (return_type.constructor === String ? "'" + return_type + "'" : return_type) : 0) + ");\n"; @@ -462,7 +453,7 @@ var type = this.registered_node_types[i]; if (type.prototype[name]) { type.prototype["_" + name] = type.prototype[name]; - } //keep old in case of replacing + } // keep old in case of replacing type.prototype[name] = func; } }, @@ -479,9 +470,7 @@ var base_class = this.registered_node_types[type]; if (!base_class) { if (LiteGraph.debug) { - console.log( - 'GraphNode type "' + type + '" not registered.' - ); + console.log('GraphNode type "' + type + '" not registered.'); } return null; } @@ -519,7 +508,7 @@ } if (!node.size) { node.size = node.computeSize(); - //call onresize? + // call onresize? } if (!node.pos) { node.pos = LiteGraph.DEFAULT_POSITION.concat(); @@ -528,18 +517,18 @@ node.mode = LiteGraph.ALWAYS; } - //extra options + // extra options if (options) { for (var i in options) { node[i] = options[i]; } } - // callback + // callback if ( node.onNodeCreated ) { node.onNodeCreated(); } - + return node; }, @@ -578,7 +567,9 @@ } if (this.auto_sort_node_types) { - r.sort(function(a,b){return a.title.localeCompare(b.title)}); + r.sort(function(a,b) { + return a.title.localeCompare(b.title) + }); } return r; @@ -593,11 +584,10 @@ getNodeTypesCategories: function( filter ) { var categories = { "": 1 }; for (var i in this.registered_node_types) { - var type = this.registered_node_types[i]; - if ( type.category && !type.skip_list ) - { - if(type.filter != filter) - continue; + var type = this.registered_node_types[i]; + if ( type.category && !type.skip_list ) { + if(type.filter != filter) + continue; categories[type.category] = 1; } } @@ -608,10 +598,10 @@ return this.auto_sort_node_types ? result.sort() : result; }, - //debug purposes: reloads all the js scripts that matches a wildcard + // debug purposes: reloads all the js scripts that matches a wildcard reloadNodes: function(folder_wildcard) { var tmp = document.getElementsByTagName("script"); - //weird, this array changes by its own, so we use a copy + // weird, this array changes by its own, so we use a copy var script_files = []; for (var i=0; i < tmp.length; i++) { script_files.push(tmp[i]); @@ -653,7 +643,7 @@ } }, - //separated just to improve if it doesn't work + // separated just to improve if it doesn't work cloneObject: function(obj, target) { if (obj == null) { return null; @@ -673,7 +663,7 @@ * https://gist.github.com/jed/982883?permalink_comment_id=852670#gistcomment-852670 */ uuidv4: function() { - return ([1e7]+-1e3+-4e3+-8e3+-1e11).replace(/[018]/g,a=>(a^Math.random()*16>>a/4).toString(16)); + return ([1e7]+-1e3+-4e3+-8e3+-1e11).replace(/[018]/g,(a) => (a^Math.random()*16>>a/4).toString(16)); }, /** @@ -684,12 +674,12 @@ * @return {Boolean} true if they can be connected */ isValidConnection: function(type_a, type_b) { - if (type_a=="" || type_a==="*") type_a = 0; - if (type_b=="" || type_b==="*") type_b = 0; + if (type_a=="" || type_a==="*") type_a = 0; + if (type_b=="" || type_b==="*") type_b = 0; if ( - !type_a //generic output + !type_a // generic output || !type_b // generic input - || type_a == type_b //same type (is valid for triggers) + || type_a == type_b // same type (is valid for triggers) || (type_a == LiteGraph.EVENT && type_b == LiteGraph.ACTION) ) { return true; @@ -711,8 +701,8 @@ var supported_types_b = type_b.split(","); for (var i = 0; i < supported_types_a.length; ++i) { for (var j = 0; j < supported_types_b.length; ++j) { - if(this.isValidConnection(supported_types_a[i],supported_types_b[j])){ - //if (supported_types_a[i] == supported_types_b[j]) { + if(this.isValidConnection(supported_types_a[i],supported_types_b[j])) { + // if (supported_types_a[i] == supported_types_b[j]) { return true; } } @@ -733,7 +723,7 @@ this.searchbox_extras[description.toLowerCase()] = { type: node_type, desc: description, - data: data + data: data, }; }, @@ -744,65 +734,61 @@ * @param {String} type an string to know how to fetch it: "text","arraybuffer","json","blob" * @param {Function} on_complete callback(data) * @param {Function} on_error in case of an error - * @return {FileReader|Promise} returns the object used to + * @return {FileReader|Promise} returns the object used to */ - fetchFile: function( url, type, on_complete, on_error ) { - var that = this; - if(!url) - return null; + fetchFile: function( url, type, on_complete, on_error ) { + var that = this; + if(!url) + return null; - type = type || "text"; - if( url.constructor === String ) - { - if (url.substr(0, 4) == "http" && LiteGraph.proxy) { - url = LiteGraph.proxy + url.substr(url.indexOf(":") + 3); - } - return fetch(url) - .then(function(response) { - if(!response.ok) - throw new Error("File not found"); //it will be catch below - if(type == "arraybuffer") - return response.arrayBuffer(); - else if(type == "text" || type == "string") - return response.text(); - else if(type == "json") - return response.json(); - else if(type == "blob") - return response.blob(); - }) - .then(function(data) { - if(on_complete) - on_complete(data); - }) - .catch(function(error) { - console.error("error fetching file:",url); - if(on_error) - on_error(error); - }); - } - else if( url.constructor === File || url.constructor === Blob) - { - var reader = new FileReader(); - reader.onload = function(e) - { - var v = e.target.result; - if( type == "json" ) - v = JSON.parse(v); - if(on_complete) - on_complete(v); - } - if(type == "arraybuffer") - return reader.readAsArrayBuffer(url); - else if(type == "text" || type == "json") - return reader.readAsText(url); - else if(type == "blob") - return reader.readAsBinaryString(url); - } - return null; - } + type = type || "text"; + if( url.constructor === String ) { + if (url.substr(0, 4) == "http" && LiteGraph.proxy) { + url = LiteGraph.proxy + url.substr(url.indexOf(":") + 3); + } + return fetch(url) + .then(function(response) { + if(!response.ok) + throw new Error("File not found"); // it will be catch below + if(type == "arraybuffer") + return response.arrayBuffer(); + else if(type == "text" || type == "string") + return response.text(); + else if(type == "json") + return response.json(); + else if(type == "blob") + return response.blob(); + }) + .then(function(data) { + if(on_complete) + on_complete(data); + }) + .catch(function(error) { + console.error("error fetching file:",url); + if(on_error) + on_error(error); + }); + } else if( url.constructor === File || url.constructor === Blob) { + var reader = new FileReader(); + reader.onload = function(e) { + var v = e.target.result; + if( type == "json" ) + v = JSON.parse(v); + if(on_complete) + on_complete(v); + } + if(type == "arraybuffer") + return reader.readAsArrayBuffer(url); + else if(type == "text" || type == "json") + return reader.readAsText(url); + else if(type == "blob") + return reader.readAsBinaryString(url); + } + return null; + }, }); - //timer that works everywhere + // timer that works everywhere if (typeof performance != "undefined") { LiteGraph.getTime = performance.now.bind(performance); } else if (typeof Date != "undefined" && Date.now) { @@ -818,9 +804,9 @@ }; } - //********************************************************************************* + //* ******************************************************************************** // LGraph CLASS - //********************************************************************************* + //* ******************************************************************************** /** * LGraph is the class that contain a full graph. We instantiate one and add nodes to it, and then we can run the execution loop. @@ -848,10 +834,10 @@ global.LGraph = LiteGraph.LGraph = LGraph; - //default supported types + // 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) + // 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; }; @@ -871,9 +857,9 @@ this.last_node_id = 0; this.last_link_id = 0; - this._version = -1; //used to detect changes + this._version = -1; // used to detect changes - //safe clear + // safe clear if (this._nodes) { for (var i = 0; i < this._nodes.length; ++i) { var node = this._nodes[i]; @@ -883,27 +869,27 @@ } } - //nodes + // nodes this._nodes = []; this._nodes_by_id = {}; - this._nodes_in_order = []; //nodes sorted in execution order - this._nodes_executable = null; //nodes that contain onExecute sorted in execution order + this._nodes_in_order = []; // nodes sorted in execution order + this._nodes_executable = null; // nodes that contain onExecute sorted in execution order - //other scene stuff + // other scene stuff this._groups = []; - //links - this.links = {}; //container with all the links + // links + this.links = {}; // container with all the links - //iterations + // iterations this.iteration = 0; - //custom data + // custom data this.config = {}; - this.vars = {}; - this.extra = {}; //to store custom data + this.vars = {}; + this.extra = {}; // to store custom data - //timing + // timing this.globaltime = 0; this.runningtime = 0; this.fixedtime = 0; @@ -917,12 +903,12 @@ this.nodes_executing = []; this.nodes_actioning = []; this.nodes_executedAction = []; - - //subgraph_data + + // subgraph_data this.inputs = {}; this.outputs = {}; - //notify canvas to redraw + // notify canvas to redraw this.change(); this.sendActionToCanvas("clear"); @@ -986,35 +972,35 @@ this.sendEventToAllNodes("onStart"); - //launch + // launch this.starttime = LiteGraph.getTime(); this.last_update_time = this.starttime; interval = interval || 0; var that = this; - //execute once per frame + // execute once per frame if ( interval == 0 && typeof window != "undefined" && window.requestAnimationFrame ) { function on_frame() { if (that.execution_timer_id != -1) { return; } window.requestAnimationFrame(on_frame); - if(that.onBeforeStep) - that.onBeforeStep(); + if(that.onBeforeStep) + that.onBeforeStep(); that.runStep(1, !that.catch_errors); - if(that.onAfterStep) - that.onAfterStep(); + if(that.onAfterStep) + that.onAfterStep(); } this.execution_timer_id = -1; on_frame(); - } else { //execute every 'interval' ms + } else { // execute every 'interval' ms this.execution_timer_id = setInterval(function() { - //execute - if(that.onBeforeStep) - that.onBeforeStep(); + // execute + if(that.onBeforeStep) + that.onBeforeStep(); that.runStep(1, !that.catch_errors); - if(that.onAfterStep) - that.onAfterStep(); + if(that.onAfterStep) + that.onAfterStep(); }, interval); } }; @@ -1049,7 +1035,7 @@ * Run N steps (cycles) of the graph * @method runStep * @param {number} num number of steps to run, default is 1 - * @param {Boolean} do_not_catch_errors [optional] if you want to try/catch errors + * @param {Boolean} do_not_catch_errors [optional] if you want to try/catch errors * @param {number} limit max number of nodes to execute (used to execute from start to a node) */ @@ -1059,10 +1045,10 @@ var start = LiteGraph.getTime(); this.globaltime = 0.001 * (start - this.starttime); - //not optimal: executes possible pending actions in node, problem is it is not optimized - //it is done here as if it was done in the later loop it wont be called in the node missed the onExecute - - //from now on it will iterate only on executable nodes which is faster + // not optimal: executes possible pending actions in node, problem is it is not optimized + // it is done here as if it was done in the later loop it wont be called in the node missed the onExecute + + // from now on it will iterate only on executable nodes which is faster var nodes = this._nodes_executable ? this._nodes_executable : this._nodes; @@ -1070,18 +1056,18 @@ return; } - limit = limit || nodes.length; + limit = limit || nodes.length; if (do_not_catch_errors) { - //iterations + // iterations for (var i = 0; i < num; i++) { for (var j = 0; j < limit; ++j) { var node = nodes[j]; if(LiteGraph.use_deferred_actions && node._waiting_actions && node._waiting_actions.length) node.executePendingActions(); if (node.mode == LiteGraph.ALWAYS && node.onExecute) { - //wrap node.onExecute(); - node.doExecute(); + // wrap node.onExecute(); + node.doExecute(); } } @@ -1094,9 +1080,9 @@ if (this.onAfterExecute) { this.onAfterExecute(); } - } else { //catch errors + } else { // catch errors try { - //iterations + // iterations for (var i = 0; i < num; i++) { for (var j = 0; j < limit; ++j) { var node = nodes[j]; @@ -1159,27 +1145,27 @@ } }; - //This is more internal, it computes the executable nodes in order and returns it + // This is more internal, it computes the executable nodes in order and returns it LGraph.prototype.computeExecutionOrder = function( only_onExecute, - set_level + set_level, ) { var L = []; var S = []; var M = {}; - var visited_links = {}; //to avoid repeating links - var remaining_links = {}; //to a + var visited_links = {}; // to avoid repeating links + var remaining_links = {}; // to a - //search for the nodes without inputs (starting nodes) + // search for the nodes without inputs (starting nodes) for (var i = 0, l = this._nodes.length; i < l; ++i) { var node = this._nodes[i]; if (only_onExecute && !node.onExecute) { continue; } - M[node.id] = node; //add to pending nodes + M[node.id] = node; // add to pending nodes - var num = 0; //num of input connections + var num = 0; // num of input connections if (node.inputs) { for (var j = 0, l2 = node.inputs.length; j < l2; j++) { if (node.inputs[j] && node.inputs[j].link != null) { @@ -1189,13 +1175,12 @@ } if (num == 0) { - //is a starting node + // is a starting node S.push(node); if (set_level) { node._level = 1; } - } //num of input links - else { + } else { // num of input links if (set_level) { node._level = 0; } @@ -1203,24 +1188,21 @@ } } - while (true) { - if (S.length == 0) { - break; - } + while (S.length != 0) { - //get an starting node + // get an starting node var node = S.shift(); - L.push(node); //add to ordered list - delete M[node.id]; //remove from the pending nodes + L.push(node); // add to ordered list + delete M[node.id]; // remove from the pending nodes if (!node.outputs) { continue; } - //for every output + // for every output for (var i = 0; i < node.outputs.length; i++) { var output = node.outputs[i]; - //not connected + // not connected if ( output == null || output.links == null || @@ -1229,7 +1211,7 @@ continue; } - //for every connection + // for every connection for (var j = 0; j < output.links.length; j++) { var link_id = output.links[j]; var link = this.links[link_id]; @@ -1237,7 +1219,7 @@ continue; } - //already visited link (ignore it) + // already visited link (ignore it) if (visited_links[link.id]) { continue; } @@ -1256,16 +1238,16 @@ target_node._level = node._level + 1; } - visited_links[link.id] = true; //mark as visited - remaining_links[target_node.id] -= 1; //reduce the number of links remaining + visited_links[link.id] = true; // mark as visited + remaining_links[target_node.id] -= 1; // reduce the number of links remaining if (remaining_links[target_node.id] == 0) { S.push(target_node); - } //if no more links, then add to starters array + } // if no more links, then add to starters array } } } - //the remaining ones (loops) + // the remaining ones (loops) for (var i in M) { L.push(M[i]); } @@ -1276,23 +1258,23 @@ var l = L.length; - //save order number in the node + // save order number in the node for (var i = 0; i < l; ++i) { L[i].order = i; } - //sort now by priority + // sort now by priority L = L.sort(function(A, B) { var Ap = A.constructor.priority || A.priority || 0; var Bp = B.constructor.priority || B.priority || 0; if (Ap == Bp) { - //if same priority, sort by order + // if same priority, sort by order return A.order - B.order; } - return Ap - Bp; //sort by priority + return Ap - Bp; // sort by priority }); - //save order number in the node, again... + // save order number in the node, again... for (var i = 0; i < l; ++i) { L[i].order = i; } @@ -1473,7 +1455,7 @@ return; } - //groups + // groups if (node.constructor === LGraphGroup) { this._groups.push(node); this.setDirtyCanvas(true); @@ -1483,15 +1465,12 @@ return; } - //nodes + // nodes if (node.id != -1 && this._nodes_by_id[node.id] != null) { - console.warn( - "LiteGraph: there is already a node with this ID, changing it" - ); + console.warn("LiteGraph: there is already a node with this ID, changing it"); if (LiteGraph.use_uuids) { node.id = LiteGraph.uuidv4(); - } - else { + } else { node.id = ++this.last_node_id; } } @@ -1500,12 +1479,11 @@ throw "LiteGraph: max number of nodes in a graph reached"; } - //give him an id + // give him an id if (LiteGraph.use_uuids) { if (node.id == null || node.id == -1) node.id = LiteGraph.uuidv4(); - } - else { + } else { if (node.id == null || node.id == -1) { node.id = ++this.last_node_id; } else if (this.last_node_id < node.id) { @@ -1538,7 +1516,7 @@ this.setDirtyCanvas(true); this.change(); - return node; //to chain actions + return node; // to chain actions }; /** @@ -1562,15 +1540,15 @@ if (this._nodes_by_id[node.id] == null) { return; - } //not found + } // not found if (node.ignore_remove) { return; - } //cannot be removed + } // cannot be removed - this.beforeChange(); //sure? - almost sure is wrong + this.beforeChange(); // sure? - almost sure is wrong - //disconnect inputs + // disconnect inputs if (node.inputs) { for (var i = 0; i < node.inputs.length; i++) { var slot = node.inputs[i]; @@ -1580,7 +1558,7 @@ } } - //disconnect outputs + // disconnect outputs if (node.outputs) { for (var i = 0; i < node.outputs.length; i++) { var slot = node.outputs[i]; @@ -1590,9 +1568,9 @@ } } - //node.id = -1; //why? + // node.id = -1; //why? - //callback + // callback if (node.onRemoved) { node.onRemoved(); } @@ -1600,7 +1578,7 @@ node.graph = null; this._version++; - //remove from canvas render + // remove from canvas render if (this.list_of_graphcanvas) { for (var i = 0; i < this.list_of_graphcanvas.length; ++i) { var canvas = this.list_of_graphcanvas[i]; @@ -1613,7 +1591,7 @@ } } - //remove from containers + // remove from containers var pos = this._nodes.indexOf(node); if (pos != -1) { this._nodes.splice(pos, 1); @@ -1624,11 +1602,11 @@ this.onNodeRemoved(node); } - //close panels - this.sendActionToCanvas("checkPanels"); + // close panels + this.sendActionToCanvas("checkPanels"); this.setDirtyCanvas(true, true); - this.afterChange(); //sure? - almost sure is wrong + this.afterChange(); // sure? - almost sure is wrong this.change(); this.updateExecutionOrder(); @@ -1723,16 +1701,16 @@ */ LGraph.prototype.getNodeOnPos = function(x, y, nodes_list, margin) { nodes_list = nodes_list || this._nodes; - var nRet = null; + var nRet = null; for (var i = nodes_list.length - 1; i >= 0; i--) { var n = nodes_list[i]; if (n.isPointInside(x, y, margin)) { // check for lesser interest nodes (TODO check for overlapping, use the top) - /*if (typeof n == "LGraphGroup"){ + /* if (typeof n == "LGraphGroup"){ nRet = n; }else{*/ - return n; - /*}*/ + return n; + /* }*/ } } return nRet; @@ -1790,14 +1768,14 @@ LGraph.prototype.onAction = function(action, param, options) { this._input_nodes = this.findNodesByClass( LiteGraph.GraphInput, - this._input_nodes + this._input_nodes, ); for (var i = 0; i < this._input_nodes.length; ++i) { var node = this._input_nodes[i]; if (node.properties.name != action) { continue; } - //wrap node.onAction(action, param); + // wrap node.onAction(action, param); node.actionDo(action, param, options); break; } @@ -1819,14 +1797,14 @@ LGraph.prototype.addInput = function(name, type, value) { var input = this.inputs[name]; if (input) { - //already exist + // already exist return; } - this.beforeChange(); + this.beforeChange(); this.inputs[name] = { name: name, type: type, value: value }; this._version++; - this.afterChange(); + this.afterChange(); if (this.onInputAdded) { this.onInputAdded(name, type); @@ -2087,7 +2065,7 @@ } }; - //used for undo, called before any change is made to the graph + // used for undo, called before any change is made to the graph LGraph.prototype.beforeChange = function(info) { if (this.onBeforeChange) { this.onBeforeChange(this,info); @@ -2095,7 +2073,7 @@ this.sendActionToCanvas("onBeforeChange", this); }; - //used to resend actions, called after any change is made to the graph + // used to resend actions, called after any change is made to the graph LGraph.prototype.afterChange = function(info) { if (this.onAfterChange) { this.onAfterChange(this,info); @@ -2178,7 +2156,7 @@ } }; - //save and recover app state *************************************** + // save and recover app state *************************************** /** * Creates a Object containing all the info about this graph, it can be serialized * @method serialize @@ -2190,18 +2168,16 @@ nodes_info.push(this._nodes[i].serialize()); } - //pack link info into a non-verbose format + // pack link info into a non-verbose format var links = []; for (var i in this.links) { - //links is an OBJECT + // links is an OBJECT var link = this.links[i]; if (!link.serialize) { - //weird bug I havent solved yet - console.warn( - "weird LLink bug, link info is not a LLink but a regular object" - ); + // weird bug I havent solved yet + console.warn("weird LLink bug, link info is not a LLink but a regular object"); var link2 = new LLink(); - for (var j in link) { + for (var j in link) { link2[j] = link[j]; } this.links[i] = link2; @@ -2223,12 +2199,12 @@ links: links, groups: groups_info, config: this.config, - extra: this.extra, - version: LiteGraph.VERSION + extra: this.extra, + version: LiteGraph.VERSION, }; - if(this.onSerialize) - this.onSerialize(data); + if(this.onSerialize) + this.onSerialize(data); return data; }; @@ -2250,16 +2226,15 @@ var nodes = data.nodes; - //decode links info (they are very verbose) + // decode links info (they are very verbose) if (data.links && data.links.constructor === Array) { var links = []; for (var i = 0; i < data.links.length; ++i) { var link_data = data.links[i]; - if(!link_data) //weird bug - { - console.warn("serialized graph link data contains errors, skipping."); - continue; - } + if(!link_data) { // weird bug + console.warn("serialized graph link data contains errors, skipping."); + continue; + } var link = new LLink(); link.configure(link_data); links[link.id] = link; @@ -2267,41 +2242,39 @@ data.links = links; } - //copy all stored fields + // copy all stored fields for (var i in data) { - if(i == "nodes" || i == "groups" ) //links must be accepted - continue; + if(i == "nodes" || i == "groups" ) // links must be accepted + continue; this[i] = data[i]; } var error = false; - //create nodes + // create nodes this._nodes = []; if (nodes) { for (var i = 0, l = nodes.length; i < l; ++i) { - var n_info = nodes[i]; //stored info + var n_info = nodes[i]; // stored info var node = LiteGraph.createNode(n_info.type, n_info.title); if (!node) { if (LiteGraph.debug) { - console.log( - "Node not found or has errors: " + n_info.type - ); + console.log("Node not found or has errors: " + n_info.type); } - //in case of error we create a replacement node to avoid losing info + // in case of error we create a replacement node to avoid losing info node = new LGraphNode(); node.last_serialization = n_info; node.has_errors = true; error = true; - //continue; + // continue; } - node.id = n_info.id; //id it or it will create a new id - this.add(node, true); //add before configure, otherwise configure cannot create links + node.id = n_info.id; // id it or it will create a new id + this.add(node, true); // add before configure, otherwise configure cannot create links } - //configure nodes afterwards so they can reach each other + // configure nodes afterwards so they can reach each other for (var i = 0, l = nodes.length; i < l; ++i) { var n_info = nodes[i]; var node = this.getNodeById(n_info.id); @@ -2311,7 +2284,7 @@ } } - //groups + // groups this._groups.length = 0; if (data.groups) { for (var i = 0; i < data.groups.length; ++i) { @@ -2323,10 +2296,10 @@ this.updateExecutionOrder(); - this.extra = data.extra || {}; + this.extra = data.extra || {}; - if(this.onConfigure) - this.onConfigure(data); + if(this.onConfigure) + this.onConfigure(data); this._version++; this.setDirtyCanvas(true, true); @@ -2336,22 +2309,21 @@ LGraph.prototype.load = function(url, callback) { var that = this; - //from file - if(url.constructor === File || url.constructor === Blob) - { - var reader = new FileReader(); - reader.addEventListener('load', function(event) { - var data = JSON.parse(event.target.result); - that.configure(data); - if(callback) - callback(); - }); - - reader.readAsText(url); - return; - } - - //is a string, then an URL + // from file + if(url.constructor === File || url.constructor === Blob) { + var reader = new FileReader(); + reader.addEventListener('load', function(event) { + var data = JSON.parse(event.target.result); + that.configure(data); + if(callback) + callback(); + }); + + reader.readAsText(url); + return; + } + + // is a string, then an URL var req = new XMLHttpRequest(); req.open("GET", url, true); req.send(null); @@ -2362,8 +2334,8 @@ } var data = JSON.parse( req.response ); that.configure(data); - if(callback) - callback(); + if(callback) + callback(); }; req.onerror = function(err) { console.error("Error loading graph:", err); @@ -2371,10 +2343,10 @@ }; LGraph.prototype.onNodeTrace = function(node, msg, color) { - //TODO + // TODO }; - //this is the class in charge of storing link information + // this is the class in charge of storing link information function LLink(id, type, origin_id, origin_slot, target_id, target_slot) { this.id = id; this.type = type; @@ -2384,7 +2356,7 @@ this.target_slot = target_slot; this._data = null; - this._pos = new Float32Array(2); //center + this._pos = new Float32Array(2); // center } LLink.prototype.configure = function(o) { @@ -2412,7 +2384,7 @@ this.origin_slot, this.target_id, this.target_slot, - this.type + this.type, ]; }; @@ -2437,7 +2409,7 @@ + resizable: if set to false it wont be resizable with the mouse + horizontal: slots are distributed horizontally + widgets_start_y: widgets start at y distance from the top of the node - + flags object: + collapsed: if it is collapsed @@ -2503,25 +2475,24 @@ get: function() { return this._pos; }, - enumerable: true + enumerable: true, }); if (LiteGraph.use_uuids) { this.id = LiteGraph.uuidv4(); - } - else { - this.id = -1; //not know till not added + } else { + this.id = -1; // not know till not added } this.type = null; - //inputs available: array of inputs + // inputs available: array of inputs this.inputs = []; this.outputs = []; this.connections = []; - //local data - this.properties = {}; //for the values - this.properties_info = []; //for the info + // local data + this.properties = {}; // for the values + this.properties_info = []; // for the info this.flags = {}; }; @@ -2536,7 +2507,7 @@ } for (var j in info) { if (j == "properties") { - //i don't want to clone properties, I want to reuse the old container + // i don't want to clone properties, I want to reuse the old container for (var k in info.properties) { this.properties[k] = info.properties[k]; if (this.onPropertyChanged) { @@ -2549,14 +2520,13 @@ if (info[j] == null) { continue; } else if (typeof info[j] == "object") { - //object + // object if (this[j] && this[j].configure) { this[j].configure(info[j]); } else { this[j] = LiteGraph.cloneObject(info[j], this[j]); } - } //value - else { + } else { // value this[j] = info[j]; } } @@ -2565,54 +2535,52 @@ this.title = this.constructor.title; } - if (this.inputs) { - for (var i = 0; i < this.inputs.length; ++i) { - var input = this.inputs[i]; - var link_info = this.graph ? this.graph.links[input.link] : null; - if (this.onConnectionsChange) - this.onConnectionsChange( LiteGraph.INPUT, i, true, link_info, input ); //link_info has been created now, so its updated + if (this.inputs) { + for (var i = 0; i < this.inputs.length; ++i) { + var input = this.inputs[i]; + var link_info = this.graph ? this.graph.links[input.link] : null; + if (this.onConnectionsChange) + this.onConnectionsChange( LiteGraph.INPUT, i, true, link_info, input ); // link_info has been created now, so its updated - if( this.onInputAdded ) - this.onInputAdded(input); + if( this.onInputAdded ) + this.onInputAdded(input); - } - } - - if (this.outputs) { - for (var i = 0; i < this.outputs.length; ++i) { - var output = this.outputs[i]; - if (!output.links) { - continue; - } - for (var j = 0; j < output.links.length; ++j) { - var link_info = this.graph ? this.graph.links[output.links[j]] : null; - if (this.onConnectionsChange) - this.onConnectionsChange( LiteGraph.OUTPUT, i, true, link_info, output ); //link_info has been created now, so its updated - } - - if( this.onOutputAdded ) - this.onOutputAdded(output); - } + } } - if( this.widgets ) - { - for (var i = 0; i < this.widgets.length; ++i) - { - var w = this.widgets[i]; - if(!w) - continue; - if(w.options && w.options.property && (this.properties[ w.options.property ] != undefined)) - w.value = JSON.parse( JSON.stringify( this.properties[ w.options.property ] ) ); - } - if (info.widgets_values) { - for (var i = 0; i < info.widgets_values.length; ++i) { - if (this.widgets[i]) { - this.widgets[i].value = info.widgets_values[i]; - } - } - } - } + if (this.outputs) { + for (var i = 0; i < this.outputs.length; ++i) { + var output = this.outputs[i]; + if (!output.links) { + continue; + } + for (var j = 0; j < output.links.length; ++j) { + var link_info = this.graph ? this.graph.links[output.links[j]] : null; + if (this.onConnectionsChange) + this.onConnectionsChange( LiteGraph.OUTPUT, i, true, link_info, output ); // link_info has been created now, so its updated + } + + if( this.onOutputAdded ) + this.onOutputAdded(output); + } + } + + if( this.widgets ) { + for (var i = 0; i < this.widgets.length; ++i) { + var w = this.widgets[i]; + if(!w) + continue; + if(w.options && w.options.property && (this.properties[w.options.property] != undefined)) + w.value = JSON.parse( JSON.stringify( this.properties[w.options.property] ) ); + } + if (info.widgets_values) { + for (var i = 0; i < info.widgets_values.length; ++i) { + if (this.widgets[i]) { + this.widgets[i].value = info.widgets_values[i]; + } + } + } + } if (this.onConfigure) { this.onConfigure(info); @@ -2625,18 +2593,18 @@ */ LGraphNode.prototype.serialize = function() { - //create serialization object + // create serialization object var o = { id: this.id, type: this.type, pos: this.pos, size: this.size, flags: LiteGraph.cloneObject(this.flags), - order: this.order, - mode: this.mode + order: this.order, + mode: this.mode, }; - //special case for when there were errors + // special case for when there were errors if (this.constructor === LGraphNode && this.last_serialization) { return this.last_serialization; } @@ -2646,7 +2614,7 @@ } if (this.outputs) { - //clear outputs last data (because data in connections is never serialized but stored inside the outputs info) + // clear outputs last data (because data in connections is never serialized but stored inside the outputs info) for (var i = 0; i < this.outputs.length; i++) { delete this.outputs[i]._data; } @@ -2664,10 +2632,10 @@ if (this.widgets && this.serialize_widgets) { o.widgets_values = []; for (var i = 0; i < this.widgets.length; ++i) { - if(this.widgets[i]) - o.widgets_values[i] = this.widgets[i].value; - else - o.widgets_values[i] = null; + if(this.widgets[i]) + o.widgets_values[i] = this.widgets[i].value; + else + o.widgets_values[i] = null; } } @@ -2690,9 +2658,7 @@ if (this.onSerialize) { if (this.onSerialize(o)) { - console.warn( - "node onSerialize shouldnt return anything, data should be stored in the object pass in the first parameter" - ); + console.warn("node onSerialize shouldnt return anything, data should be stored in the object pass in the first parameter"); } } @@ -2706,10 +2672,10 @@ return null; } - //we clone it because serialize returns shared containers + // we clone it because serialize returns shared containers var data = LiteGraph.cloneObject(this.serialize()); - //remove links + // remove links if (data.inputs) { for (var i = 0; i < data.inputs.length; ++i) { data.inputs[i].link = null; @@ -2730,7 +2696,7 @@ data["id"] = LiteGraph.uuidv4() } - //remove links + // remove links node.configure(data); return node; @@ -2744,7 +2710,7 @@ LGraphNode.prototype.toString = function() { return JSON.stringify(this.serialize()); }; - //LGraphNode.prototype.deserialize = function(info) {} //this cannot be done from within, must be done in LiteGraph + // LGraphNode.prototype.deserialize = function(info) {} //this cannot be done from within, must be done in LiteGraph /** * get the title string @@ -2765,26 +2731,24 @@ if (!this.properties) { this.properties = {}; } - if( value === this.properties[name] ) - return; - var prev_value = this.properties[name]; + if( value === this.properties[name] ) + return; + var prev_value = this.properties[name]; this.properties[name] = value; if (this.onPropertyChanged) { - if( this.onPropertyChanged(name, value, prev_value) === false ) //abort change - this.properties[name] = prev_value; + if( this.onPropertyChanged(name, value, prev_value) === false ) // abort change + this.properties[name] = prev_value; } - if(this.widgets) //widgets could be linked to properties - for(var i = 0; i < this.widgets.length; ++i) - { - var w = this.widgets[i]; - if(!w) - continue; - if(w.options.property == name) - { - w.value = value; - break; - } - } + if(this.widgets) // widgets could be linked to properties + for(var i = 0; i < this.widgets.length; ++i) { + var w = this.widgets[i]; + if(!w) + continue; + if(w.options.property == name) { + w.value = value; + break; + } + } }; // Execution ************************* @@ -2799,8 +2763,8 @@ return; } - //this maybe slow and a niche case - //if(slot && slot.constructor === String) + // this maybe slow and a niche case + // if(slot && slot.constructor === String) // slot = this.findOutputSlot(slot); if (slot == -1 || slot >= this.outputs.length) { @@ -2812,16 +2776,16 @@ return; } - //store data in the output itself in case we want to debug + // store data in the output itself in case we want to debug output_info._data = data; - //if there are connections, pass the data to the connections + // if there are connections, pass the data to the connections if (this.outputs[slot].links) { for (var i = 0; i < this.outputs[slot].links.length; i++) { var link_id = this.outputs[slot].links[i]; - var link = this.graph.links[link_id]; - if(link) - link.data = data; + var link = this.graph.links[link_id]; + if(link) + link.data = data; } } }; @@ -2843,10 +2807,10 @@ if (!output_info) { return; } - //store data in the output itself in case we want to debug + // store data in the output itself in case we want to debug output_info.type = type; - //if there are connections, pass the data to the connections + // if there are connections, pass the data to the connections if (this.outputs[slot].links) { for (var i = 0; i < this.outputs[slot].links.length; i++) { var link_id = this.outputs[slot].links[i]; @@ -2865,7 +2829,7 @@ LGraphNode.prototype.getInputData = function(slot, force_update) { if (!this.inputs) { return; - } //undefined; + } // undefined; if (slot >= this.inputs.length || this.inputs[slot].link == null) { return; @@ -2874,7 +2838,7 @@ var link_id = this.inputs[slot].link; var link = this.graph.links[link_id]; if (!link) { - //bug: weird case but it happens sometimes + // bug: weird case but it happens sometimes return null; } @@ -2882,7 +2846,7 @@ return link.data; } - //special case: used to extract data from the incoming connection before the graph has been executed + // special case: used to extract data from the incoming connection before the graph has been executed var node = this.graph.getNodeById(link.origin_id); if (!node) { return link.data; @@ -2906,7 +2870,7 @@ LGraphNode.prototype.getInputDataType = function(slot) { if (!this.inputs) { return null; - } //undefined; + } // undefined; if (slot >= this.inputs.length || this.inputs[slot].link == null) { return null; @@ -2914,7 +2878,7 @@ var link_id = this.inputs[slot].link; var link = this.graph.links[link_id]; if (!link) { - //bug: weird case but it happens sometimes + // bug: weird case but it happens sometimes return null; } var node = this.graph.getNodeById(link.origin_id); @@ -2937,7 +2901,7 @@ */ LGraphNode.prototype.getInputDataByName = function( slot_name, - force_update + force_update, ) { var slot = this.findInputSlot(slot_name); if (slot == -1) { @@ -2987,7 +2951,7 @@ } if (slot < this.inputs.length) { var slot_info = this.inputs[slot]; - return this.graph.links[ slot_info.link ]; + return this.graph.links[slot_info.link]; } return null; }; @@ -3141,59 +3105,58 @@ return r; }; - LGraphNode.prototype.addOnTriggerInput = function(){ + LGraphNode.prototype.addOnTriggerInput = function() { var trigS = this.findInputSlot("onTrigger"); - if (trigS == -1){ //!trigS || + if (trigS == -1) { // !trigS || var input = this.addInput("onTrigger", LiteGraph.EVENT, {optional: true, nameLocked: true}); return this.findInputSlot("onTrigger"); } return trigS; } - - LGraphNode.prototype.addOnExecutedOutput = function(){ + + LGraphNode.prototype.addOnExecutedOutput = function() { var trigS = this.findOutputSlot("onExecuted"); - if (trigS == -1){ //!trigS || + if (trigS == -1) { // !trigS || var output = this.addOutput("onExecuted", LiteGraph.ACTION, {optional: true, nameLocked: true}); return this.findOutputSlot("onExecuted"); } return trigS; } - - LGraphNode.prototype.onAfterExecuteNode = function(param, options){ + + LGraphNode.prototype.onAfterExecuteNode = function(param, options) { var trigS = this.findOutputSlot("onExecuted"); - if (trigS != -1){ - - //console.debug(this.id+":"+this.order+" triggering slot onAfterExecute"); - //console.debug(param); - //console.debug(options); + if (trigS != -1) { + + // console.debug(this.id+":"+this.order+" triggering slot onAfterExecute"); + // console.debug(param); + // console.debug(options); this.triggerSlot(trigS, param, null, options); - + } - } - - LGraphNode.prototype.changeMode = function(modeTo){ - switch(modeTo){ + } + + LGraphNode.prototype.changeMode = function(modeTo) { + switch(modeTo) { case LiteGraph.ON_EVENT: // this.addOnExecutedOutput(); break; - + case LiteGraph.ON_TRIGGER: this.addOnTriggerInput(); this.addOnExecutedOutput(); break; - + case LiteGraph.NEVER: break; - + case LiteGraph.ALWAYS: break; - + case LiteGraph.ON_REQUEST: break; - + default: return false; - break; } this.mode = modeTo; return true; @@ -3202,19 +3165,18 @@ /** * Triggers the execution of actions that were deferred when the action was triggered * @method executePendingActions - */ + */ LGraphNode.prototype.executePendingActions = function() { if(!this._waiting_actions || !this._waiting_actions.length) return; - for(var i = 0; i < this._waiting_actions.length;++i) - { + for(var i = 0; i < this._waiting_actions.length;++i) { var p = this._waiting_actions[i]; this.onAction(p[0],p[1],p[2],p[3],p[4]); - } + } this._waiting_actions.length = 0; } - + /** * Triggers the node code execution, place a boolean/counter to mark the node as being executed * @method doExecute @@ -3223,30 +3185,28 @@ */ LGraphNode.prototype.doExecute = function(param, options) { options = options || {}; - if (this.onExecute){ - + if (this.onExecute) { + // enable this to give the event an ID - if (!options.action_call) options.action_call = this.id+"_exec_"+Math.floor(Math.random()*9999); - - this.graph.nodes_executing[this.id] = true; //.push(this.id); + if (!options.action_call) options.action_call = this.id+"_exec_"+Math.floor(Math.random()*9999); + + this.graph.nodes_executing[this.id] = true; // .push(this.id); this.onExecute(param, options); - - this.graph.nodes_executing[this.id] = false; //.pop(); - + + this.graph.nodes_executing[this.id] = false; // .pop(); + // save execution/action ref this.exec_version = this.graph.iteration; - if(options && options.action_call){ + if(options && options.action_call) { this.action_call = options.action_call; // if (param) this.graph.nodes_executedAction[this.id] = options.action_call; } } - else { - } this.execute_triggered = 2; // the nFrames it will be used (-- each step), means "how old" is the event if(this.onAfterExecuteNode) this.onAfterExecuteNode(param, options); // callback }; - + /** * Triggers an action, wrapped by logics to control execution flow * @method actionDo @@ -3255,19 +3215,19 @@ */ LGraphNode.prototype.actionDo = function(action, param, options, action_slot ) { options = options || {}; - if (this.onAction){ - - // enable this to give the event an ID + if (this.onAction) { + + // enable this to give the event an ID if (!options.action_call) options.action_call = this.id+"_"+(action?action:"action")+"_"+Math.floor(Math.random()*9999); - - this.graph.nodes_actioning[this.id] = (action?action:"actioning"); //.push(this.id); - + + this.graph.nodes_actioning[this.id] = (action?action:"actioning"); // .push(this.id); + this.onAction(action, param, options, action_slot); - - this.graph.nodes_actioning[this.id] = false; //.pop(); - + + this.graph.nodes_actioning[this.id] = false; // .pop(); + // save execution/action ref - if(options && options.action_call){ + if(options && options.action_call) { this.action_call = options.action_call; // if (param) this.graph.nodes_executedAction[this.id] = options.action_call; } @@ -3275,7 +3235,7 @@ this.action_triggered = 2; // the nFrames it will be used (-- each step), means "how old" is the event if(this.onAfterExecuteNode) this.onAfterExecuteNode(param, options); }; - + /** * Triggers an event in this node, this will trigger any output with the same name * @method trigger @@ -3311,14 +3271,13 @@ return; } - if(slot == null) - { - console.error("slot must be a number"); - return; - } + if(slot == null) { + console.error("slot must be a number"); + return; + } - if(slot.constructor !== Number) - console.warn("slot must be a number, use node.trigger('name') if you want to use a string"); + if(slot.constructor !== Number) + console.warn("slot must be a number, use node.trigger('name') if you want to use a string"); var output = this.outputs[slot]; if (!output) { @@ -3334,52 +3293,47 @@ this.graph._last_trigger_time = LiteGraph.getTime(); } - //for every link attached here + // for every link attached here for (var k = 0; k < links.length; ++k) { var id = links[k]; if (link_id != null && link_id != id) { - //to skip links + // to skip links continue; } var link_info = this.graph.links[links[k]]; if (!link_info) { - //not connected + // not connected continue; } link_info._last_time = LiteGraph.getTime(); var node = this.graph.getNodeById(link_info.target_id); if (!node) { - //node not found? + // node not found? continue; } - //used to mark events in graph + // used to mark events in graph var target_connection = node.inputs[link_info.target_slot]; - if (node.mode === LiteGraph.ON_TRIGGER) - { - // generate unique trigger ID if not present - if (!options.action_call) options.action_call = this.id+"_trigg_"+Math.floor(Math.random()*9999); + if (node.mode === LiteGraph.ON_TRIGGER) { + // generate unique trigger ID if not present + if (!options.action_call) options.action_call = this.id+"_trigg_"+Math.floor(Math.random()*9999); if (node.onExecute) { // -- wrapping node.onExecute(param); -- node.doExecute(param, options); } - } - else if (node.onAction) { + } else if (node.onAction) { // generate unique action ID if not present - if (!options.action_call) options.action_call = this.id+"_act_"+Math.floor(Math.random()*9999); - //pass the action name + if (!options.action_call) options.action_call = this.id+"_act_"+Math.floor(Math.random()*9999); + // pass the action name var target_connection = node.inputs[link_info.target_slot]; - //instead of executing them now, it will be executed in the next graph loop, to ensure data flow - if(LiteGraph.use_deferred_actions && node.onExecute) - { + // instead of executing them now, it will be executed in the next graph loop, to ensure data flow + if(LiteGraph.use_deferred_actions && node.onExecute) { if(!node._waiting_actions) node._waiting_actions = []; node._waiting_actions.push([target_connection.name, param, options, link_info.target_slot]); - } - else - { + } else { // wrap node.onAction(target_connection.name, param); node.actionDo( target_connection.name, param, options, link_info.target_slot ); } @@ -3408,16 +3362,16 @@ return; } - //for every link attached here + // for every link attached here for (var k = 0; k < links.length; ++k) { var id = links[k]; if (link_id != null && link_id != id) { - //to skip links + // to skip links continue; } var link_info = this.graph.links[links[k]]; if (!link_info) { - //not connected + // not connected continue; } link_info._last_time = 0; @@ -3429,12 +3383,11 @@ * @method setSize * @param {vec2} size */ - LGraphNode.prototype.setSize = function(size) - { - this.size = size; - if(this.onResize) - this.onResize(this.size); - } + LGraphNode.prototype.setSize = function(size) { + this.size = size; + if(this.onResize) + this.onResize(this.size); + } /** * add a new property to this node @@ -3448,7 +3401,7 @@ name, default_value, type, - extra_info + extra_info, ) { var o = { name: name, type: type, default_value: default_value }; if (extra_info) { @@ -3467,7 +3420,7 @@ return o; }; - //connections + // connections /** * add a new output slot to use in this node @@ -3491,9 +3444,9 @@ if (this.onOutputAdded) { this.onOutputAdded(output); } - + if (LiteGraph.auto_load_slot_types) LiteGraph.registerNodeAndSlotType(this,type,true); - + this.setSize( this.computeSize() ); this.setDirtyCanvas(true, true); return output; @@ -3521,9 +3474,9 @@ if (this.onOutputAdded) { this.onOutputAdded(o); } - + if (LiteGraph.auto_load_slot_types) LiteGraph.registerNodeAndSlotType(this,info[1],true); - + } this.setSize( this.computeSize() ); @@ -3584,8 +3537,8 @@ if (this.onInputAdded) { this.onInputAdded(input); - } - + } + LiteGraph.registerNodeAndSlotType(this,type); this.setDirtyCanvas(true, true); @@ -3614,7 +3567,7 @@ if (this.onInputAdded) { this.onInputAdded(o); } - + LiteGraph.registerNodeAndSlotType(this,info[1]); } @@ -3661,7 +3614,7 @@ type: type, pos: pos, direction: direction, - links: null + links: null, }; this.connections.push(o); return o; @@ -3680,11 +3633,11 @@ var rows = Math.max( this.inputs ? this.inputs.length : 1, - this.outputs ? this.outputs.length : 1 + this.outputs ? this.outputs.length : 1, ); var size = out || new Float32Array([0, 0]); rows = Math.max(rows, 1); - var font_size = LiteGraph.NODE_TEXT_SIZE; //although it should be graphcanvas.inner_text_font size + var font_size = LiteGraph.NODE_TEXT_SIZE; // although it should be graphcanvas.inner_text_font size var title_width = compute_text_size(this.title); var input_width = 0; @@ -3731,7 +3684,7 @@ widgets_height += 8; } - //compute height using widgets height + // compute height using widgets height if( this.widgets_up ) size[1] = Math.max( size[1], widgets_height ); else if( this.widgets_start_y != null ) @@ -3753,7 +3706,7 @@ size[1] = this.constructor.min_height; } - size[1] += 6; //margin + size[1] += 6; // margin return size; }; @@ -3765,13 +3718,12 @@ * @param {String} property name of the property * @return {Object} the object with all the available info */ - LGraphNode.prototype.getPropertyInfo = function( property ) - { + LGraphNode.prototype.getPropertyInfo = function( property ) { var info = null; - //there are several ways to define info about a property - //legacy mode - if (this.properties_info) { + // there are several ways to define info about a property + // legacy mode + if (this.properties_info) { for (var i = 0; i < this.properties_info.length; ++i) { if (this.properties_info[i].name == property) { info = this.properties_info[i]; @@ -3779,27 +3731,27 @@ } } } - //litescene mode using the constructor - if(this.constructor["@" + property]) - info = this.constructor["@" + property]; + // litescene mode using the constructor + if(this.constructor["@" + property]) + info = this.constructor["@" + property]; - if(this.constructor.widgets_info && this.constructor.widgets_info[property]) - info = this.constructor.widgets_info[property]; + if(this.constructor.widgets_info && this.constructor.widgets_info[property]) + info = this.constructor.widgets_info[property]; - //litescene mode using the constructor - if (!info && this.onGetPropertyInfo) { + // litescene mode using the constructor + if (!info && this.onGetPropertyInfo) { info = this.onGetPropertyInfo(property); } if (!info) info = {}; - if(!info.type) - info.type = typeof this.properties[property]; - if(info.widget == "combo") - info.type = "enum"; + if(!info.type) + info.type = typeof this.properties[property]; + if(info.widget == "combo") + info.type = "enum"; - return info; - } + return info; + } /** * Defines a widget inside the node, it will be rendered on top of the node, you can control lots of properties @@ -3809,44 +3761,40 @@ * @param {String} name the text to show on the widget * @param {String} value the default value * @param {Function|String} callback function to call when it changes (optionally, it can be the name of the property to modify) - * @param {Object} options the object that contains special properties of this widget + * @param {Object} options the object that contains special properties of this widget * @return {Object} the created widget object */ - LGraphNode.prototype.addWidget = function( type, name, value, callback, options ) - { + LGraphNode.prototype.addWidget = function( type, name, value, callback, options ) { if (!this.widgets) { this.widgets = []; } - if(!options && callback && callback.constructor === Object) - { - options = callback; - callback = null; - } + if(!options && callback && callback.constructor === Object) { + options = callback; + callback = null; + } - if(options && options.constructor === String) //options can be the property name - options = { property: options }; + if(options && options.constructor === String) // options can be the property name + options = { property: options }; - if(callback && callback.constructor === String) //callback can be the property name - { - if(!options) - options = {}; - options.property = callback; - callback = null; - } + if(callback && callback.constructor === String) { // callback can be the property name + if(!options) + options = {}; + options.property = callback; + callback = null; + } - if(callback && callback.constructor !== Function) - { - console.warn("addWidget: callback must be a function"); - callback = null; - } + if(callback && callback.constructor !== Function) { + console.warn("addWidget: callback must be a function"); + callback = null; + } var w = { type: type.toLowerCase(), name: name, value: value, callback: callback, - options: options || {} + options: options || {}, }; if (w.options.y !== undefined) { @@ -3860,7 +3808,7 @@ throw "LiteGraph addWidget('combo',...) requires to pass values in options: { values:['red','blue'] }"; } this.widgets.push(w); - this.setSize( this.computeSize() ); + this.setSize( this.computeSize() ); return w; }; @@ -3884,13 +3832,13 @@ const nodePos = this.pos; const isCollapsed = this.flags.collapsed; const nodeSize = this.size; - + let left_offset = 0; // 1 offset due to how nodes are rendered - let right_offset = 1 ; + let right_offset = 1 ; let top_offset = 0; let bottom_offset = 0; - + if (compute_outer) { // 4 offset for collapsed node connection points left_offset = 4; @@ -3901,7 +3849,7 @@ // 5 offset for bottom shadow and collapsed node connection points bottom_offset = 5 + top_offset; } - + out[0] = nodePos[0] - left_offset; out[1] = nodePos[1] - LiteGraph.NODE_TITLE_HEIGHT - top_offset; out[2] = isCollapsed ? @@ -3932,7 +3880,7 @@ margin_top = 0; } if (this.flags && this.flags.collapsed) { - //if ( distance([x,y], [this.pos[0] + this.size[0]*0.5, this.pos[1] + this.size[1]*0.5]) < LiteGraph.NODE_COLLAPSED_RADIUS) + // if ( distance([x,y], [this.pos[0] + this.size[0]*0.5, this.pos[1] + this.size[1]*0.5]) < LiteGraph.NODE_COLLAPSED_RADIUS) if ( isInsideRectangle( x, @@ -3941,7 +3889,7 @@ this.pos[1] - LiteGraph.NODE_TITLE_HEIGHT - margin, (this._collapsed_width || LiteGraph.NODE_COLLAPSED_WIDTH) + 2 * margin, - LiteGraph.NODE_TITLE_HEIGHT + 2 * margin + LiteGraph.NODE_TITLE_HEIGHT + 2 * margin, ) ) { return true; @@ -3965,7 +3913,7 @@ * @return {Object} if found the object contains { input|output: slot object, slot: number, link_pos: [x,y] } */ LGraphNode.prototype.getSlotInPosition = function(x, y) { - //search for inputs + // search for inputs var link_pos = new Float32Array(2); if (this.inputs) { for (var i = 0, l = this.inputs.length; i < l; ++i) { @@ -3978,7 +3926,7 @@ link_pos[0] - 10, link_pos[1] - 5, 20, - 10 + 10, ) ) { return { input: input, slot: i, link_pos: link_pos }; @@ -3997,7 +3945,7 @@ link_pos[0] - 10, link_pos[1] - 5, 20, - 10 + 10, ) ) { return { output: output, slot: i, link_pos: link_pos }; @@ -4015,7 +3963,7 @@ * @param {boolean} returnObj if the obj itself wanted * @return {number_or_object} the slot (-1 if not found) */ - LGraphNode.prototype.findInputSlot = function(name, returnObj) { + LGraphNode.prototype.findInputSlot = function(name, returnObj) { if (!this.inputs) { return -1; } @@ -4046,9 +3994,9 @@ } return -1; }; - + // TODO refactor: USE SINGLE findInput/findOutput functions! :: merge options - + /** * returns the first free input slot * @method findInputSlotFree @@ -4057,9 +4005,10 @@ */ LGraphNode.prototype.findInputSlotFree = function(optsIn) { var optsIn = optsIn || {}; - var optsDef = {returnObj: false - ,typesNotAccepted: [] - }; + var optsDef = { + returnObj: false, + typesNotAccepted: [], + }; var opts = Object.assign(optsDef,optsIn); if (!this.inputs) { return -1; @@ -4068,7 +4017,7 @@ if (this.inputs[i].link && this.inputs[i].link != null) { continue; } - if (opts.typesNotAccepted && opts.typesNotAccepted.includes && opts.typesNotAccepted.includes(this.inputs[i].type)){ + if (opts.typesNotAccepted && opts.typesNotAccepted.includes && opts.typesNotAccepted.includes(this.inputs[i].type)) { continue; } return !opts.returnObj ? i : this.inputs[i]; @@ -4084,9 +4033,10 @@ */ LGraphNode.prototype.findOutputSlotFree = function(optsIn) { var optsIn = optsIn || {}; - var optsDef = { returnObj: false - ,typesNotAccepted: [] - }; + var optsDef = { + returnObj: false, + typesNotAccepted: [], + }; var opts = Object.assign(optsDef,optsIn); if (!this.outputs) { return -1; @@ -4095,14 +4045,14 @@ if (this.outputs[i].links && this.outputs[i].links != null) { continue; } - if (opts.typesNotAccepted && opts.typesNotAccepted.includes && opts.typesNotAccepted.includes(this.outputs[i].type)){ + if (opts.typesNotAccepted && opts.typesNotAccepted.includes && opts.typesNotAccepted.includes(this.outputs[i].type)) { continue; } return !opts.returnObj ? i : this.outputs[i]; } return -1; }; - + /** * findSlotByType for INPUTS */ @@ -4116,7 +4066,7 @@ LGraphNode.prototype.findOutputSlotByType = function(type, returnObj, preferFreeSlot, doNotUseOccupied) { return this.findSlotByType(false, type, returnObj, preferFreeSlot, doNotUseOccupied); }; - + /** * returns the output (or input) slot with a given type, -1 if not found * @method findSlotByType @@ -4135,20 +4085,20 @@ if (!aSlots) { return -1; } - // !! empty string type is considered 0, * !! - if (type == "" || type == "*") type = 0; + // !! empty string type is considered 0, * !! + if (type == "" || type == "*") type = 0; for (var i = 0, l = aSlots.length; i < l; ++i) { var tFound = false; var aSource = (type+"").toLowerCase().split(","); var aDest = aSlots[i].type=="0"||aSlots[i].type=="*"?"0":aSlots[i].type; - aDest = (aDest+"").toLowerCase().split(","); - for(var sI=0;sI= 0 && target_slot !== null){ - //console.debug("CONNbyTYPE type "+target_slotType+" for "+target_slot) + if (target_slot >= 0 && target_slot !== null) { + // console.debug("CONNbyTYPE type "+target_slotType+" for "+target_slot) return this.connect(slot, target_node, target_slot); }else{ - //console.log("type "+target_slotType+" not found or not free?") - if (opts.createEventInCase && target_slotType == LiteGraph.EVENT){ + // console.log("type "+target_slotType+" not found or not free?") + if (opts.createEventInCase && target_slotType == LiteGraph.EVENT) { // WILL CREATE THE onTrigger IN SLOT - //console.debug("connect WILL CREATE THE onTrigger "+target_slotType+" to "+target_node); + // console.debug("connect WILL CREATE THE onTrigger "+target_slotType+" to "+target_node); return this.connect(slot, target_node, -1); } - // connect to the first general output slot if not found a specific type and - if (opts.generalTypeInCase){ + // connect to the first general output slot if not found a specific type and + if (opts.generalTypeInCase) { var target_slot = target_node.findInputSlotByType(0, false, true, true); - //console.debug("connect TO a general type (*, 0), if not found the specific type ",target_slotType," to ",target_node,"RES_SLOT:",target_slot); - if (target_slot >= 0){ + // console.debug("connect TO a general type (*, 0), if not found the specific type ",target_slotType," to ",target_node,"RES_SLOT:",target_slot); + if (target_slot >= 0) { return this.connect(slot, target_node, target_slot); } } // connect to the first free input slot if not found a specific type and this output is general - if (opts.firstFreeIfOutputGeneralInCase && (target_slotType == 0 || target_slotType == "*" || target_slotType == "")){ + if (opts.firstFreeIfOutputGeneralInCase && (target_slotType == 0 || target_slotType == "*" || target_slotType == "")) { var target_slot = target_node.findInputSlotFree({typesNotAccepted: [LiteGraph.EVENT] }); - //console.debug("connect TO TheFirstFREE ",target_slotType," to ",target_node,"RES_SLOT:",target_slot); - if (target_slot >= 0){ - return this.connect(slot, target_node, target_slot); + // console.debug("connect TO TheFirstFREE ",target_slotType," to ",target_node,"RES_SLOT:",target_slot); + if (target_slot >= 0) { + return this.connect(slot, target_node, target_slot); } } - - console.debug("no way to connect type: ",target_slotType," to targetNODE ",target_node); - //TODO filter - + + console.debug("no way to connect type: ",target_slotType," to targetNODE ",target_node); + // TODO filter + return null; } } - + /** * connect this node input to the output of another node BY TYPE * @method connectByType @@ -4239,51 +4190,52 @@ */ LGraphNode.prototype.connectByTypeOutput = function(slot, source_node, source_slotType, optsIn) { var optsIn = optsIn || {}; - var optsDef = { createEventInCase: true - ,firstFreeIfInputGeneralInCase: true - ,generalTypeInCase: true - }; + var optsDef = { + createEventInCase: true, + firstFreeIfInputGeneralInCase: true, + generalTypeInCase: true, + }; var opts = Object.assign(optsDef,optsIn); if (source_node && source_node.constructor === Number) { source_node = this.graph.getNodeById(source_node); } var source_slot = source_node.findOutputSlotByType(source_slotType, false, true); - if (source_slot >= 0 && source_slot !== null){ - //console.debug("CONNbyTYPE OUT! type "+source_slotType+" for "+source_slot) + if (source_slot >= 0 && source_slot !== null) { + // console.debug("CONNbyTYPE OUT! type "+source_slotType+" for "+source_slot) return source_node.connect(source_slot, this, slot); }else{ - - // connect to the first general output slot if not found a specific type and - if (opts.generalTypeInCase){ + + // connect to the first general output slot if not found a specific type and + if (opts.generalTypeInCase) { var source_slot = source_node.findOutputSlotByType(0, false, true, true); - if (source_slot >= 0){ + if (source_slot >= 0) { return source_node.connect(source_slot, this, slot); } } - - if (opts.createEventInCase && source_slotType == LiteGraph.EVENT){ + + if (opts.createEventInCase && source_slotType == LiteGraph.EVENT) { // WILL CREATE THE onExecuted OUT SLOT - if (LiteGraph.do_add_triggers_slots){ - var source_slot = source_node.addOnExecutedOutput(); - return source_node.connect(source_slot, this, slot); - } + if (LiteGraph.do_add_triggers_slots) { + var source_slot = source_node.addOnExecutedOutput(); + return source_node.connect(source_slot, this, slot); + } } // connect to the first free output slot if not found a specific type and this input is general - if (opts.firstFreeIfInputGeneralInCase && (source_slotType == 0 || source_slotType == "*" || source_slotType == "")){ + if (opts.firstFreeIfInputGeneralInCase && (source_slotType == 0 || source_slotType == "*" || source_slotType == "")) { var source_slot = source_node.findOutputSlotFree({typesNotAccepted: [LiteGraph.EVENT] }); - if (source_slot >= 0){ + if (source_slot >= 0) { return source_node.connect(source_slot, this, slot); } } - - console.debug("no way to connect byOUT type: ",source_slotType," to sourceNODE ",source_node); - //TODO filter - - //console.log("type OUT! "+source_slotType+" not found or not free?") + + console.debug("no way to connect byOUT type: ",source_slotType," to sourceNODE ",source_node); + // TODO filter + + // console.log("type OUT! "+source_slotType+" not found or not free?") return null; } } - + /** * connect this node output to the input of another node * @method connect @@ -4296,14 +4248,12 @@ target_slot = target_slot || 0; if (!this.graph) { - //could be connected before adding it to a graph - console.log( - "Connect: Error, node doesn't belong to any graph. Nodes must be added first to a graph before connecting them." - ); //due to link ids being associated with graphs + // could be connected before adding it to a graph + console.log("Connect: Error, node doesn't belong to any graph. Nodes must be added first to a graph before connecting them."); // due to link ids being associated with graphs return null; } - //seek for the output slot + // seek for the output slot if (slot.constructor === String) { slot = this.findOutputSlot(slot); if (slot == -1) { @@ -4326,33 +4276,31 @@ throw "target node is null"; } - //avoid loopback + // avoid loopback if (target_node == this) { return null; } - //you can specify the slot by name + // you can specify the slot by name if (target_slot.constructor === String) { target_slot = target_node.findInputSlot(target_slot); if (target_slot == -1) { if (LiteGraph.debug) { - console.log( - "Connect: Error, no slot of name " + target_slot - ); + console.log("Connect: Error, no slot of name " + target_slot); } return null; } } else if (target_slot === LiteGraph.EVENT) { - - if (LiteGraph.do_add_triggers_slots){ - //search for first slot with event? :: NO this is done outside - //console.log("Connect: Creating triggerEvent"); - // force mode - target_node.changeMode(LiteGraph.ON_TRIGGER); - target_slot = target_node.findInputSlot("onTrigger"); - }else{ - return null; // -- break -- - } + + if (LiteGraph.do_add_triggers_slots) { + // search for first slot with event? :: NO this is done outside + // console.log("Connect: Creating triggerEvent"); + // force mode + target_node.changeMode(LiteGraph.ON_TRIGGER); + target_slot = target_node.findInputSlot("onTrigger"); + } else { + return null; // -- break -- + } } else if ( !target_node.inputs || target_slot >= target_node.inputs.length @@ -4363,14 +4311,14 @@ return null; } - var changed = false; + var changed = false; var input = target_node.inputs[target_slot]; var link_info = null; var output = this.outputs[slot]; - - if (!this.outputs[slot]){ - /*console.debug("Invalid slot passed: "+slot); + + if (!this.outputs[slot]) { + /* console.debug("Invalid slot passed: "+slot); console.debug(this.outputs);*/ return null; } @@ -4378,21 +4326,20 @@ // allow target node to change slot if (target_node.onBeforeConnectInput) { // This way node can choose another slot (or make a new one?) - target_slot = target_node.onBeforeConnectInput(target_slot); //callback + target_slot = target_node.onBeforeConnectInput(target_slot); // callback } - //check target_slot and check connection types - if (target_slot===false || target_slot===null || !LiteGraph.isValidConnection(output.type, input.type)) - { - this.setDirtyCanvas(false, true); - if(changed) - this.graph.connectionChange(this, link_info); - return null; - }else{ - //console.debug("valid connection",output.type, input.type); - } + // check target_slot and check connection types + if (target_slot===false || target_slot===null || !LiteGraph.isValidConnection(output.type, input.type)) { + this.setDirtyCanvas(false, true); + if(changed) + this.graph.connectionChange(this, link_info); + return null; + }else{ + // console.debug("valid connection",output.type, input.type); + } - //allows nodes to block connection, callback + // allows nodes to block connection, callback if (target_node.onConnectInput) { if ( target_node.onConnectInput(target_slot, output.type, output, this, slot) === false ) { return null; @@ -4404,23 +4351,23 @@ } } - //if there is something already plugged there, disconnect + // if there is something already plugged there, disconnect if (target_node.inputs[target_slot] && target_node.inputs[target_slot].link != null) { - this.graph.beforeChange(); + this.graph.beforeChange(); target_node.disconnectInput(target_slot, {doProcessChange: false}); - changed = true; + changed = true; } - if (output.links !== null && output.links.length){ - switch(output.type){ + if (output.links !== null && output.links.length) { + switch(output.type) { case LiteGraph.EVENT: - if (!LiteGraph.allow_multi_output_for_events){ + if (!LiteGraph.allow_multi_output_for_events) { this.graph.beforeChange(); this.disconnectOutput(slot, false, {doProcessChange: false}); // Input(target_slot, {doProcessChange: false}); changed = true; } - break; + break; default: - break; + break; } } @@ -4429,68 +4376,68 @@ nextId = LiteGraph.uuidv4(); else nextId = ++this.graph.last_link_id; - - //create link class - link_info = new LLink( - nextId, - input.type || output.type, - this.id, - slot, - target_node.id, - target_slot - ); - - //add to graph links list - this.graph.links[link_info.id] = link_info; - - //connect in output - if (output.links == null) { - output.links = []; - } - output.links.push(link_info.id); - //connect in input - target_node.inputs[target_slot].link = link_info.id; - if (this.graph) { - this.graph._version++; - } - if (this.onConnectionsChange) { - this.onConnectionsChange( - LiteGraph.OUTPUT, - slot, - true, - link_info, - output - ); - } //link_info has been created now, so its updated - if (target_node.onConnectionsChange) { - target_node.onConnectionsChange( - LiteGraph.INPUT, - target_slot, - true, - link_info, - input - ); - } - if (this.graph && this.graph.onNodeConnectionChange) { - this.graph.onNodeConnectionChange( - LiteGraph.INPUT, - target_node, - target_slot, - this, - slot - ); - this.graph.onNodeConnectionChange( - LiteGraph.OUTPUT, - this, - slot, - target_node, - target_slot - ); - } + + // create link class + link_info = new LLink( + nextId, + input.type || output.type, + this.id, + slot, + target_node.id, + target_slot, + ); + + // add to graph links list + this.graph.links[link_info.id] = link_info; + + // connect in output + if (output.links == null) { + output.links = []; + } + output.links.push(link_info.id); + // connect in input + target_node.inputs[target_slot].link = link_info.id; + if (this.graph) { + this.graph._version++; + } + if (this.onConnectionsChange) { + this.onConnectionsChange( + LiteGraph.OUTPUT, + slot, + true, + link_info, + output, + ); + } // link_info has been created now, so its updated + if (target_node.onConnectionsChange) { + target_node.onConnectionsChange( + LiteGraph.INPUT, + target_slot, + true, + link_info, + input, + ); + } + if (this.graph && this.graph.onNodeConnectionChange) { + this.graph.onNodeConnectionChange( + LiteGraph.INPUT, + target_node, + target_slot, + this, + slot, + ); + this.graph.onNodeConnectionChange( + LiteGraph.OUTPUT, + this, + slot, + target_node, + target_slot, + ); + } this.setDirtyCanvas(false, true); - this.graph.afterChange(); - this.graph.connectionChange(this, link_info); + this.graph.afterChange(); + this.graph.connectionChange(this, link_info); return link_info; }; @@ -4518,13 +4465,13 @@ return false; } - //get output slot + // get output slot var output = this.outputs[slot]; if (!output || !output.links || output.links.length == 0) { return false; } - //one of the output links in this slot + // one of the output links in this slot if (target_node) { if (target_node.constructor === Number) { target_node = this.graph.getNodeById(target_node); @@ -4537,12 +4484,12 @@ var link_id = output.links[i]; var link_info = this.graph.links[link_id]; - //is the link we are searching for... + // is the link we are searching for... if (link_info.target_id == target_node.id) { - output.links.splice(i, 1); //remove here + output.links.splice(i, 1); // remove here var input = target_node.inputs[link_info.target_slot]; - input.link = null; //remove there - delete this.graph.links[link_id]; //remove the link from the links pool + input.link = null; // remove there + delete this.graph.links[link_id]; // remove the link from the links pool if (this.graph) { this.graph._version++; } @@ -4552,47 +4499,46 @@ link_info.target_slot, false, link_info, - input + input, ); - } //link_info hasn't been modified so its ok + } // link_info hasn't been modified so its ok if (this.onConnectionsChange) { this.onConnectionsChange( LiteGraph.OUTPUT, slot, false, link_info, - output + output, ); } if (this.graph && this.graph.onNodeConnectionChange) { this.graph.onNodeConnectionChange( LiteGraph.OUTPUT, this, - slot + slot, ); } if (this.graph && this.graph.onNodeConnectionChange) { this.graph.onNodeConnectionChange( LiteGraph.OUTPUT, this, - slot + slot, ); this.graph.onNodeConnectionChange( LiteGraph.INPUT, target_node, - link_info.target_slot + link_info.target_slot, ); } break; } } - } //all the links in this output slot - else { + } else { // all the links in this output slot for (var i = 0, l = output.links.length; i < l; i++) { var link_id = output.links[i]; var link_info = this.graph.links[link_id]; if (!link_info) { - //bug: it happens sometimes + // bug: it happens sometimes continue; } @@ -4603,44 +4549,44 @@ } if (target_node) { input = target_node.inputs[link_info.target_slot]; - input.link = null; //remove other side link + input.link = null; // remove other side link if (target_node.onConnectionsChange) { target_node.onConnectionsChange( LiteGraph.INPUT, link_info.target_slot, false, link_info, - input + input, ); - } //link_info hasn't been modified so its ok + } // link_info hasn't been modified so its ok if (this.graph && this.graph.onNodeConnectionChange) { this.graph.onNodeConnectionChange( LiteGraph.INPUT, target_node, - link_info.target_slot + link_info.target_slot, ); } } - delete this.graph.links[link_id]; //remove the link from the links pool + delete this.graph.links[link_id]; // remove the link from the links pool if (this.onConnectionsChange) { this.onConnectionsChange( LiteGraph.OUTPUT, slot, false, link_info, - output + output, ); } if (this.graph && this.graph.onNodeConnectionChange) { this.graph.onNodeConnectionChange( LiteGraph.OUTPUT, this, - slot + slot, ); this.graph.onNodeConnectionChange( LiteGraph.INPUT, target_node, - link_info.target_slot + link_info.target_slot, ); } } @@ -4659,7 +4605,7 @@ * @return {boolean} if it was disconnected successfully */ LGraphNode.prototype.disconnectInput = function(slot) { - //seek for the output slot + // seek for the output slot if (slot.constructor === String) { slot = this.findInputSlot(slot); if (slot == -1) { @@ -4681,96 +4627,95 @@ } var link_id = this.inputs[slot].link; - if(link_id != null) - { - this.inputs[slot].link = null; - - //remove other side - var link_info = this.graph.links[link_id]; - if (link_info) { - var target_node = this.graph.getNodeById(link_info.origin_id); - if (!target_node) { - return false; - } - - var output = target_node.outputs[link_info.origin_slot]; - if (!output || !output.links || output.links.length == 0) { - return false; - } - - //search in the inputs list for this link - for (var i = 0, l = output.links.length; i < l; i++) { - if (output.links[i] == link_id) { - output.links.splice(i, 1); - break; - } - } - - delete this.graph.links[link_id]; //remove from the pool - if (this.graph) { - this.graph._version++; - } - if (this.onConnectionsChange) { - this.onConnectionsChange( - LiteGraph.INPUT, - slot, - false, - link_info, - input - ); - } - if (target_node.onConnectionsChange) { - target_node.onConnectionsChange( - LiteGraph.OUTPUT, - i, - false, - link_info, - output - ); - } - if (this.graph && this.graph.onNodeConnectionChange) { - this.graph.onNodeConnectionChange( - LiteGraph.OUTPUT, - target_node, - i - ); - this.graph.onNodeConnectionChange(LiteGraph.INPUT, this, slot); - } - } - } //link != null - - this.setDirtyCanvas(false, true); - if(this.graph) - this.graph.connectionChange(this); - return true; - }; + if(link_id != null) { + this.inputs[slot].link = null; + + // remove other side + var link_info = this.graph.links[link_id]; + if (link_info) { + var target_node = this.graph.getNodeById(link_info.origin_id); + if (!target_node) { + return false; + } - /** - * returns the center of a connection point in canvas coords - * @method getConnectionPos - * @param {boolean} is_input true if if a input slot, false if it is an output - * @param {number_or_string} slot (could be the number of the slot or the string with the name of the slot) - * @param {vec2} out [optional] a place to store the output, to free garbage - * @return {[x,y]} the position - **/ - LGraphNode.prototype.getConnectionPos = function( - is_input, - slot_number, - out - ) { - out = out || new Float32Array(2); - var num_slots = 0; - if (is_input && this.inputs) { - num_slots = this.inputs.length; - } - if (!is_input && this.outputs) { - num_slots = this.outputs.length; - } + var output = target_node.outputs[link_info.origin_slot]; + if (!output || !output.links || output.links.length == 0) { + return false; + } - var offset = LiteGraph.NODE_SLOT_HEIGHT * 0.5; + // search in the inputs list for this link + for (var i = 0, l = output.links.length; i < l; i++) { + if (output.links[i] == link_id) { + output.links.splice(i, 1); + break; + } + } - if (this.flags.collapsed) { - var w = this._collapsed_width || LiteGraph.NODE_COLLAPSED_WIDTH; + delete this.graph.links[link_id]; // remove from the pool + if (this.graph) { + this.graph._version++; + } + if (this.onConnectionsChange) { + this.onConnectionsChange( + LiteGraph.INPUT, + slot, + false, + link_info, + input, + ); + } + if (target_node.onConnectionsChange) { + target_node.onConnectionsChange( + LiteGraph.OUTPUT, + i, + false, + link_info, + output, + ); + } + if (this.graph && this.graph.onNodeConnectionChange) { + this.graph.onNodeConnectionChange( + LiteGraph.OUTPUT, + target_node, + i, + ); + this.graph.onNodeConnectionChange(LiteGraph.INPUT, this, slot); + } + } + } // link != null + + this.setDirtyCanvas(false, true); + if(this.graph) + this.graph.connectionChange(this); + return true; + }; + + /** + * returns the center of a connection point in canvas coords + * @method getConnectionPos + * @param {boolean} is_input true if if a input slot, false if it is an output + * @param {number_or_string} slot (could be the number of the slot or the string with the name of the slot) + * @param {vec2} out [optional] a place to store the output, to free garbage + * @return {[x,y]} the position + **/ + LGraphNode.prototype.getConnectionPos = function( + is_input, + slot_number, + out, + ) { + out = out || new Float32Array(2); + var num_slots = 0; + if (is_input && this.inputs) { + num_slots = this.inputs.length; + } + if (!is_input && this.outputs) { + num_slots = this.outputs.length; + } + + var offset = LiteGraph.NODE_SLOT_HEIGHT * 0.5; + + if (this.flags.collapsed) { + var w = this._collapsed_width || LiteGraph.NODE_COLLAPSED_WIDTH; if (this.horizontal) { out[0] = this.pos[0] + w * 0.5; if (is_input) { @@ -4789,14 +4734,14 @@ return out; } - //weird feature that never got finished + // weird feature that never got finished if (is_input && slot_number == -1) { out[0] = this.pos[0] + LiteGraph.NODE_TITLE_HEIGHT * 0.5; out[1] = this.pos[1] + LiteGraph.NODE_TITLE_HEIGHT * 0.5; return out; } - //hard-coded pos + // hard-coded pos if ( is_input && num_slots > slot_number && @@ -4815,7 +4760,7 @@ return out; } - //horizontal distributed slots + // horizontal distributed slots if (this.horizontal) { out[0] = this.pos[0] + (slot_number + 0.5) * (this.size[0] / num_slots); @@ -4827,7 +4772,7 @@ return out; } - //default vertical slots + // default vertical slots if (is_input) { out[0] = this.pos[0] + offset; } else { @@ -4861,21 +4806,21 @@ this.console.shift(); } - if(this.graph.onNodeTrace) - this.graph.onNodeTrace(this, msg); + if(this.graph.onNodeTrace) + this.graph.onNodeTrace(this, msg); }; /* Forces to redraw or the main canvas (LGraphNode) or the bg canvas (links) */ LGraphNode.prototype.setDirtyCanvas = function( dirty_foreground, - dirty_background + dirty_background, ) { if (!this.graph) { return; } this.graph.sendActionToCanvas("setDirty", [ dirty_foreground, - dirty_background + dirty_background, ]); }; @@ -4892,7 +4837,7 @@ return img; }; - //safe LGraphNode action execution (not sure if safe) + // safe LGraphNode action execution (not sure if safe) /* LGraphNode.prototype.executeAction = function(action) { @@ -4941,12 +4886,12 @@ LGraphNode.prototype.executeAction = function(action) for (var i = 0; i < list.length; ++i) { var c = list[i]; - //releasing somebody elses capture?! + // releasing somebody elses capture?! if (!v && c.node_capturing_input != this) { continue; } - //change + // change c.node_capturing_input = v ? this : null; } }; @@ -4985,7 +4930,7 @@ LGraphNode.prototype.executeAction = function(action) LGraphNode.prototype.localToScreen = function(x, y, graphcanvas) { return [ (x + this.pos[0]) * graphcanvas.scale + graphcanvas.offset[0], - (y + this.pos[1]) * graphcanvas.scale + graphcanvas.offset[1] + (y + this.pos[1]) * graphcanvas.scale + graphcanvas.offset[1], ]; }; @@ -5018,7 +4963,7 @@ LGraphNode.prototype.executeAction = function(action) get: function() { return this._pos; }, - enumerable: true + enumerable: true, }); Object.defineProperty(this, "size", { @@ -5032,7 +4977,7 @@ LGraphNode.prototype.executeAction = function(action) get: function() { return this._size; }, - enumerable: true + enumerable: true, }); }; @@ -5051,10 +4996,10 @@ LGraphNode.prototype.executeAction = function(action) Math.round(b[0]), Math.round(b[1]), Math.round(b[2]), - Math.round(b[3]) + Math.round(b[3]), ], color: this.color, - font_size: this.font_size + font_size: this.font_size, }; }; @@ -5081,7 +5026,7 @@ LGraphNode.prototype.executeAction = function(action) node.getBounding(node_bounding); if (!overlapBounding(this._bounding, node_bounding)) { continue; - } //out of the visible area + } // out of the visible area this._nodes.push(node); } }; @@ -5089,9 +5034,9 @@ LGraphNode.prototype.executeAction = function(action) LGraphGroup.prototype.isPointInside = LGraphNode.prototype.isPointInside; LGraphGroup.prototype.setDirtyCanvas = LGraphNode.prototype.setDirtyCanvas; - //**************************************** + //* *************************************** - //Scale and Offset + // Scale and Offset function DragAndScale(element, skip_events) { this.offset = new Float32Array([0, 0]); this.scale = 1; @@ -5118,14 +5063,14 @@ LGraphNode.prototype.executeAction = function(action) this._binded_mouse_callback = this.onMouse.bind(this); - LiteGraph.pointerListenerAdd(element,"down", this._binded_mouse_callback); - LiteGraph.pointerListenerAdd(element,"move", this._binded_mouse_callback); - LiteGraph.pointerListenerAdd(element,"up", this._binded_mouse_callback); + LiteGraph.pointerListenerAdd(element,"down", this._binded_mouse_callback); + LiteGraph.pointerListenerAdd(element,"move", this._binded_mouse_callback); + LiteGraph.pointerListenerAdd(element,"up", this._binded_mouse_callback); element.addEventListener( "mousewheel", this._binded_mouse_callback, - false + false, ); element.addEventListener("wheel", this._binded_mouse_callback, false); }; @@ -5139,13 +5084,12 @@ LGraphNode.prototype.executeAction = function(action) var height = this.element.height; var startx = -this.offset[0]; var starty = -this.offset[1]; - if( viewport ) - { - startx += viewport[0] / this.scale; - starty += viewport[1] / this.scale; - width = viewport[2]; - height = viewport[3]; - } + if( viewport ) { + startx += viewport[0] / this.scale; + starty += viewport[1] / this.scale; + width = viewport[2]; + height = viewport[3]; + } var endx = startx + width / this.scale; var endy = starty + height / this.scale; this.visible_area[0] = startx; @@ -5166,11 +5110,11 @@ LGraphNode.prototype.executeAction = function(action) e.canvasx = x; e.canvasy = y; e.dragging = this.dragging; - - var is_inside = !this.viewport || ( this.viewport && x >= this.viewport[0] && x < (this.viewport[0] + this.viewport[2]) && y >= this.viewport[1] && y < (this.viewport[1] + this.viewport[3]) ); - //console.log("pointerevents: DragAndScale onMouse "+e.type+" "+is_inside); - + var is_inside = !this.viewport || ( this.viewport && x >= this.viewport[0] && x < (this.viewport[0] + this.viewport[2]) && y >= this.viewport[1] && y < (this.viewport[1] + this.viewport[3]) ); + + // console.log("pointerevents: DragAndScale onMouse "+e.type+" "+is_inside); + var ignore = false; if (this.onmouse) { ignore = this.onmouse(e); @@ -5178,9 +5122,9 @@ LGraphNode.prototype.executeAction = function(action) if (e.type == LiteGraph.pointerevents_method+"down" && is_inside) { this.dragging = true; - LiteGraph.pointerListenerRemove(canvas,"move",this._binded_mouse_callback); - LiteGraph.pointerListenerAdd(document,"move",this._binded_mouse_callback); - LiteGraph.pointerListenerAdd(document,"up",this._binded_mouse_callback); + LiteGraph.pointerListenerRemove(canvas,"move",this._binded_mouse_callback); + LiteGraph.pointerListenerAdd(document,"move",this._binded_mouse_callback); + LiteGraph.pointerListenerAdd(document,"up",this._binded_mouse_callback); } else if (e.type == LiteGraph.pointerevents_method+"move") { if (!ignore) { var deltax = x - this.last_mouse[0]; @@ -5191,9 +5135,9 @@ LGraphNode.prototype.executeAction = function(action) } } else if (e.type == LiteGraph.pointerevents_method+"up") { this.dragging = false; - LiteGraph.pointerListenerRemove(document,"move",this._binded_mouse_callback); - LiteGraph.pointerListenerRemove(document,"up",this._binded_mouse_callback); - LiteGraph.pointerListenerAdd(canvas,"move",this._binded_mouse_callback); + LiteGraph.pointerListenerRemove(document,"move",this._binded_mouse_callback); + LiteGraph.pointerListenerRemove(document,"up",this._binded_mouse_callback); + LiteGraph.pointerListenerAdd(canvas,"move",this._binded_mouse_callback); } else if ( is_inside && (e.type == "mousewheel" || e.type == "wheel" || @@ -5207,24 +5151,23 @@ LGraphNode.prototype.executeAction = function(action) e.wheelDeltaY != null ? e.wheelDeltaY : e.detail * -60; } - //from stack overflow + // from stack overflow e.delta = e.wheelDelta ? e.wheelDelta / 40 : e.deltaY - ? -e.deltaY / 3 - : 0; + ? -e.deltaY / 3 + : 0; this.changeDeltaScale(1.0 + e.delta * 0.05); } this.last_mouse[0] = x; this.last_mouse[1] = y; - if(is_inside) - { - e.preventDefault(); - e.stopPropagation(); - return false; - } + if(is_inside) { + e.preventDefault(); + e.stopPropagation(); + return false; + } }; DragAndScale.prototype.toCanvasContext = function(ctx) { @@ -5233,10 +5176,10 @@ LGraphNode.prototype.executeAction = function(action) }; DragAndScale.prototype.convertOffsetToCanvas = function(pos) { - //return [pos[0] / this.scale - this.offset[0], pos[1] / this.scale - this.offset[1]]; + // return [pos[0] / this.scale - this.offset[0], pos[1] / this.scale - this.offset[1]]; return [ (pos[0] + this.offset[0]) * this.scale, - (pos[1] + this.offset[1]) * this.scale + (pos[1] + this.offset[1]) * this.scale, ]; }; @@ -5278,7 +5221,7 @@ LGraphNode.prototype.executeAction = function(action) zooming_center = zooming_center || [ rect.width * 0.5, - rect.height * 0.5 + rect.height * 0.5, ]; var center = this.convertCanvasToOffset(zooming_center); this.scale = value; @@ -5289,7 +5232,7 @@ LGraphNode.prototype.executeAction = function(action) var new_center = this.convertCanvasToOffset(zooming_center); var delta_offset = [ new_center[0] - center[0], - new_center[1] - center[1] + new_center[1] - center[1], ]; this.offset[0] += delta_offset[0]; @@ -5310,9 +5253,9 @@ LGraphNode.prototype.executeAction = function(action) this.offset[1] = 0; }; - //********************************************************************************* + //* ******************************************************************************** // LGraphCanvas: LGraph renderer CLASS - //********************************************************************************* + //* ******************************************************************************** /** * This class is in charge of rendering one graph inside a canvas. And provides all the interaction required. @@ -5327,7 +5270,7 @@ LGraphNode.prototype.executeAction = function(action) function LGraphCanvas(canvas, graph, options) { this.options = options = options || {}; - //if(graph === undefined) + // if(graph === undefined) // throw ("No graph assigned"); this.background_image = LGraphCanvas.DEFAULT_BACKGROUND_IMAGE; @@ -5336,7 +5279,7 @@ LGraphNode.prototype.executeAction = function(action) } this.ds = new DragAndScale(); - this.zoom_modify_alpha = true; //otherwise it generates ugly patterns when scaling down too much + this.zoom_modify_alpha = true; // otherwise it generates ugly patterns when scaling down too much this.title_text_font = "" + LiteGraph.NODE_TEXT_SIZE + "px Arial"; this.inner_text_font = @@ -5345,93 +5288,92 @@ LGraphNode.prototype.executeAction = function(action) this.default_link_color = LiteGraph.LINK_COLOR; this.default_connection_color = { input_off: "#778", - input_on: "#7F7", //"#BBD" + input_on: "#7F7", // "#BBD" output_off: "#778", - output_on: "#7F7" //"#BBD" - }; - this.default_connection_color_byType = { - /*number: "#7F7", + output_on: "#7F7", // "#BBD" + }; + /* number: "#7F7", string: "#77F", boolean: "#F77",*/ - } - this.default_connection_color_byTypeOff = { - /*number: "#474", + this.default_connection_color_byType = {}; + + /* number: "#474", string: "#447", boolean: "#744",*/ - }; + this.default_connection_color_byTypeOff = {}; this.highquality_render = true; - this.use_gradients = false; //set to true to render titlebar with gradients - this.editor_alpha = 1; //used for transition + this.use_gradients = false; // set to true to render titlebar with gradients + this.editor_alpha = 1; // used for transition this.pause_rendering = false; this.clear_background = true; this.clear_background_color = "#222"; - this.read_only = false; //if set to true users cannot modify the graph + this.read_only = false; // if set to true users cannot modify the graph this.render_only_selected = true; this.live_mode = false; this.show_info = true; this.allow_dragcanvas = true; this.allow_dragnodes = true; - this.allow_interaction = true; //allow to control widgets, buttons, collapse, etc - this.multi_select = false; //allow selecting multi nodes without pressing extra keys + this.allow_interaction = true; // allow to control widgets, buttons, collapse, etc + this.multi_select = false; // allow selecting multi nodes without pressing extra keys this.allow_searchbox = true; - this.allow_reconnect_links = true; //allows to change a connection with having to redo it again - this.align_to_grid = false; //snap to grid + this.allow_reconnect_links = true; // allows to change a connection with having to redo it again + this.align_to_grid = false; // snap to grid this.drag_mode = false; this.dragging_rectangle = null; - this.filter = null; //allows to filter to only accept some type of nodes in a graph + this.filter = null; // allows to filter to only accept some type of nodes in a graph - this.set_canvas_dirty_on_mouse_event = true; //forces to redraw the canvas if the mouse does anything + this.set_canvas_dirty_on_mouse_event = true; // forces to redraw the canvas if the mouse does anything this.always_render_background = false; this.render_shadows = true; this.render_canvas_border = true; - this.render_connections_shadows = false; //too much cpu + this.render_connections_shadows = false; // too much cpu this.render_connections_border = true; this.render_curved_connections = false; this.render_connection_arrows = false; this.render_collapsed_slots = true; this.render_execution_order = false; this.render_title_colored = true; - this.render_link_tooltip = true; + this.render_link_tooltip = true; this.links_render_mode = LiteGraph.SPLINE_LINK; - this.mouse = [0, 0]; //mouse in canvas coordinates, where 0,0 is the top-left corner of the blue rectangle - this.graph_mouse = [0, 0]; //mouse in graph coordinates, where 0,0 is the top-left corner of the blue rectangle - this.canvas_mouse = this.graph_mouse; //LEGACY: REMOVE THIS, USE GRAPH_MOUSE INSTEAD + this.mouse = [0, 0]; // mouse in canvas coordinates, where 0,0 is the top-left corner of the blue rectangle + this.graph_mouse = [0, 0]; // mouse in graph coordinates, where 0,0 is the top-left corner of the blue rectangle + this.canvas_mouse = this.graph_mouse; // LEGACY: REMOVE THIS, USE GRAPH_MOUSE INSTEAD - //to personalize the search box + // to personalize the search box this.onSearchBox = null; this.onSearchBoxSelection = null; - //callbacks + // callbacks this.onMouse = null; - this.onDrawBackground = null; //to render background objects (behind nodes and connections) in the canvas affected by transform - this.onDrawForeground = null; //to render foreground objects (above nodes and connections) in the canvas affected by transform - this.onDrawOverlay = null; //to render foreground objects not affected by transform (for GUIs) - this.onDrawLinkTooltip = null; //called when rendering a tooltip - this.onNodeMoved = null; //called after moving a node - this.onSelectionChange = null; //called if the selection changes - this.onConnectingChange = null; //called before any link changes - this.onBeforeChange = null; //called before modifying the graph - this.onAfterChange = null; //called after modifying the graph + this.onDrawBackground = null; // to render background objects (behind nodes and connections) in the canvas affected by transform + this.onDrawForeground = null; // to render foreground objects (above nodes and connections) in the canvas affected by transform + this.onDrawOverlay = null; // to render foreground objects not affected by transform (for GUIs) + this.onDrawLinkTooltip = null; // called when rendering a tooltip + this.onNodeMoved = null; // called after moving a node + this.onSelectionChange = null; // called if the selection changes + this.onConnectingChange = null; // called before any link changes + this.onBeforeChange = null; // called before modifying the graph + this.onAfterChange = null; // called after modifying the graph this.connections_width = 3; this.round_radius = 8; this.current_node = null; - this.node_widget = null; //used for widgets - this.over_link_center = null; + this.node_widget = null; // used for widgets + this.over_link_center = null; this.last_mouse_position = [0, 0]; this.visible_area = this.ds.visible_area; this.visible_links = []; - this.viewport = options.viewport || null; //to constraint render area to a portion of the canvas + this.viewport = options.viewport || null; // to constraint render area to a portion of the canvas - //link canvas and graph + // link canvas and graph if (graph) { graph.attachCanvas(this); } @@ -5448,14 +5390,14 @@ LGraphNode.prototype.executeAction = function(action) global.LGraphCanvas = LiteGraph.LGraphCanvas = LGraphCanvas; - LGraphCanvas.DEFAULT_BACKGROUND_IMAGE = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAIAAAD/gAIDAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAQBJREFUeNrs1rEKwjAUhlETUkj3vP9rdmr1Ysammk2w5wdxuLgcMHyptfawuZX4pJSWZTnfnu/lnIe/jNNxHHGNn//HNbbv+4dr6V+11uF527arU7+u63qfa/bnmh8sWLBgwYJlqRf8MEptXPBXJXa37BSl3ixYsGDBMliwFLyCV/DeLIMFCxYsWLBMwSt4Be/NggXLYMGCBUvBK3iNruC9WbBgwYJlsGApeAWv4L1ZBgsWLFiwYJmCV/AK3psFC5bBggULloJX8BpdwXuzYMGCBctgwVLwCl7Be7MMFixYsGDBsu8FH1FaSmExVfAxBa/gvVmwYMGCZbBg/W4vAQYA5tRF9QYlv/QAAAAASUVORK5CYII="; + LGraphCanvas.DEFAULT_BACKGROUND_IMAGE = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAIAAAD/gAIDAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAQBJREFUeNrs1rEKwjAUhlETUkj3vP9rdmr1Ysammk2w5wdxuLgcMHyptfawuZX4pJSWZTnfnu/lnIe/jNNxHHGNn//HNbbv+4dr6V+11uF527arU7+u63qfa/bnmh8sWLBgwYJlqRf8MEptXPBXJXa37BSl3ixYsGDBMliwFLyCV/DeLIMFCxYsWLBMwSt4Be/NggXLYMGCBUvBK3iNruC9WbBgwYJlsGApeAWv4L1ZBgsWLFiwYJmCV/AK3psFC5bBggULloJX8BpdwXuzYMGCBctgwVLwCl7Be7MMFixYsGDBsu8FH1FaSmExVfAxBa/gvVmwYMGCZbBg/W4vAQYA5tRF9QYlv/QAAAAASUVORK5CYII="; LGraphCanvas.link_type_colors = { "-1": LiteGraph.EVENT_LINK_COLOR, number: "#AAA", - node: "#DCA" + node: "#DCA", }; - LGraphCanvas.gradients = {}; //cache of gradients + LGraphCanvas.gradients = {}; // cache of gradients /** * clears all the data inside @@ -5468,8 +5410,8 @@ LGraphNode.prototype.executeAction = function(action) this.render_time = 0; this.fps = 0; - //this.scale = 1; - //this.offset = [0,0]; + // this.scale = 1; + // this.offset = [0,0]; this.dragging_rectangle = null; @@ -5483,7 +5425,7 @@ LGraphNode.prototype.executeAction = function(action) this.connecting_node = null; this.highlighted_links = {}; - this.dragging_canvas = false; + this.dragging_canvas = false; this.dirty_canvas = true; this.dirty_bgcanvas = true; @@ -5494,8 +5436,8 @@ LGraphNode.prototype.executeAction = function(action) this.last_mouse = [0, 0]; this.last_mouseclick = 0; - this.pointer_is_down = false; - this.pointer_is_double = false; + this.pointer_is_down = false; + this.pointer_is_double = false; this.visible_area.set([0, 0, 0, 0]); if (this.onClear) { @@ -5525,9 +5467,9 @@ LGraphNode.prototype.executeAction = function(action) graph.attachCanvas(this); - //remove the graph stack in case a subgraph was open - if (this._graph_stack) - this._graph_stack = null; + // remove the graph stack in case a subgraph was open + if (this._graph_stack) + this._graph_stack = null; this.setDirty(true, true); }; @@ -5538,12 +5480,11 @@ LGraphNode.prototype.executeAction = function(action) * @method getTopGraph * @return {LGraph} graph */ - LGraphCanvas.prototype.getTopGraph = function() - { - if(this._graph_stack.length) - return this._graph_stack[0]; - return this.graph; - } + LGraphCanvas.prototype.getTopGraph = function() { + if(this._graph_stack.length) + return this._graph_stack[0]; + return this.graph; + } /** * opens a graph contained inside a node in the current graph @@ -5570,7 +5511,7 @@ LGraphNode.prototype.executeAction = function(action) } graph.attachCanvas(this); - this.checkPanels(); + this.checkPanels(); this.setDirty(true, true); }; @@ -5631,7 +5572,7 @@ LGraphNode.prototype.executeAction = function(action) } if (!canvas && this.canvas) { - //maybe detach events from old_canvas + // maybe detach events from old_canvas if (!skip_events) { this.unbindEvents(); } @@ -5644,12 +5585,12 @@ LGraphNode.prototype.executeAction = function(action) return; } - //this.canvas.tabindex = "1000"; + // this.canvas.tabindex = "1000"; canvas.className += " lgraphcanvas"; canvas.data = this; - canvas.tabindex = "1"; //to allow key events + canvas.tabindex = "1"; // to allow key events - //bg canvas: used for non changing stuff + // bg canvas: used for non changing stuff this.bgcanvas = null; if (!this.bgcanvas) { this.bgcanvas = document.createElement("canvas"); @@ -5668,14 +5609,12 @@ LGraphNode.prototype.executeAction = function(action) var ctx = (this.ctx = canvas.getContext("2d")); if (ctx == null) { if (!canvas.webgl_enabled) { - console.warn( - "This canvas seems to be WebGL, enabling WebGL renderer" - ); + console.warn("This canvas seems to be WebGL, enabling WebGL renderer"); } this.enableWebGL(); } - //input: (move and up could be unbinded) + // input: (move and up could be unbinded) // why here? this._mousemove_callback = this.processMouseMove.bind(this); // why here? this._mouseup_callback = this.processMouseUp.bind(this); @@ -5684,9 +5623,9 @@ LGraphNode.prototype.executeAction = function(action) } }; - //used in some events to capture them + // used in some events to capture them LGraphCanvas.prototype._doNothing = function doNothing(e) { - //console.log("pointerevents: _doNothing "+e.type); + // console.log("pointerevents: _doNothing "+e.type); e.preventDefault(); return false; }; @@ -5705,37 +5644,37 @@ LGraphNode.prototype.executeAction = function(action) return; } - //console.log("pointerevents: bindEvents"); - + // console.log("pointerevents: bindEvents"); + var canvas = this.canvas; var ref_window = this.getCanvasWindow(); - var document = ref_window.document; //hack used when moving canvas between windows + var document = ref_window.document; // hack used when moving canvas between windows this._mousedown_callback = this.processMouseDown.bind(this); this._mousewheel_callback = this.processMouseWheel.bind(this); // why mousemove and mouseup were not binded here? this._mousemove_callback = this.processMouseMove.bind(this); this._mouseup_callback = this.processMouseUp.bind(this); - - //touch events -- TODO IMPLEMENT - //this._touch_callback = this.touchHandler.bind(this); - LiteGraph.pointerListenerAdd(canvas,"down", this._mousedown_callback, true); //down do not need to store the binded + // touch events -- TODO IMPLEMENT + // this._touch_callback = this.touchHandler.bind(this); + + LiteGraph.pointerListenerAdd(canvas,"down", this._mousedown_callback, true); // down do not need to store the binded canvas.addEventListener("mousewheel", this._mousewheel_callback, false); LiteGraph.pointerListenerAdd(canvas,"up", this._mouseup_callback, true); // CHECK: ??? binded or not - LiteGraph.pointerListenerAdd(canvas,"move", this._mousemove_callback); - + LiteGraph.pointerListenerAdd(canvas,"move", this._mousemove_callback); + canvas.addEventListener("contextmenu", this._doNothing); canvas.addEventListener( "DOMMouseScroll", this._mousewheel_callback, - false + false, ); - //touch events -- THIS WAY DOES NOT WORK, finish implementing pointerevents, than clean the touchevents - /*if( 'touchstart' in document.documentElement ) + // touch events -- THIS WAY DOES NOT WORK, finish implementing pointerevents, than clean the touchevents + /* if( 'touchstart' in document.documentElement ) { canvas.addEventListener("touchstart", this._touch_callback, true); canvas.addEventListener("touchmove", this._touch_callback, true); @@ -5743,13 +5682,13 @@ LGraphNode.prototype.executeAction = function(action) canvas.addEventListener("touchcancel", this._touch_callback, true); }*/ - //Keyboard ****************** + // Keyboard ****************** this._key_callback = this.processKey.bind(this); - canvas.setAttribute("tabindex",1); //otherwise key events are ignored + canvas.setAttribute("tabindex",1); // otherwise key events are ignored canvas.addEventListener("keydown", this._key_callback, true); - document.addEventListener("keyup", this._key_callback, true); //in document, otherwise it doesn't fire keyup + document.addEventListener("keyup", this._key_callback, true); // in document, otherwise it doesn't fire keyup - //Dropping Stuff over nodes ************************************ + // Dropping Stuff over nodes ************************************ this._ondrop_callback = this.processDrop.bind(this); canvas.addEventListener("dragover", this._doNothing, false); @@ -5770,21 +5709,21 @@ LGraphNode.prototype.executeAction = function(action) return; } - //console.log("pointerevents: unbindEvents"); - + // console.log("pointerevents: unbindEvents"); + var ref_window = this.getCanvasWindow(); var document = ref_window.document; - LiteGraph.pointerListenerRemove(this.canvas,"move", this._mousedown_callback); + LiteGraph.pointerListenerRemove(this.canvas,"move", this._mousedown_callback); LiteGraph.pointerListenerRemove(this.canvas,"up", this._mousedown_callback); LiteGraph.pointerListenerRemove(this.canvas,"down", this._mousedown_callback); this.canvas.removeEventListener( "mousewheel", - this._mousewheel_callback + this._mousewheel_callback, ); this.canvas.removeEventListener( "DOMMouseScroll", - this._mousewheel_callback + this._mousewheel_callback, ); this.canvas.removeEventListener("keydown", this._key_callback); document.removeEventListener("keyup", this._key_callback); @@ -5792,8 +5731,8 @@ LGraphNode.prototype.executeAction = function(action) this.canvas.removeEventListener("drop", this._ondrop_callback); this.canvas.removeEventListener("dragenter", this._doReturnTrue); - //touch events -- THIS WAY DOES NOT WORK, finish implementing pointerevents, than clean the touchevents - /*this.canvas.removeEventListener("touchstart", this._touch_callback ); + // touch events -- THIS WAY DOES NOT WORK, finish implementing pointerevents, than clean the touchevents + /* this.canvas.removeEventListener("touchstart", this._touch_callback ); this.canvas.removeEventListener("touchmove", this._touch_callback ); this.canvas.removeEventListener("touchend", this._touch_callback ); this.canvas.removeEventListener("touchcancel", this._touch_callback );*/ @@ -5883,7 +5822,7 @@ LGraphNode.prototype.executeAction = function(action) LGraphCanvas.prototype.startRendering = function() { if (this.is_rendering) { return; - } //already rendering + } // already rendering this.is_rendering = true; renderFrame.call(this); @@ -5918,19 +5857,18 @@ LGraphNode.prototype.executeAction = function(action) /* LiteGraphCanvas input */ - //used to block future mouse events (because of im gui) - LGraphCanvas.prototype.blockClick = function() - { - this.block_click = true; - this.last_mouseclick = 0; - } - + // used to block future mouse events (because of im gui) + LGraphCanvas.prototype.blockClick = function() { + this.block_click = true; + this.last_mouseclick = 0; + } + LGraphCanvas.prototype.processMouseDown = function(e) { - - if( this.set_canvas_dirty_on_mouse_event ) - this.dirty_canvas = true; - - if (!this.graph) { + + if( this.set_canvas_dirty_on_mouse_event ) + this.dirty_canvas = true; + + if (!this.graph) { return; } @@ -5941,62 +5879,58 @@ LGraphNode.prototype.executeAction = function(action) LGraphCanvas.active_canvas = this; var that = this; - var x = e.clientX; - var y = e.clientY; - //console.log(y,this.viewport); - //console.log("pointerevents: processMouseDown pointerId:"+e.pointerId+" which:"+e.which+" isPrimary:"+e.isPrimary+" :: x y "+x+" "+y); + var x = e.clientX; + var y = e.clientY; + // console.log(y,this.viewport); + // console.log("pointerevents: processMouseDown pointerId:"+e.pointerId+" which:"+e.which+" isPrimary:"+e.isPrimary+" :: x y "+x+" "+y); - this.ds.viewport = this.viewport; - var is_inside = !this.viewport || ( this.viewport && x >= this.viewport[0] && x < (this.viewport[0] + this.viewport[2]) && y >= this.viewport[1] && y < (this.viewport[1] + this.viewport[3]) ); + this.ds.viewport = this.viewport; + var is_inside = !this.viewport || ( this.viewport && x >= this.viewport[0] && x < (this.viewport[0] + this.viewport[2]) && y >= this.viewport[1] && y < (this.viewport[1] + this.viewport[3]) ); - //move mouse move event to the window in case it drags outside of the canvas - if(!this.options.skip_events) - { - LiteGraph.pointerListenerRemove(this.canvas,"move", this._mousemove_callback); - LiteGraph.pointerListenerAdd(ref_window.document,"move", this._mousemove_callback,true); //catch for the entire window - LiteGraph.pointerListenerAdd(ref_window.document,"up", this._mouseup_callback,true); - } + // move mouse move event to the window in case it drags outside of the canvas + if(!this.options.skip_events) { + LiteGraph.pointerListenerRemove(this.canvas,"move", this._mousemove_callback); + LiteGraph.pointerListenerAdd(ref_window.document,"move", this._mousemove_callback,true); // catch for the entire window + LiteGraph.pointerListenerAdd(ref_window.document,"up", this._mouseup_callback,true); + } - if(!is_inside){ - return; - } + if(!is_inside) { + return; + } var node = this.graph.getNodeOnPos( e.canvasX, e.canvasY, this.visible_nodes, 5 ); var skip_dragging = false; var skip_action = false; var now = LiteGraph.getTime(); - var is_primary = (e.isPrimary === undefined || !e.isPrimary); + var is_primary = (e.isPrimary === undefined || !e.isPrimary); var is_double_click = (now - this.last_mouseclick < 300) && is_primary; - this.mouse[0] = e.clientX; - this.mouse[1] = e.clientY; + this.mouse[0] = e.clientX; + this.mouse[1] = e.clientY; this.graph_mouse[0] = e.canvasX; this.graph_mouse[1] = e.canvasY; - this.last_click_position = [this.mouse[0],this.mouse[1]]; - - if (this.pointer_is_down && is_primary ){ - this.pointer_is_double = true; - //console.log("pointerevents: pointer_is_double start"); - }else{ - this.pointer_is_double = false; - } - this.pointer_is_down = true; - - + this.last_click_position = [this.mouse[0],this.mouse[1]]; + + if (this.pointer_is_down && is_primary ) { + this.pointer_is_double = true; + // console.log("pointerevents: pointer_is_double start"); + } else { + this.pointer_is_double = false; + } + this.pointer_is_down = true; + + this.canvas.focus(); LiteGraph.closeAllContextMenus(ref_window); - if (this.onMouse) - { + if (this.onMouse) { if (this.onMouse(e) == true) return; } - //left button mouse / single finger - if (e.which == 1 && !this.pointer_is_double) - { - if (e.ctrlKey) - { + // left button mouse / single finger + if (e.which == 1 && !this.pointer_is_double) { + if (e.ctrlKey) { this.dragging_rectangle = new Float32Array(4); this.dragging_rectangle[0] = e.canvasX; this.dragging_rectangle[1] = e.canvasY; @@ -6006,9 +5940,9 @@ LGraphNode.prototype.executeAction = function(action) } // clone node ALT dragging - if (LiteGraph.alt_drag_do_clone_nodes && e.altKey && node && this.allow_interaction && !skip_action && !this.read_only) - { - if (cloned = node.clone()){ + if (LiteGraph.alt_drag_do_clone_nodes && e.altKey && node && this.allow_interaction && !skip_action && !this.read_only) { + cloned = node.clone(); + if (cloned) { cloned.pos[0] += 5; cloned.pos[1] += 5; this.graph.add(cloned,false,{doCalcSize: false}); @@ -6016,7 +5950,7 @@ LGraphNode.prototype.executeAction = function(action) skip_action = true; if (!block_drag_node) { if (this.allow_dragnodes) { - this.graph.beforeChange(); + this.graph.beforeChange(); this.node_dragged = node; } if (!this.selected_nodes[node.id]) { @@ -6025,35 +5959,36 @@ LGraphNode.prototype.executeAction = function(action) } } } - + var clicking_canvas_bg = false; - //when clicked on top of a node - //and it is not interactive + // when clicked on top of a node + // and it is not interactive if (node && (this.allow_interaction || node.flags.allow_interaction) && !skip_action && !this.read_only) { if (!this.live_mode && !node.flags.pinned) { this.bringToFront(node); - } //if it wasn't selected? + } // if it wasn't selected? - //not dragging mouse to connect two slots + // not dragging mouse to connect two slots if ( this.allow_interaction && !this.connecting_node && !node.flags.collapsed && !this.live_mode ) { - //Search for corner for resize + // Search for corner for resize if ( !skip_action && node.resizable !== false && - isInsideRectangle( e.canvasX, + isInsideRectangle( + e.canvasX, e.canvasY, node.pos[0] + node.size[0] - 5, node.pos[1] + node.size[1] - 5, 10, - 10 + 10, ) ) { - this.graph.beforeChange(); + this.graph.beforeChange(); this.resizing_node = node; this.canvas.style.cursor = "se-resize"; skip_action = true; } else { - //search for outputs + // search for outputs if (node.outputs) { for ( var i = 0, l = node.outputs.length; i < l; ++i ) { var output = node.outputs[i]; @@ -6065,7 +6000,7 @@ LGraphNode.prototype.executeAction = function(action) link_pos[0] - 15, link_pos[1] - 10, 30, - 20 + 20, ) ) { this.connecting_node = node; @@ -6074,7 +6009,7 @@ LGraphNode.prototype.executeAction = function(action) this.connecting_pos = node.getConnectionPos( false, i ); this.connecting_slot = i; - if (LiteGraph.shift_click_do_break_link_from){ + if (LiteGraph.shift_click_do_break_link_from) { if (e.shiftKey) { node.disconnectOutput(i); } @@ -6096,7 +6031,7 @@ LGraphNode.prototype.executeAction = function(action) } } - //search for inputs + // search for inputs if (node.inputs) { for ( var i = 0, l = node.inputs.length; i < l; ++i ) { var input = node.inputs[i]; @@ -6108,7 +6043,7 @@ LGraphNode.prototype.executeAction = function(action) link_pos[0] - 15, link_pos[1] - 10, 30, - 20 + 20, ) ) { if (is_double_click) { @@ -6124,8 +6059,8 @@ LGraphNode.prototype.executeAction = function(action) if (input.link !== null) { var link_info = this.graph.links[ input.link - ]; //before disconnecting - if (LiteGraph.click_do_break_link_to){ + ]; // before disconnecting + if (LiteGraph.click_do_break_link_to) { node.disconnectInput(i); this.dirty_bgcanvas = true; skip_action = true; @@ -6135,10 +6070,10 @@ LGraphNode.prototype.executeAction = function(action) if ( this.allow_reconnect_links || - //this.move_destination_link_without_shift || + // this.move_destination_link_without_shift || e.shiftKey ) { - if (!LiteGraph.click_do_break_link_to){ + if (!LiteGraph.click_do_break_link_to) { node.disconnectInput(i); } this.connecting_node = this.graph._nodes_by_id[ @@ -6150,48 +6085,48 @@ LGraphNode.prototype.executeAction = function(action) this.connecting_slot ]; this.connecting_pos = this.connecting_node.getConnectionPos( false, this.connecting_slot ); - + this.dirty_bgcanvas = true; skip_action = true; } - + }else{ // has not node } - - if (!skip_action){ + + if (!skip_action) { // connect from in to out, from to to from this.connecting_node = node; this.connecting_input = input; this.connecting_input.slot_index = i; this.connecting_pos = node.getConnectionPos( true, i ); this.connecting_slot = i; - + this.dirty_bgcanvas = true; skip_action = true; } } } } - } //not resizing + } // not resizing } - //it wasn't clicked on the links boxes + // it wasn't clicked on the links boxes if (!skip_action) { var block_drag_node = false; - var pos = [e.canvasX - node.pos[0], e.canvasY - node.pos[1]]; + var pos = [e.canvasX - node.pos[0], e.canvasY - node.pos[1]]; - //widgets + // widgets var widget = this.processNodeWidgets( node, this.graph_mouse, e ); if (widget) { block_drag_node = true; this.node_widget = [node, widget]; } - //double clicking + // double clicking if (this.allow_interaction && is_double_click && this.selected_nodes[node.id]) { - //double click node + // double click node if (node.onDblClick) { node.onDblClick( e, pos, this ); } @@ -6199,30 +6134,29 @@ LGraphNode.prototype.executeAction = function(action) block_drag_node = true; } - //if do not capture mouse + // if do not capture mouse if ( node.onMouseDown && node.onMouseDown( e, pos, this ) ) { block_drag_node = true; } else { - //open subgraph button - if(node.subgraph && !node.skip_subgraph_button) - { - if ( !node.flags.collapsed && pos[0] > node.size[0] - LiteGraph.NODE_TITLE_HEIGHT && pos[1] < 0 ) { - var that = this; - setTimeout(function() { - that.openSubgraph(node.subgraph); - }, 10); - } - } - - if (this.live_mode) { - clicking_canvas_bg = true; - block_drag_node = true; - } + // open subgraph button + if(node.subgraph && !node.skip_subgraph_button) { + if ( !node.flags.collapsed && pos[0] > node.size[0] - LiteGraph.NODE_TITLE_HEIGHT && pos[1] < 0 ) { + var that = this; + setTimeout(function() { + that.openSubgraph(node.subgraph); + }, 10); + } + } + + if (this.live_mode) { + clicking_canvas_bg = true; + block_drag_node = true; + } } if (!block_drag_node) { if (this.allow_dragnodes) { - this.graph.beforeChange(); + this.graph.beforeChange(); this.node_dragged = node; } this.processNodeSelected(node, e); @@ -6236,155 +6170,155 @@ LGraphNode.prototype.executeAction = function(action) this.dirty_canvas = true; } - } //clicked outside of nodes - else { - if (!skip_action){ - //search for link connector - if(!this.read_only) { - for (var i = 0; i < this.visible_links.length; ++i) { - var link = this.visible_links[i]; - var center = link._pos; - if ( - !center || + } else { // clicked outside of nodes + if (!skip_action) { + // search for link connector + if(!this.read_only) { + for (var i = 0; i < this.visible_links.length; ++i) { + var link = this.visible_links[i]; + var center = link._pos; + if ( + !center || e.canvasX < center[0] - 4 || e.canvasX > center[0] + 4 || e.canvasY < center[1] - 4 || e.canvasY > center[1] + 4 - ) { - continue; - } - //link clicked - this.showLinkMenu(link, e); - this.over_link_center = null; //clear tooltip - break; - } - } - - this.selected_group = this.graph.getGroupOnPos( e.canvasX, e.canvasY ); - this.selected_group_resizing = false; - if (this.selected_group && !this.read_only ) { - if (e.ctrlKey) { - this.dragging_rectangle = null; - } - - var dist = distance( [e.canvasX, e.canvasY], [ this.selected_group.pos[0] + this.selected_group.size[0], this.selected_group.pos[1] + this.selected_group.size[1] ] ); - if (dist * this.ds.scale < 10) { - this.selected_group_resizing = true; - } else { - this.selected_group.recomputeInsideNodes(); - } - } - - if (is_double_click && !this.read_only && this.allow_searchbox) { - this.showSearchBox(e); - e.preventDefault(); - e.stopPropagation(); - } - - clicking_canvas_bg = true; - } + ) { + continue; + } + // link clicked + this.showLinkMenu(link, e); + this.over_link_center = null; // clear tooltip + break; + } + } + + this.selected_group = this.graph.getGroupOnPos( e.canvasX, e.canvasY ); + this.selected_group_resizing = false; + if (this.selected_group && !this.read_only ) { + if (e.ctrlKey) { + this.dragging_rectangle = null; + } + + var dist = distance( [e.canvasX, e.canvasY], [ this.selected_group.pos[0] + this.selected_group.size[0], this.selected_group.pos[1] + this.selected_group.size[1] ] ); + if (dist * this.ds.scale < 10) { + this.selected_group_resizing = true; + } else { + this.selected_group.recomputeInsideNodes(); + } + } + + if (is_double_click && !this.read_only && this.allow_searchbox) { + this.showSearchBox(e); + e.preventDefault(); + e.stopPropagation(); + } + + clicking_canvas_bg = true; + } } if (!skip_action && clicking_canvas_bg && this.allow_dragcanvas) { - //console.log("pointerevents: dragging_canvas start"); - this.dragging_canvas = true; + // console.log("pointerevents: dragging_canvas start"); + this.dragging_canvas = true; } - + } else if (e.which == 2) { - //middle button - - if (LiteGraph.middle_click_slot_add_default_node){ - if (node && this.allow_interaction && !skip_action && !this.read_only){ - //not dragging mouse to connect two slots - if ( - !this.connecting_node && + // middle button + + if (LiteGraph.middle_click_slot_add_default_node) { + if (node && this.allow_interaction && !skip_action && !this.read_only) { + // not dragging mouse to connect two slots + if ( + !this.connecting_node && !node.flags.collapsed && !this.live_mode - ) { - var mClikSlot = false; - var mClikSlot_index = false; - var mClikSlot_isOut = false; - //search for outputs - if (node.outputs) { - for ( var i = 0, l = node.outputs.length; i < l; ++i ) { - var output = node.outputs[i]; - var link_pos = node.getConnectionPos(false, i); - if (isInsideRectangle(e.canvasX,e.canvasY,link_pos[0] - 15,link_pos[1] - 10,30,20)) { - mClikSlot = output; - mClikSlot_index = i; - mClikSlot_isOut = true; - break; - } - } - } - - //search for inputs - if (node.inputs) { - for ( var i = 0, l = node.inputs.length; i < l; ++i ) { - var input = node.inputs[i]; - var link_pos = node.getConnectionPos(true, i); - if (isInsideRectangle(e.canvasX,e.canvasY,link_pos[0] - 15,link_pos[1] - 10,30,20)) { - mClikSlot = input; - mClikSlot_index = i; - mClikSlot_isOut = false; - break; - } - } - } - //console.log("middleClickSlots? "+mClikSlot+" & "+(mClikSlot_index!==false)); - if (mClikSlot && mClikSlot_index!==false){ - - var alphaPosY = 0.5-((mClikSlot_index+1)/((mClikSlot_isOut?node.outputs.length:node.inputs.length))); - var node_bounding = node.getBounding(); - // estimate a position: this is a bad semi-bad-working mess .. REFACTOR with a correct autoplacement that knows about the others slots and nodes - var posRef = [ (!mClikSlot_isOut?node_bounding[0]:node_bounding[0]+node_bounding[2])// + node_bounding[0]/this.canvas.width*150 - ,e.canvasY-80// + node_bounding[0]/this.canvas.width*66 // vertical "derive" - ]; - var nodeCreated = this.createDefaultNodeForSlot({ nodeFrom: !mClikSlot_isOut?null:node - ,slotFrom: !mClikSlot_isOut?null:mClikSlot_index - ,nodeTo: !mClikSlot_isOut?node:null - ,slotTo: !mClikSlot_isOut?mClikSlot_index:null - ,position: posRef //,e: e - ,nodeType: "AUTO" //nodeNewType - ,posAdd:[!mClikSlot_isOut?-30:30, -alphaPosY*130] //-alphaPosY*30] - ,posSizeFix:[!mClikSlot_isOut?-1:0, 0] //-alphaPosY*2*/ - }); - - } - } - } - } else if (!skip_action && this.allow_dragcanvas) { - //console.log("pointerevents: dragging_canvas start from middle button"); - this.dragging_canvas = true; - } - - + ) { + var mClikSlot = false; + var mClikSlot_index = false; + var mClikSlot_isOut = false; + // search for outputs + if (node.outputs) { + for ( var i = 0, l = node.outputs.length; i < l; ++i ) { + var output = node.outputs[i]; + var link_pos = node.getConnectionPos(false, i); + if (isInsideRectangle(e.canvasX,e.canvasY,link_pos[0] - 15,link_pos[1] - 10,30,20)) { + mClikSlot = output; + mClikSlot_index = i; + mClikSlot_isOut = true; + break; + } + } + } + + // search for inputs + if (node.inputs) { + for ( var i = 0, l = node.inputs.length; i < l; ++i ) { + var input = node.inputs[i]; + var link_pos = node.getConnectionPos(true, i); + if (isInsideRectangle(e.canvasX,e.canvasY,link_pos[0] - 15,link_pos[1] - 10,30,20)) { + mClikSlot = input; + mClikSlot_index = i; + mClikSlot_isOut = false; + break; + } + } + } + // console.log("middleClickSlots? "+mClikSlot+" & "+(mClikSlot_index!==false)); + if (mClikSlot && mClikSlot_index!==false) { + + var alphaPosY = 0.5-((mClikSlot_index+1)/((mClikSlot_isOut?node.outputs.length:node.inputs.length))); + var node_bounding = node.getBounding(); + // estimate a position: this is a bad semi-bad-working mess .. REFACTOR with a correct autoplacement that knows about the others slots and nodes + var posRef = [ (!mClikSlot_isOut?node_bounding[0]:node_bounding[0]+node_bounding[2]),// + node_bounding[0]/this.canvas.width*150 + e.canvasY-80,// + node_bounding[0]/this.canvas.width*66 // vertical "derive" + ]; + var nodeCreated = this.createDefaultNodeForSlot({ + nodeFrom: !mClikSlot_isOut?null:node, + slotFrom: !mClikSlot_isOut?null:mClikSlot_index, + nodeTo: !mClikSlot_isOut?node:null, + slotTo: !mClikSlot_isOut?mClikSlot_index:null, + position: posRef, // ,e: e + nodeType: "AUTO", // nodeNewType + posAdd: [!mClikSlot_isOut?-30:30, -alphaPosY*130], // -alphaPosY*30] + posSizeFix: [!mClikSlot_isOut?-1:0, 0], // -alphaPosY*2*/ + }); + + } + } + } + } else if (!skip_action && this.allow_dragcanvas) { + // console.log("pointerevents: dragging_canvas start from middle button"); + this.dragging_canvas = true; + } + + } else if (e.which == 3 || this.pointer_is_double) { - - //right button - if (this.allow_interaction && !skip_action && !this.read_only){ - - // is it hover a node ? - if (node){ - if(Object.keys(this.selected_nodes).length - && (this.selected_nodes[node.id] || e.shiftKey || e.ctrlKey || e.metaKey) - ){ - // is multiselected or using shift to include the now node - if (!this.selected_nodes[node.id]) this.selectNodes([node],true); // add this if not present - }else{ - // update selection - this.selectNodes([node]); - } - } - - // show menu on this node - this.processContextMenu(node, e); - } - + + // right button + if (this.allow_interaction && !skip_action && !this.read_only) { + + // is it hover a node ? + if (node) { + if(Object.keys(this.selected_nodes).length + && (this.selected_nodes[node.id] || e.shiftKey || e.ctrlKey || e.metaKey) + ) { + // is multiselected or using shift to include the now node + if (!this.selected_nodes[node.id]) this.selectNodes([node],true); // add this if not present + }else{ + // update selection + this.selectNodes([node]); + } + } + + // show menu on this node + this.processContextMenu(node, e); + } + } - //TODO - //if(this.node_selected != prev_selected) + // TODO + // if(this.node_selected != prev_selected) // this.onNodeSelectionChange(this.node_selected); this.last_mouse[0] = e.clientX; @@ -6399,7 +6333,7 @@ LGraphNode.prototype.executeAction = function(action) this.graph.change(); - //this is to ensure to defocus(blur) if a text input element is on focus + // this is to ensure to defocus(blur) if a text input element is on focus if ( !ref_window.document.activeElement || (ref_window.document.activeElement.nodeName.toLowerCase() != @@ -6427,8 +6361,8 @@ LGraphNode.prototype.executeAction = function(action) this.resize(); } - if( this.set_canvas_dirty_on_mouse_event ) - this.dirty_canvas = true; + if( this.set_canvas_dirty_on_mouse_event ) + this.dirty_canvas = true; if (!this.graph) { return; @@ -6437,24 +6371,23 @@ LGraphNode.prototype.executeAction = function(action) LGraphCanvas.active_canvas = this; this.adjustMouseEvent(e); var mouse = [e.clientX, e.clientY]; - this.mouse[0] = mouse[0]; - this.mouse[1] = mouse[1]; + this.mouse[0] = mouse[0]; + this.mouse[1] = mouse[1]; var delta = [ mouse[0] - this.last_mouse[0], - mouse[1] - this.last_mouse[1] + mouse[1] - this.last_mouse[1], ]; this.last_mouse = mouse; this.graph_mouse[0] = e.canvasX; this.graph_mouse[1] = e.canvasY; - //console.log("pointerevents: processMouseMove "+e.pointerId+" "+e.isPrimary); - - if(this.block_click) - { - //console.log("pointerevents: processMouseMove block_click"); - e.preventDefault(); - return false; - } + // console.log("pointerevents: processMouseMove "+e.pointerId+" "+e.isPrimary); + + if(this.block_click) { + // console.log("pointerevents: processMouseMove block_click"); + e.preventDefault(); + return false; + } e.dragging = this.last_mouse_dragging; @@ -6463,27 +6396,24 @@ LGraphNode.prototype.executeAction = function(action) this.node_widget[0], this.graph_mouse, e, - this.node_widget[1] + this.node_widget[1], ); this.dirty_canvas = true; } - //get node over + // get node over var node = this.graph.getNodeOnPos(e.canvasX,e.canvasY,this.visible_nodes); - if (this.dragging_rectangle) - { + if (this.dragging_rectangle) { this.dragging_rectangle[2] = e.canvasX - this.dragging_rectangle[0]; this.dragging_rectangle[3] = e.canvasY - this.dragging_rectangle[1]; this.dirty_canvas = true; - } - else if (this.selected_group && !this.read_only) - { - //moving/resizing a group + } else if (this.selected_group && !this.read_only) { + // moving/resizing a group if (this.selected_group_resizing) { this.selected_group.size = [ e.canvasX - this.selected_group.pos[0], - e.canvasY - this.selected_group.pos[1] + e.canvasY - this.selected_group.pos[1], ]; } else { var deltax = delta[0] / this.ds.scale; @@ -6495,7 +6425,7 @@ LGraphNode.prototype.executeAction = function(action) } this.dirty_bgcanvas = true; } else if (this.dragging_canvas) { - ////console.log("pointerevents: processMouseMove is dragging_canvas"); + // //console.log("pointerevents: processMouseMove is dragging_canvas"); this.ds.offset[0] += delta[0] / this.ds.scale; this.ds.offset[1] += delta[1] / this.ds.scale; this.dirty_canvas = true; @@ -6505,10 +6435,10 @@ LGraphNode.prototype.executeAction = function(action) this.dirty_canvas = true; } - //remove mouseover flag + // remove mouseover flag for (var i = 0, l = this.graph._nodes.length; i < l; ++i) { if (this.graph._nodes[i].mouseOver && node != this.graph._nodes[i] ) { - //mouse leave + // mouse leave this.graph._nodes[i].mouseOver = false; if (this.node_over && this.node_over.onMouseLeave) { this.node_over.onMouseLeave(e); @@ -6518,15 +6448,15 @@ LGraphNode.prototype.executeAction = function(action) } } - //mouse over a node + // mouse over a node if (node) { - if(node.redraw_on_mouse) + if(node.redraw_on_mouse) this.dirty_canvas = true; - //this.canvas.style.cursor = "move"; + // this.canvas.style.cursor = "move"; if (!node.mouseOver) { - //mouse enter + // mouse enter node.mouseOver = true; this.node_over = node; this.dirty_canvas = true; @@ -6536,45 +6466,45 @@ LGraphNode.prototype.executeAction = function(action) } } - //in case the node wants to do something + // in case the node wants to do something if (node.onMouseMove) { node.onMouseMove( e, [e.canvasX - node.pos[0], e.canvasY - node.pos[1]], this ); } - //if dragging a link + // if dragging a link if (this.connecting_node) { - - if (this.connecting_output){ - - var pos = this._highlight_input || [0, 0]; //to store the output of isOverNodeInput - //on top of input + if (this.connecting_output) { + + var pos = this._highlight_input || [0, 0]; // to store the output of isOverNodeInput + + // on top of input if (this.isOverNodeBox(node, e.canvasX, e.canvasY)) { - //mouse on top of the corner box, don't know what to do + // mouse on top of the corner box, don't know what to do } else { - //check if I have a slot below de mouse + // check if I have a slot below de mouse var slot = this.isOverNodeInput( node, e.canvasX, e.canvasY, pos ); if (slot != -1 && node.inputs[slot]) { var slot_type = node.inputs[slot].type; if ( LiteGraph.isValidConnection( this.connecting_output.type, slot_type ) ) { this._highlight_input = pos; - this._highlight_input_slot = node.inputs[slot]; // XXX CHECK THIS + this._highlight_input_slot = node.inputs[slot]; // XXX CHECK THIS } } else { this._highlight_input = null; - this._highlight_input_slot = null; // XXX CHECK THIS + this._highlight_input_slot = null; // XXX CHECK THIS } } - - }else if(this.connecting_input){ - - var pos = this._highlight_output || [0, 0]; //to store the output of isOverNodeOutput - //on top of output + }else if(this.connecting_input) { + + var pos = this._highlight_output || [0, 0]; // to store the output of isOverNodeOutput + + // on top of output if (this.isOverNodeBox(node, e.canvasX, e.canvasY)) { - //mouse on top of the corner box, don't know what to do + // mouse on top of the corner box, don't know what to do } else { - //check if I have a slot below de mouse + // check if I have a slot below de mouse var slot = this.isOverNodeOutput( node, e.canvasX, e.canvasY, pos ); if (slot != -1 && node.outputs[slot]) { var slot_type = node.outputs[slot].type; @@ -6588,7 +6518,7 @@ LGraphNode.prototype.executeAction = function(action) } } - //Search for corner + // Search for corner if (this.canvas) { if ( isInsideRectangle( @@ -6597,7 +6527,7 @@ LGraphNode.prototype.executeAction = function(action) node.pos[0] + node.size[0] - 5, node.pos[1] + node.size[1] - 5, 5, - 5 + 5, ) ) { this.canvas.style.cursor = "se-resize"; @@ -6605,44 +6535,43 @@ LGraphNode.prototype.executeAction = function(action) this.canvas.style.cursor = "crosshair"; } } - } else { //not over a node + } else { // not over a node - //search for link connector - var over_link = null; - for (var i = 0; i < this.visible_links.length; ++i) { - var link = this.visible_links[i]; - var center = link._pos; - if ( - !center || + // search for link connector + var over_link = null; + for (var i = 0; i < this.visible_links.length; ++i) { + var link = this.visible_links[i]; + var center = link._pos; + if ( + !center || e.canvasX < center[0] - 4 || e.canvasX > center[0] + 4 || e.canvasY < center[1] - 4 || e.canvasY > center[1] + 4 - ) { - continue; - } - over_link = link; - break; - } - if( over_link != this.over_link_center ) - { - this.over_link_center = over_link; - this.dirty_canvas = true; - } - - if (this.canvas) { - this.canvas.style.cursor = ""; - } - } //end - - //send event to node if capturing input (used with widgets that allow drag outside of the area of the node) + ) { + continue; + } + over_link = link; + break; + } + if( over_link != this.over_link_center ) { + this.over_link_center = over_link; + this.dirty_canvas = true; + } + + if (this.canvas) { + this.canvas.style.cursor = ""; + } + } // end + + // send event to node if capturing input (used with widgets that allow drag outside of the area of the node) if ( this.node_capturing_input && this.node_capturing_input != node && this.node_capturing_input.onMouseMove ) { this.node_capturing_input.onMouseMove(e,[e.canvasX - this.node_capturing_input.pos[0],e.canvasY - this.node_capturing_input.pos[1]], this); } - //node being dragged + // node being dragged if (this.node_dragged && !this.live_mode) { - //console.log("draggin!",this.selected_nodes); + // console.log("draggin!",this.selected_nodes); for (var i in this.selected_nodes) { var n = this.selected_nodes[i]; n.pos[0] += delta[0] / this.ds.scale; @@ -6658,12 +6587,12 @@ LGraphNode.prototype.executeAction = function(action) } if (this.resizing_node && !this.live_mode) { - //convert mouse to node space - var desired_size = [ e.canvasX - this.resizing_node.pos[0], e.canvasY - this.resizing_node.pos[1] ]; - var min_size = this.resizing_node.computeSize(); - desired_size[0] = Math.max( min_size[0], desired_size[0] ); - desired_size[1] = Math.max( min_size[1], desired_size[1] ); - this.resizing_node.setSize( desired_size ); + // convert mouse to node space + var desired_size = [ e.canvasX - this.resizing_node.pos[0], e.canvasY - this.resizing_node.pos[1] ]; + var min_size = this.resizing_node.computeSize(); + desired_size[0] = Math.max( min_size[0], desired_size[0] ); + desired_size[1] = Math.max( min_size[1], desired_size[1] ); + this.resizing_node.setSize( desired_size ); this.canvas.style.cursor = "se-resize"; this.dirty_canvas = true; @@ -6681,20 +6610,20 @@ LGraphNode.prototype.executeAction = function(action) **/ LGraphCanvas.prototype.processMouseUp = function(e) { - var is_primary = ( e.isPrimary === undefined || e.isPrimary ); - - //early exit for extra pointer - if(!is_primary){ - /*e.stopPropagation(); - e.preventDefault();*/ - //console.log("pointerevents: processMouseUp pointerN_stop "+e.pointerId+" "+e.isPrimary); - return false; - } - - //console.log("pointerevents: processMouseUp "+e.pointerId+" "+e.isPrimary+" :: "+e.clientX+" "+e.clientY); - - if( this.set_canvas_dirty_on_mouse_event ) - this.dirty_canvas = true; + var is_primary = ( e.isPrimary === undefined || e.isPrimary ); + + // early exit for extra pointer + if(!is_primary) { + /* e.stopPropagation(); + e.preventDefault();*/ + // console.log("pointerevents: processMouseUp pointerN_stop "+e.pointerId+" "+e.isPrimary); + return false; + } + + // console.log("pointerevents: processMouseUp "+e.pointerId+" "+e.isPrimary+" :: "+e.clientX+" "+e.clientY); + + if( this.set_canvas_dirty_on_mouse_event ) + this.dirty_canvas = true; if (!this.graph) return; @@ -6703,37 +6632,34 @@ LGraphNode.prototype.executeAction = function(action) var document = window.document; LGraphCanvas.active_canvas = this; - //restore the mousemove event back to the canvas - if(!this.options.skip_events) - { - //console.log("pointerevents: processMouseUp adjustEventListener"); - LiteGraph.pointerListenerRemove(document,"move", this._mousemove_callback,true); - LiteGraph.pointerListenerAdd(this.canvas,"move", this._mousemove_callback,true); - LiteGraph.pointerListenerRemove(document,"up", this._mouseup_callback,true); - } + // restore the mousemove event back to the canvas + if(!this.options.skip_events) { + // console.log("pointerevents: processMouseUp adjustEventListener"); + LiteGraph.pointerListenerRemove(document,"move", this._mousemove_callback,true); + LiteGraph.pointerListenerAdd(this.canvas,"move", this._mousemove_callback,true); + LiteGraph.pointerListenerRemove(document,"up", this._mouseup_callback,true); + } this.adjustMouseEvent(e); var now = LiteGraph.getTime(); e.click_time = now - this.last_mouseclick; this.last_mouse_dragging = false; - this.last_click_position = null; + this.last_click_position = null; + + if(this.block_click) { + // console.log("pointerevents: processMouseUp block_clicks"); + this.block_click = false; // used to avoid sending twice a click in a immediate button + } - if(this.block_click) - { - //console.log("pointerevents: processMouseUp block_clicks"); - this.block_click = false; //used to avoid sending twice a click in a immediate button - } + // console.log("pointerevents: processMouseUp which: "+e.which); - //console.log("pointerevents: processMouseUp which: "+e.which); - if (e.which == 1) { - if( this.node_widget ) - { - this.processNodeWidgets( this.node_widget[0], this.graph_mouse, e ); - } + if( this.node_widget ) { + this.processNodeWidgets( this.node_widget[0], this.graph_mouse, e ); + } - //left button + // left button this.node_widget = null; if (this.selected_group) { @@ -6744,12 +6670,8 @@ LGraphNode.prototype.executeAction = function(action) this.selected_group.pos[1] - Math.round(this.selected_group.pos[1]); this.selected_group.move(diffx, diffy, e.ctrlKey); - this.selected_group.pos[0] = Math.round( - this.selected_group.pos[0] - ); - this.selected_group.pos[1] = Math.round( - this.selected_group.pos[1] - ); + this.selected_group.pos[0] = Math.round(this.selected_group.pos[0]); + this.selected_group.pos[1] = Math.round(this.selected_group.pos[1]); if (this.selected_group._nodes.length) { this.dirty_canvas = true; } @@ -6757,18 +6679,18 @@ LGraphNode.prototype.executeAction = function(action) } this.selected_group_resizing = false; - var node = this.graph.getNodeOnPos( - e.canvasX, - e.canvasY, - this.visible_nodes - ); - + var node = this.graph.getNodeOnPos( + e.canvasX, + e.canvasY, + this.visible_nodes, + ); + if (this.dragging_rectangle) { if (this.graph) { var nodes = this.graph._nodes; var node_bounding = new Float32Array(4); - - //compute bounding and flip if left to right + + // compute bounding and flip if left to right var w = Math.abs(this.dragging_rectangle[2]); var h = Math.abs(this.dragging_rectangle[3]); var startx = @@ -6784,114 +6706,114 @@ LGraphNode.prototype.executeAction = function(action) this.dragging_rectangle[2] = w; this.dragging_rectangle[3] = h; - // test dragging rect size, if minimun simulate a click - if (!node || (w > 10 && h > 10 )){ - //test against all nodes (not visible because the rectangle maybe start outside - var to_select = []; - for (var i = 0; i < nodes.length; ++i) { - var nodeX = nodes[i]; - nodeX.getBounding(node_bounding); - if ( - !overlapBounding( - this.dragging_rectangle, - node_bounding - ) - ) { - continue; - } //out of the visible area - to_select.push(nodeX); - } - if (to_select.length) { - this.selectNodes(to_select,e.shiftKey); // add to selection with shift - } - }else{ - // will select of update selection - this.selectNodes([node],e.shiftKey||e.ctrlKey); // add to selection add to selection with ctrlKey or shiftKey - } - + // test dragging rect size, if minimun simulate a click + if (!node || (w > 10 && h > 10 )) { + // test against all nodes (not visible because the rectangle maybe start outside + var to_select = []; + for (var i = 0; i < nodes.length; ++i) { + var nodeX = nodes[i]; + nodeX.getBounding(node_bounding); + if ( + !overlapBounding( + this.dragging_rectangle, + node_bounding, + ) + ) { + continue; + } // out of the visible area + to_select.push(nodeX); + } + if (to_select.length) { + this.selectNodes(to_select,e.shiftKey); // add to selection with shift + } + }else{ + // will select of update selection + this.selectNodes([node],e.shiftKey||e.ctrlKey); // add to selection add to selection with ctrlKey or shiftKey + } + } this.dragging_rectangle = null; } else if (this.connecting_node) { - //dragging a connection + // dragging a connection this.dirty_canvas = true; this.dirty_bgcanvas = true; var connInOrOut = this.connecting_output || this.connecting_input; var connType = connInOrOut.type; - - //node below mouse + + // node below mouse if (node) { - + /* no need to condition on event type.. just another type if ( connType == LiteGraph.EVENT && this.isOverNodeBox(node, e.canvasX, e.canvasY) ) { - + this.connecting_node.connect( this.connecting_slot, node, LiteGraph.EVENT ); - + } else {*/ - - //slot below mouse? connect - - if (this.connecting_output){ - - var slot = this.isOverNodeInput( - node, - e.canvasX, - e.canvasY - ); - if (slot != -1) { - this.connecting_node.connect(this.connecting_slot, node, slot); - } else { - //not on top of an input - // look for a good slot - this.connecting_node.connectByType(this.connecting_slot,node,connType); - } - - }else if (this.connecting_input){ - - var slot = this.isOverNodeOutput( - node, - e.canvasX, - e.canvasY - ); - if (slot != -1) { - node.connect(slot, this.connecting_node, this.connecting_slot); // this is inverted has output-input nature like - } else { - //not on top of an input - // look for a good slot - this.connecting_node.connectByTypeOutput(this.connecting_slot,node,connType); - } - + // slot below mouse? connect + + if (this.connecting_output) { + + var slot = this.isOverNodeInput( + node, + e.canvasX, + e.canvasY, + ); + if (slot != -1) { + this.connecting_node.connect(this.connecting_slot, node, slot); + } else { + // not on top of an input + // look for a good slot + this.connecting_node.connectByType(this.connecting_slot,node,connType); } - - - //} - - }else{ - + + }else if (this.connecting_input) { + + var slot = this.isOverNodeOutput( + node, + e.canvasX, + e.canvasY, + ); + + if (slot != -1) { + node.connect(slot, this.connecting_node, this.connecting_slot); // this is inverted has output-input nature like + } else { + // not on top of an input + // look for a good slot + this.connecting_node.connectByTypeOutput(this.connecting_slot,node,connType); + } + + } + + + // } + + } else { + // add menu when releasing link in empty space - if (LiteGraph.release_link_on_empty_shows_menu){ - if (e.shiftKey && this.allow_searchbox){ - if(this.connecting_output){ - this.showSearchBox(e,{node_from: this.connecting_node, slot_from: this.connecting_output, type_filter_in: this.connecting_output.type}); - }else if(this.connecting_input){ - this.showSearchBox(e,{node_to: this.connecting_node, slot_from: this.connecting_input, type_filter_out: this.connecting_input.type}); - } - }else{ - if(this.connecting_output){ - this.showConnectionMenu({nodeFrom: this.connecting_node, slotFrom: this.connecting_output, e: e}); - }else if(this.connecting_input){ - this.showConnectionMenu({nodeTo: this.connecting_node, slotTo: this.connecting_input, e: e}); - } - } - } + if (LiteGraph.release_link_on_empty_shows_menu) { + if (e.shiftKey && this.allow_searchbox) { + if(this.connecting_output) { + this.showSearchBox(e,{node_from: this.connecting_node, slot_from: this.connecting_output, type_filter_in: this.connecting_output.type}); + }else if(this.connecting_input) { + this.showSearchBox(e,{node_to: this.connecting_node, slot_from: this.connecting_input, type_filter_out: this.connecting_input.type}); + } + } else { + if(this.connecting_output) { + this.showConnectionMenu({nodeFrom: this.connecting_node, slotFrom: this.connecting_output, e: e}); + }else if(this.connecting_input) { + this.showConnectionMenu({nodeTo: this.connecting_node, slotTo: this.connecting_input, e: e}); + } + } + } } this.connecting_output = null; @@ -6899,14 +6821,13 @@ LGraphNode.prototype.executeAction = function(action) this.connecting_pos = null; this.connecting_node = null; this.connecting_slot = -1; - } //not dragging connection - else if (this.resizing_node) { + } else if (this.resizing_node) { // not dragging connection this.dirty_canvas = true; this.dirty_bgcanvas = true; - this.graph.afterChange(this.resizing_node); + this.graph.afterChange(this.resizing_node); this.resizing_node = null; } else if (this.node_dragged) { - //node being dragged? + // node being dragged? var node = this.node_dragged; if ( node && @@ -6923,17 +6844,16 @@ LGraphNode.prototype.executeAction = function(action) if (this.graph.config.align_to_grid || this.align_to_grid ) { this.node_dragged.alignToGrid(); } - if( this.onNodeMoved ) - this.onNodeMoved( this.node_dragged ); - this.graph.afterChange(this.node_dragged); + if( this.onNodeMoved ) + this.onNodeMoved( this.node_dragged ); + this.graph.afterChange(this.node_dragged); this.node_dragged = null; - } //no node being dragged - else { - //get node over + } else { // no node being dragged + // get node over var node = this.graph.getNodeOnPos( e.canvasX, e.canvasY, - this.visible_nodes + this.visible_nodes, ); if (!node && e.click_time < 300) { @@ -6952,18 +6872,18 @@ LGraphNode.prototype.executeAction = function(action) ) { this.node_capturing_input.onMouseUp(e, [ e.canvasX - this.node_capturing_input.pos[0], - e.canvasY - this.node_capturing_input.pos[1] + e.canvasY - this.node_capturing_input.pos[1], ]); } } } else if (e.which == 2) { - //middle button - //trace("middle"); + // middle button + // trace("middle"); this.dirty_canvas = true; this.dragging_canvas = false; } else if (e.which == 3) { - //right button - //trace("right"); + // right button + // trace("right"); this.dirty_canvas = true; this.dragging_canvas = false; } @@ -6973,15 +6893,14 @@ LGraphNode.prototype.executeAction = function(action) this.draw(); */ - if (is_primary) - { - this.pointer_is_down = false; - this.pointer_is_double = false; - } - + if (is_primary) { + this.pointer_is_down = false; + this.pointer_is_double = false; + } + this.graph.change(); - //console.log("pointerevents: processMouseUp stopPropagation"); + // console.log("pointerevents: processMouseUp stopPropagation"); e.stopPropagation(); e.preventDefault(); return false; @@ -7000,21 +6919,17 @@ LGraphNode.prototype.executeAction = function(action) this.adjustMouseEvent(e); - var x = e.clientX; - var y = e.clientY; - var is_inside = !this.viewport || ( this.viewport && x >= this.viewport[0] && x < (this.viewport[0] + this.viewport[2]) && y >= this.viewport[1] && y < (this.viewport[1] + this.viewport[3]) ); - if(!is_inside) - return; + var x = e.clientX; + var y = e.clientY; + var is_inside = !this.viewport || ( this.viewport && x >= this.viewport[0] && x < (this.viewport[0] + this.viewport[2]) && y >= this.viewport[1] && y < (this.viewport[1] + this.viewport[3]) ); + if(!is_inside) + return; var scale = this.ds.scale; - if (delta > 0) { - scale *= 1.1; - } else if (delta < 0) { - scale *= 1 / 1.1; - } + scale *= Math.pow(1.1, delta * 0.01); - //this.setZoom( scale, [ e.clientX, e.clientY ] ); + // this.setZoom( scale, [ e.clientX, e.clientY ] ); this.ds.changeScale(scale, [e.clientX, e.clientY]); this.graph.change(); @@ -7036,7 +6951,7 @@ LGraphNode.prototype.executeAction = function(action) node.pos[0] + 2, node.pos[1] + 2 - title_height, title_height - 4, - title_height - 4 + title_height - 4, ) ) { return true; @@ -7052,7 +6967,7 @@ LGraphNode.prototype.executeAction = function(action) node, canvasx, canvasy, - slot_pos + slot_pos, ) { if (node.inputs) { for (var i = 0, l = node.inputs.length; i < l; ++i) { @@ -7066,7 +6981,7 @@ LGraphNode.prototype.executeAction = function(action) link_pos[0] - 5, link_pos[1] - 10, 10, - 20 + 20, ); } else { is_inside = isInsideRectangle( @@ -7075,7 +6990,7 @@ LGraphNode.prototype.executeAction = function(action) link_pos[0] - 10, link_pos[1] - 5, 40, - 10 + 10, ); } if (is_inside) { @@ -7089,7 +7004,7 @@ LGraphNode.prototype.executeAction = function(action) } return -1; }; - + /** * returns the INDEX if a position (in graph space) is on top of a node output slot * @method isOverNodeOuput @@ -7098,7 +7013,7 @@ LGraphNode.prototype.executeAction = function(action) node, canvasx, canvasy, - slot_pos + slot_pos, ) { if (node.outputs) { for (var i = 0, l = node.outputs.length; i < l; ++i) { @@ -7112,7 +7027,7 @@ LGraphNode.prototype.executeAction = function(action) link_pos[0] - 5, link_pos[1] - 10, 10, - 20 + 20, ); } else { is_inside = isInsideRectangle( @@ -7121,7 +7036,7 @@ LGraphNode.prototype.executeAction = function(action) link_pos[0] - 10, link_pos[1] - 5, 40, - 10 + 10, ); } if (is_inside) { @@ -7146,7 +7061,7 @@ LGraphNode.prototype.executeAction = function(action) } var block_default = false; - //console.log(e); //debug + // console.log(e); //debug if (e.target.localName == "input") { return; @@ -7154,26 +7069,26 @@ LGraphNode.prototype.executeAction = function(action) if (e.type == "keydown") { if (e.keyCode == 32) { - //space + // space this.dragging_canvas = true; block_default = true; } - + if (e.keyCode == 27) { - //esc + // esc if(this.node_panel) this.node_panel.close(); if(this.options_panel) this.options_panel.close(); block_default = true; } - //select all Control A + // select all Control A if (e.keyCode == 65 && e.ctrlKey) { this.selectNodes(); block_default = true; } if ((e.keyCode === 67) && (e.metaKey || e.ctrlKey) && !e.shiftKey) { - //copy + // copy if (this.selected_nodes) { this.copyToClipboard(); block_default = true; @@ -7181,11 +7096,11 @@ LGraphNode.prototype.executeAction = function(action) } if ((e.keyCode === 86) && (e.metaKey || e.ctrlKey)) { - //paste + // paste this.pasteFromClipboard(e.shiftKey); } - //delete or backspace + // delete or backspace if (e.keyCode == 46 || e.keyCode == 8) { if ( e.target.localName != "input" && @@ -7196,10 +7111,10 @@ LGraphNode.prototype.executeAction = function(action) } } - //collapse - //... + // collapse + // ... - //TODO + // TODO if (this.selected_nodes) { for (var i in this.selected_nodes) { if (this.selected_nodes[i].onKeyDown) { @@ -7234,16 +7149,17 @@ LGraphNode.prototype.executeAction = function(action) LGraphCanvas.prototype.copyToClipboard = function() { var clipboard_info = { nodes: [], - links: [] + links: [], }; var index = 0; var selected_nodes_array = []; + var map = {}; for (var i in this.selected_nodes) { var node = this.selected_nodes[i]; if (node.clonable === false) continue; - node._relative_id = index; selected_nodes_array.push(node); + map[node.id] = index; index += 1; } @@ -7252,8 +7168,7 @@ LGraphNode.prototype.executeAction = function(action) if(node.clonable === false) continue; var cloned = node.clone(); - if(!cloned) - { + if(!cloned) { console.warn("node type not found: " + node.type ); continue; } @@ -7268,25 +7183,23 @@ LGraphNode.prototype.executeAction = function(action) if (!link_info) { continue; } - var target_node = this.graph.getNodeById( - link_info.origin_id - ); + var target_node = this.graph.getNodeById(link_info.origin_id); if (!target_node) { continue; } clipboard_info.links.push([ - target_node._relative_id, - link_info.origin_slot, //j, - node._relative_id, + map[target_node.id], + link_info.origin_slot, // j, + map[node.id], link_info.target_slot, - target_node.id + target_node.id, ]); } } } localStorage.setItem( "litegrapheditor_clipboard", - JSON.stringify(clipboard_info) + JSON.stringify(clipboard_info), ); }; @@ -7300,25 +7213,24 @@ LGraphNode.prototype.executeAction = function(action) return; } - this.graph.beforeChange(); + this.graph.beforeChange(); - //create nodes + // create nodes var clipboard_info = JSON.parse(data); // calculate top-left node, could work without this processing but using diff with last node pos :: clipboard_info.nodes[clipboard_info.nodes.length-1].pos var posMin = false; var posMinIndexes = false; for (var i = 0; i < clipboard_info.nodes.length; ++i) { - if (posMin){ - if(posMin[0]>clipboard_info.nodes[i].pos[0]){ + if (posMin) { + if(posMin[0]>clipboard_info.nodes[i].pos[0]) { posMin[0] = clipboard_info.nodes[i].pos[0]; posMinIndexes[0] = i; } - if(posMin[1]>clipboard_info.nodes[i].pos[1]){ + if(posMin[1]>clipboard_info.nodes[i].pos[1]) { posMin[1] = clipboard_info.nodes[i].pos[1]; posMinIndexes[1] = i; } - } - else{ + } else{ posMin = [clipboard_info.nodes[i].pos[0], clipboard_info.nodes[i].pos[1]]; posMinIndexes = [i, i]; } @@ -7329,21 +7241,21 @@ LGraphNode.prototype.executeAction = function(action) var node = LiteGraph.createNode(node_data.type); if (node) { node.configure(node_data); - - //paste in last known mouse position - node.pos[0] += this.graph_mouse[0] - posMin[0]; //+= 5; - node.pos[1] += this.graph_mouse[1] - posMin[1]; //+= 5; - this.graph.add(node,{doProcessChange:false}); - + // paste in last known mouse position + node.pos[0] += this.graph_mouse[0] - posMin[0]; // += 5; + node.pos[1] += this.graph_mouse[1] - posMin[1]; // += 5; + + this.graph.add(node,{doProcessChange: false}); + nodes.push(node); } } - //create links + // create links for (var i = 0; i < clipboard_info.links.length; ++i) { var link_info = clipboard_info.links[i]; - var origin_node; + var origin_node = null; var origin_node_relative_id = link_info[0]; if (origin_node_relative_id != null) { origin_node = nodes[origin_node_relative_id]; @@ -7354,15 +7266,15 @@ LGraphNode.prototype.executeAction = function(action) } } var target_node = nodes[link_info[2]]; - if( origin_node && target_node ) - origin_node.connect(link_info[1], target_node, link_info[3]); - else - console.warn("Warning, nodes missing on pasting"); + if( origin_node && target_node ) + origin_node.connect(link_info[1], target_node, link_info[3]); + else + console.warn("Warning, nodes missing on pasting"); } this.selectNodes(nodes); - this.graph.afterChange(); + this.graph.afterChange(); }; /** @@ -7372,13 +7284,13 @@ LGraphNode.prototype.executeAction = function(action) LGraphCanvas.prototype.processDrop = function(e) { e.preventDefault(); this.adjustMouseEvent(e); - var x = e.clientX; - var y = e.clientY; - var is_inside = !this.viewport || ( this.viewport && x >= this.viewport[0] && x < (this.viewport[0] + this.viewport[2]) && y >= this.viewport[1] && y < (this.viewport[1] + this.viewport[3]) ); - if(!is_inside){ - return; - // --- BREAK --- - } + var x = e.clientX; + var y = e.clientY; + var is_inside = !this.viewport || ( this.viewport && x >= this.viewport[0] && x < (this.viewport[0] + this.viewport[2]) && y >= this.viewport[1] && y < (this.viewport[1] + this.viewport[3]) ); + if(!is_inside) { + return; + // --- BREAK --- + } var pos = [e.canvasX, e.canvasY]; @@ -7403,22 +7315,22 @@ LGraphNode.prototype.executeAction = function(action) var file = e.dataTransfer.files[0]; var filename = file.name; var ext = LGraphCanvas.getFileExtension(filename); - //console.log(file); + // console.log(file); if (node.onDropFile) { node.onDropFile(file); } if (node.onDropData) { - //prepare reader + // prepare reader var reader = new FileReader(); reader.onload = function(event) { - //console.log(event.target); + // console.log(event.target); var data = event.target.result; node.onDropData(data, filename, file); }; - //read data + // read data var type = file.type.split("/")[0]; if (type == "text" || type == "") { reader.readAsText(file); @@ -7445,21 +7357,21 @@ LGraphNode.prototype.executeAction = function(action) return false; }; - //called if the graph doesn't have a default drop item behaviour + // called if the graph doesn't have a default drop item behaviour LGraphCanvas.prototype.checkDropItem = function(e) { if (e.dataTransfer.files.length) { var file = e.dataTransfer.files[0]; var ext = LGraphCanvas.getFileExtension(file.name).toLowerCase(); var nodetype = LiteGraph.node_types_by_file_extension[ext]; if (nodetype) { - this.graph.beforeChange(); + this.graph.beforeChange(); var node = LiteGraph.createNode(nodetype.type); node.pos = [e.canvasX, e.canvasY]; this.graph.add(node); if (node.onDropFile) { node.onDropFile(file); } - this.graph.afterChange(); + this.graph.afterChange(); } } }; @@ -7467,11 +7379,9 @@ LGraphNode.prototype.executeAction = function(action) LGraphCanvas.prototype.processNodeDblClicked = function(n) { if (this.onShowNodePanel) { this.onShowNodePanel(n); + } else { + this.showShowNodePanel(n); } - else - { - this.showShowNodePanel(n); - } if (this.onNodeDblClicked) { this.onNodeDblClicked(n); @@ -7493,7 +7403,7 @@ LGraphNode.prototype.executeAction = function(action) **/ LGraphCanvas.prototype.selectNode = function( node, - add_to_current_selection + add_to_current_selection, ) { if (node == null) { this.deselectAllNodes(); @@ -7506,14 +7416,13 @@ LGraphNode.prototype.executeAction = function(action) * selects several nodes (or adds them to the current selection) * @method selectNodes **/ - LGraphCanvas.prototype.selectNodes = function( nodes, add_to_current_selection ) - { - if (!add_to_current_selection) { + LGraphCanvas.prototype.selectNodes = function( nodes, add_to_current_selection ) { + if (!add_to_current_selection) { this.deselectAllNodes(); } nodes = nodes || this.graph._nodes; - if (typeof nodes == "string") nodes = [nodes]; + if (typeof nodes == "string") nodes = [nodes]; for (var i in nodes) { var node = nodes[i]; if (node.is_selected) { @@ -7544,8 +7453,8 @@ LGraphNode.prototype.executeAction = function(action) } } - if( this.onSelectionChange ) - this.onSelectionChange( this.selected_nodes ); + if( this.onSelectionChange ) + this.onSelectionChange( this.selected_nodes ); this.setDirty(true); }; @@ -7567,7 +7476,7 @@ LGraphNode.prototype.executeAction = function(action) this.onNodeDeselected(node); } - //remove highlighted + // remove highlighted if (node.inputs) { for (var i = 0; i < node.inputs.length; ++i) { delete this.highlighted_links[node.inputs[i].link]; @@ -7603,15 +7512,15 @@ LGraphNode.prototype.executeAction = function(action) node.onDeselected(); } node.is_selected = false; - if (this.onNodeDeselected) { - this.onNodeDeselected(node); - } + if (this.onNodeDeselected) { + this.onNodeDeselected(node); + } } this.selected_nodes = {}; this.current_node = null; this.highlighted_links = {}; - if( this.onSelectionChange ) - this.onSelectionChange( this.selected_nodes ); + if( this.onSelectionChange ) + this.onSelectionChange( this.selected_nodes ); this.setDirty(true); }; @@ -7621,36 +7530,35 @@ LGraphNode.prototype.executeAction = function(action) **/ LGraphCanvas.prototype.deleteSelectedNodes = function() { - this.graph.beforeChange(); + this.graph.beforeChange(); for (var i in this.selected_nodes) { var node = this.selected_nodes[i]; - if(node.block_delete) - continue; + if(node.block_delete) + continue; - //autoconnect when possible (very basic, only takes into account first input-output) - if(node.inputs && node.inputs.length && node.outputs && node.outputs.length && LiteGraph.isValidConnection( node.inputs[0].type, node.outputs[0].type ) && node.inputs[0].link && node.outputs[0].links && node.outputs[0].links.length ) - { - var input_link = node.graph.links[ node.inputs[0].link ]; - var output_link = node.graph.links[ node.outputs[0].links[0] ]; - var input_node = node.getInputNode(0); - var output_node = node.getOutputNodes(0)[0]; - if(input_node && output_node) - input_node.connect( input_link.origin_slot, output_node, output_link.target_slot ); - } + // autoconnect when possible (very basic, only takes into account first input-output) + if(node.inputs && node.inputs.length && node.outputs && node.outputs.length && LiteGraph.isValidConnection( node.inputs[0].type, node.outputs[0].type ) && node.inputs[0].link && node.outputs[0].links && node.outputs[0].links.length ) { + var input_link = node.graph.links[node.inputs[0].link]; + var output_link = node.graph.links[node.outputs[0].links[0]]; + var input_node = node.getInputNode(0); + var output_node = node.getOutputNodes(0)[0]; + if(input_node && output_node) + input_node.connect( input_link.origin_slot, output_node, output_link.target_slot ); + } this.graph.remove(node); - if (this.onNodeDeselected) { - this.onNodeDeselected(node); - } + if (this.onNodeDeselected) { + this.onNodeDeselected(node); + } } this.selected_nodes = {}; this.current_node = null; this.highlighted_links = {}; this.setDirty(true); - this.graph.afterChange(); + this.graph.afterChange(); }; - + /** * centers the camera on a given node * @method centerOnNode @@ -7672,18 +7580,18 @@ LGraphNode.prototype.executeAction = function(action) * @method adjustMouseEvent **/ LGraphCanvas.prototype.adjustMouseEvent = function(e) { - var clientX_rel = 0; + var clientX_rel = 0; var clientY_rel = 0; - - if (this.canvas) { + + if (this.canvas) { var b = this.canvas.getBoundingClientRect(); clientX_rel = e.clientX - b.left; clientY_rel = e.clientY - b.top; } else { - clientX_rel = e.clientX; - clientY_rel = e.clientY; + clientX_rel = e.clientX; + clientY_rel = e.clientY; } - + // e.deltaX = clientX_rel - this.last_mouse_position[0]; // e.deltaY = clientY_rel- this.last_mouse_position[1]; @@ -7692,8 +7600,8 @@ LGraphNode.prototype.executeAction = function(action) e.canvasX = clientX_rel / this.ds.scale - this.ds.offset[0]; e.canvasY = clientY_rel / this.ds.scale - this.ds.offset[1]; - - //console.log("pointerevents: adjustMouseEvent "+e.clientX+":"+e.clientY+" "+clientX_rel+":"+clientY_rel+" "+e.canvasX+":"+e.canvasY); + + // console.log("pointerevents: adjustMouseEvent "+e.clientX+":"+e.clientY+" "+clientX_rel+":"+clientY_rel+" "+e.canvasX+":"+e.canvasY); }; /** @@ -7742,12 +7650,12 @@ LGraphNode.prototype.executeAction = function(action) return this.ds.convertCanvasToOffset(pos, out); }; - //converts event coordinates from canvas2D to graph coordinates + // converts event coordinates from canvas2D to graph coordinates LGraphCanvas.prototype.convertEventToCanvasOffset = function(e) { var rect = this.canvas.getBoundingClientRect(); return this.convertCanvasToOffset([ e.clientX - rect.left, - e.clientY - rect.top + e.clientY - rect.top, ]); }; @@ -7795,14 +7703,14 @@ LGraphNode.prototype.executeAction = function(action) for (var i = 0, l = nodes.length; i < l; ++i) { var n = nodes[i]; - //skip rendering nodes in live mode + // skip rendering nodes in live mode if (this.live_mode && !n.onDrawBackground && !n.onDrawForeground) { continue; } if (!overlapBounding(this.visible_area, n.getBounding(temp, true))) { continue; - } //out of the visible area + } // out of the visible area visible_nodes.push(n); } @@ -7818,7 +7726,7 @@ LGraphNode.prototype.executeAction = function(action) return; } - //fps counting + // fps counting var now = LiteGraph.getTime(); this.render_time = (now - this.last_draw_time) * 0.001; this.last_draw_time = now; @@ -7858,19 +7766,19 @@ LGraphNode.prototype.executeAction = function(action) } var ctx = this.ctx; if (!ctx) { - //maybe is using webgl... + // maybe is using webgl... return; } var canvas = this.canvas; if ( ctx.start2D && !this.viewport ) { ctx.start2D(); - ctx.restore(); - ctx.setTransform(1, 0, 0, 1, 0, 0); + ctx.restore(); + ctx.setTransform(1, 0, 0, 1, 0, 0); } - //clip dirty area if there is one, otherwise work in full canvas - var area = this.viewport || this.dirty_area; + // clip dirty area if there is one, otherwise work in full canvas + var area = this.viewport || this.dirty_area; if (area) { ctx.save(); ctx.beginPath(); @@ -7878,89 +7786,88 @@ LGraphNode.prototype.executeAction = function(action) ctx.clip(); } - //clear - //canvas.width = canvas.width; + // clear + // canvas.width = canvas.width; if (this.clear_background) { - if(area) - ctx.clearRect( area[0],area[1],area[2],area[3] ); - else - ctx.clearRect(0, 0, canvas.width, canvas.height); + if(area) + ctx.clearRect( area[0],area[1],area[2],area[3] ); + else + ctx.clearRect(0, 0, canvas.width, canvas.height); } - //draw bg canvas + // draw bg canvas if (this.bgcanvas == this.canvas) { this.drawBackCanvas(); } else { ctx.drawImage( this.bgcanvas, 0, 0 ); } - //rendering + // rendering if (this.onRender) { this.onRender(canvas, ctx); } - //info widget + // info widget if (this.show_info) { this.renderInfo(ctx, area ? area[0] : 0, area ? area[1] : 0 ); } if (this.graph) { - //apply transformations + // apply transformations ctx.save(); this.ds.toCanvasContext(ctx); - //draw nodes + // draw nodes var drawn_nodes = 0; var visible_nodes = this.computeVisibleNodes( null, - this.visible_nodes + this.visible_nodes, ); for (var i = 0; i < visible_nodes.length; ++i) { var node = visible_nodes[i]; - //transform coords system + // transform coords system ctx.save(); ctx.translate(node.pos[0], node.pos[1]); - //Draw + // Draw this.drawNode(node, ctx); drawn_nodes += 1; - //Restore + // Restore ctx.restore(); } - //on top (debug) + // on top (debug) if (this.render_execution_order) { this.drawExecutionOrder(ctx); } - //connections ontop? + // connections ontop? if (this.graph.config.links_ontop) { if (!this.live_mode) { this.drawConnections(ctx); } } - //current connection (the one being dragged by the mouse) + // current connection (the one being dragged by the mouse) if (this.connecting_pos != null) { ctx.lineWidth = this.connections_width; var link_color = null; - + var connInOrOut = this.connecting_output || this.connecting_input; var connType = connInOrOut.type; var connDir = connInOrOut.dir; - if(connDir == null) - { - if (this.connecting_output) - connDir = this.connecting_node.horizontal ? LiteGraph.DOWN : LiteGraph.RIGHT; - else - connDir = this.connecting_node.horizontal ? LiteGraph.UP : LiteGraph.LEFT; - } + if(connDir == null) { + if (this.connecting_output) + connDir = this.connecting_node.horizontal ? LiteGraph.DOWN : LiteGraph.RIGHT; + else + connDir = this.connecting_node.horizontal ? LiteGraph.UP : LiteGraph.LEFT; + } var connShape = connInOrOut.shape; - + switch (connType) { case LiteGraph.EVENT: link_color = LiteGraph.EVENT_LINK_COLOR; @@ -7969,7 +7876,7 @@ LGraphNode.prototype.executeAction = function(action) link_color = LiteGraph.CONNECTING_LINK_COLOR; } - //the connection being dragged by the mouse + // the connection being dragged by the mouse this.renderLink( ctx, this.connecting_pos, @@ -7979,7 +7886,7 @@ LGraphNode.prototype.executeAction = function(action) null, link_color, connDir, - LiteGraph.CENTER + LiteGraph.CENTER, ); ctx.beginPath(); @@ -7991,38 +7898,37 @@ LGraphNode.prototype.executeAction = function(action) this.connecting_pos[0] - 6 + 0.5, this.connecting_pos[1] - 5 + 0.5, 14, - 10 + 10, ); - ctx.fill(); - ctx.beginPath(); + ctx.fill(); + ctx.beginPath(); ctx.rect( this.graph_mouse[0] - 6 + 0.5, this.graph_mouse[1] - 5 + 0.5, 14, - 10 + 10, ); } else if (connShape === LiteGraph.ARROW_SHAPE) { ctx.moveTo(this.connecting_pos[0] + 8, this.connecting_pos[1] + 0.5); ctx.lineTo(this.connecting_pos[0] - 4, this.connecting_pos[1] + 6 + 0.5); ctx.lineTo(this.connecting_pos[0] - 4, this.connecting_pos[1] - 6 + 0.5); ctx.closePath(); - } - else { + } else { ctx.arc( this.connecting_pos[0], this.connecting_pos[1], 4, 0, - Math.PI * 2 + Math.PI * 2, ); - ctx.fill(); - ctx.beginPath(); + ctx.fill(); + ctx.beginPath(); ctx.arc( this.graph_mouse[0], this.graph_mouse[1], 4, 0, - Math.PI * 2 + Math.PI * 2, ); } ctx.fill(); @@ -8042,7 +7948,7 @@ LGraphNode.prototype.executeAction = function(action) this._highlight_input[1], 6, 0, - Math.PI * 2 + Math.PI * 2, ); } ctx.fill(); @@ -8060,32 +7966,32 @@ LGraphNode.prototype.executeAction = function(action) this._highlight_output[1], 6, 0, - Math.PI * 2 + Math.PI * 2, ); } ctx.fill(); } } - //the selection rectangle + // the selection rectangle if (this.dragging_rectangle) { ctx.strokeStyle = "#FFF"; ctx.strokeRect( this.dragging_rectangle[0], this.dragging_rectangle[1], this.dragging_rectangle[2], - this.dragging_rectangle[3] + this.dragging_rectangle[3], ); } - //on top of link center - if(this.over_link_center && this.render_link_tooltip) - this.drawLinkTooltip( ctx, this.over_link_center ); - else - if(this.onDrawLinkTooltip) //to remove - this.onDrawLinkTooltip(ctx,null); + // on top of link center + if(this.over_link_center && this.render_link_tooltip) + this.drawLinkTooltip( ctx, this.over_link_center ); + else + if(this.onDrawLinkTooltip) // to remove + this.onDrawLinkTooltip(ctx,null); - //custom info + // custom info if (this.onDrawForeground) { this.onDrawForeground(ctx, this.visible_rect); } @@ -8093,22 +7999,22 @@ LGraphNode.prototype.executeAction = function(action) ctx.restore(); } - //draws panel in the corner - if (this._graph_stack && this._graph_stack.length) { - this.drawSubgraphPanel( ctx ); - } + // draws panel in the corner + if (this._graph_stack && this._graph_stack.length) { + this.drawSubgraphPanel( ctx ); + } if (this.onDrawOverlay) { this.onDrawOverlay(ctx); } - if (area){ + if (area) { ctx.restore(); } if (ctx.finish2D) { - //this is a function I use in webgl renderer + // this is a function I use in webgl renderer ctx.finish2D(); } }; @@ -8159,7 +8065,7 @@ LGraphNode.prototype.executeAction = function(action) if (input.not_subgraph_input) continue; - //input button clicked + // input button clicked if (this.drawButton(20, y + 2, w - 20, h - 2)) { var type = subnode.constructor.input_node_type || "graph/input"; this.graph.beforeChange(); @@ -8176,8 +8082,7 @@ LGraphNode.prototype.executeAction = function(action) this.node_dragged.pos[0] = this.graph_mouse[0] - 5; this.node_dragged.pos[1] = this.graph_mouse[1] - 5; this.graph.afterChange(); - } - else + } else console.error("graph input node not found:", type); } ctx.fillStyle = "#9C9"; @@ -8191,7 +8096,7 @@ LGraphNode.prototype.executeAction = function(action) ctx.fillText(input.type, 130, y + h * 0.75); y += h; } - //add + button + // add + button if (this.drawButton(20, y + 2, w - 20, h - 2, "+", "#151515", "#222")) { this.showSubgraphPropertiesDialog(subnode); } @@ -8229,7 +8134,7 @@ LGraphNode.prototype.executeAction = function(action) if (output.not_subgraph_input) continue; - //output button clicked + // output button clicked if (this.drawButton(canvas_w - w, y + 2, w - 20, h - 2)) { var type = subnode.constructor.output_node_type || "graph/output"; this.graph.beforeChange(); @@ -8246,8 +8151,7 @@ LGraphNode.prototype.executeAction = function(action) this.node_dragged.pos[0] = this.graph_mouse[0] - 5; this.node_dragged.pos[1] = this.graph_mouse[1] - 5; this.graph.afterChange(); - } - else + } else console.error("graph input node not found:", type); } ctx.fillStyle = "#9C9"; @@ -8261,64 +8165,60 @@ LGraphNode.prototype.executeAction = function(action) ctx.fillText(output.type, canvas_w - w + 130, y + h * 0.75); y += h; } - //add + button + // add + button if (this.drawButton(canvas_w - w, y + 2, w - 20, h - 2, "+", "#151515", "#222")) { this.showSubgraphPropertiesDialogRight(subnode); } } - //Draws a button into the canvas overlay and computes if it was clicked using the immediate gui paradigm - LGraphCanvas.prototype.drawButton = function( x,y,w,h, text, bgcolor, hovercolor, textcolor ) - { - var ctx = this.ctx; - bgcolor = bgcolor || LiteGraph.NODE_DEFAULT_COLOR; - hovercolor = hovercolor || "#555"; - textcolor = textcolor || LiteGraph.NODE_TEXT_COLOR; - var pos = this.ds.convertOffsetToCanvas(this.graph_mouse); - var hover = LiteGraph.isInsideRectangle( pos[0], pos[1], x,y,w,h ); - pos = this.last_click_position ? [this.last_click_position[0], this.last_click_position[1]] : null; + // Draws a button into the canvas overlay and computes if it was clicked using the immediate gui paradigm + LGraphCanvas.prototype.drawButton = function( x,y,w,h, text, bgcolor, hovercolor, textcolor ) { + var ctx = this.ctx; + bgcolor = bgcolor || LiteGraph.NODE_DEFAULT_COLOR; + hovercolor = hovercolor || "#555"; + textcolor = textcolor || LiteGraph.NODE_TEXT_COLOR; + var pos = this.ds.convertOffsetToCanvas(this.graph_mouse); + var hover = LiteGraph.isInsideRectangle( pos[0], pos[1], x,y,w,h ); + pos = this.last_click_position ? [this.last_click_position[0], this.last_click_position[1]] : null; if(pos) { var rect = this.canvas.getBoundingClientRect(); pos[0] -= rect.left; pos[1] -= rect.top; } - var clicked = pos && LiteGraph.isInsideRectangle( pos[0], pos[1], x,y,w,h ); + var clicked = pos && LiteGraph.isInsideRectangle( pos[0], pos[1], x,y,w,h ); - ctx.fillStyle = hover ? hovercolor : bgcolor; - if(clicked) - ctx.fillStyle = "#AAA"; - ctx.beginPath(); - ctx.roundRect(x,y,w,h,[4] ); - ctx.fill(); + ctx.fillStyle = hover ? hovercolor : bgcolor; + if(clicked) + ctx.fillStyle = "#AAA"; + ctx.beginPath(); + ctx.roundRect(x,y,w,h,[4] ); + ctx.fill(); - if(text != null) - { - if(text.constructor == String) - { - ctx.fillStyle = textcolor; - ctx.textAlign = "center"; - ctx.font = ((h * 0.65)|0) + "px Arial"; - ctx.fillText( text, x + w * 0.5,y + h * 0.75 ); - ctx.textAlign = "left"; - } - } + if(text != null) { + if(text.constructor == String) { + ctx.fillStyle = textcolor; + ctx.textAlign = "center"; + ctx.font = ((h * 0.65)|0) + "px Arial"; + ctx.fillText( text, x + w * 0.5,y + h * 0.75 ); + ctx.textAlign = "left"; + } + } - var was_clicked = clicked && !this.block_click; - if(clicked) - this.blockClick(); - return was_clicked; - } + var was_clicked = clicked && !this.block_click; + if(clicked) + this.blockClick(); + return was_clicked; + } - LGraphCanvas.prototype.isAreaClicked = function( x,y,w,h, hold_click ) - { - var pos = this.mouse; - var hover = LiteGraph.isInsideRectangle( pos[0], pos[1], x,y,w,h ); - pos = this.last_click_position; - var clicked = pos && LiteGraph.isInsideRectangle( pos[0], pos[1], x,y,w,h ); - var was_clicked = clicked && !this.block_click; - if(clicked && hold_click) - this.blockClick(); - return was_clicked; - } + LGraphCanvas.prototype.isAreaClicked = function( x,y,w,h, hold_click ) { + var pos = this.mouse; + var hover = LiteGraph.isInsideRectangle( pos[0], pos[1], x,y,w,h ); + pos = this.last_click_position; + var clicked = pos && LiteGraph.isInsideRectangle( pos[0], pos[1], x,y,w,h ); + var was_clicked = clicked && !this.block_click; + if(clicked && hold_click) + this.blockClick(); + return was_clicked; + } /** * draws some useful stats in the corner of the canvas @@ -8333,7 +8233,7 @@ LGraphNode.prototype.executeAction = function(action) ctx.font = "10px Arial"; ctx.fillStyle = "#888"; - ctx.textAlign = "left"; + ctx.textAlign = "left"; if (this.graph) { ctx.fillText( "T: " + this.graph.globaltime.toFixed(2) + "s", 5, 13 * 1 ); ctx.fillText("I: " + this.graph.iteration, 5, 13 * 2 ); @@ -8368,14 +8268,14 @@ LGraphNode.prototype.executeAction = function(action) ctx.start(); } - var viewport = this.viewport || [0,0,ctx.canvas.width,ctx.canvas.height]; + var viewport = this.viewport || [0,0,ctx.canvas.width,ctx.canvas.height]; - //clear + // clear if (this.clear_background) { ctx.clearRect( viewport[0], viewport[1], viewport[2], viewport[3] ); } - //show subgraph stack header + // show subgraph stack header if (this._graph_stack && this._graph_stack.length) { ctx.save(); var parent_graph = this._graph_stack[this._graph_stack.length - 1]; @@ -8395,7 +8295,7 @@ LGraphNode.prototype.executeAction = function(action) ctx.fillText( title + subgraph_node.getTitle(), canvas.width * 0.5, - 40 + 40, ); ctx.restore(); } @@ -8405,28 +8305,26 @@ LGraphNode.prototype.executeAction = function(action) bg_already_painted = this.onRenderBackground(canvas, ctx); } - //reset in case of error - if ( !this.viewport ) - { - ctx.restore(); - ctx.setTransform(1, 0, 0, 1, 0, 0); - } + // reset in case of error + if ( !this.viewport ) { + ctx.restore(); + ctx.setTransform(1, 0, 0, 1, 0, 0); + } this.visible_links.length = 0; if (this.graph) { - //apply transformations + // apply transformations ctx.save(); this.ds.toCanvasContext(ctx); - //render BG - if ( this.ds.scale < 1.5 && !bg_already_painted && this.clear_background_color ) - { + // render BG + if ( this.ds.scale < 1.5 && !bg_already_painted && this.clear_background_color ) { ctx.fillStyle = this.clear_background_color; ctx.fillRect( this.visible_area[0], this.visible_area[1], this.visible_area[2], - this.visible_area[3] + this.visible_area[3], ); } @@ -8441,7 +8339,7 @@ LGraphNode.prototype.executeAction = function(action) } else { ctx.globalAlpha = this.editor_alpha; } - ctx.imageSmoothingEnabled = ctx.imageSmoothingEnabled = false; // ctx.mozImageSmoothingEnabled = + ctx.imageSmoothingEnabled = ctx.imageSmoothingEnabled = false; // ctx.mozImageSmoothingEnabled = if ( !this._bg_img || this._bg_img.name != this.background_image @@ -8469,16 +8367,16 @@ LGraphNode.prototype.executeAction = function(action) this.visible_area[0], this.visible_area[1], this.visible_area[2], - this.visible_area[3] + this.visible_area[3], ); ctx.fillStyle = "transparent"; } ctx.globalAlpha = 1.0; - ctx.imageSmoothingEnabled = ctx.imageSmoothingEnabled = true; //= ctx.mozImageSmoothingEnabled + ctx.imageSmoothingEnabled = ctx.imageSmoothingEnabled = true; // = ctx.mozImageSmoothingEnabled } - //groups + // groups if (this.graph._groups.length && !this.live_mode) { this.drawGroups(canvas, ctx); } @@ -8487,18 +8385,16 @@ LGraphNode.prototype.executeAction = function(action) this.onDrawBackground(ctx, this.visible_area); } if (this.onBackgroundRender) { - //LEGACY - console.error( - "WARNING! onBackgroundRender deprecated, now is named onDrawBackground " - ); + // LEGACY + console.error("WARNING! onBackgroundRender deprecated, now is named onDrawBackground "); this.onBackgroundRender = null; } - //DEBUG: show clipping area - //ctx.fillStyle = "red"; - //ctx.fillRect( this.visible_area[0] + 10, this.visible_area[1] + 10, this.visible_area[2] - 20, this.visible_area[3] - 20); + // DEBUG: show clipping area + // ctx.fillStyle = "red"; + // ctx.fillRect( this.visible_area[0] + 10, this.visible_area[1] + 10, this.visible_area[2] - 20, this.visible_area[3] - 20); - //bg + // bg if (this.render_canvas_border) { ctx.strokeStyle = "#235"; ctx.strokeRect(0, 0, canvas.width, canvas.height); @@ -8513,14 +8409,14 @@ LGraphNode.prototype.executeAction = function(action) ctx.shadowColor = "rgba(0,0,0,0)"; } - //draw connections + // draw connections if (!this.live_mode) { this.drawConnections(ctx); } ctx.shadowColor = "rgba(0,0,0,0)"; - //restore state + // restore state ctx.restore(); } @@ -8529,7 +8425,7 @@ LGraphNode.prototype.executeAction = function(action) } this.dirty_bgcanvas = false; - this.dirty_canvas = true; //to force to repaint the front canvas with the bgcanvas + this.dirty_canvas = true; // to force to repaint the front canvas with the bgcanvas }; var temp_vec2 = new Float32Array(2); @@ -8545,14 +8441,14 @@ LGraphNode.prototype.executeAction = function(action) var color = node.color || node.constructor.color || LiteGraph.NODE_DEFAULT_COLOR; var bgcolor = node.bgcolor || node.constructor.bgcolor || LiteGraph.NODE_DEFAULT_BGCOLOR; - //shadow and glow + // shadow and glow if (node.mouseOver) { glow = true; } - var low_quality = this.ds.scale < 0.6; //zoomed out + var low_quality = this.ds.scale < 0.6; // zoomed out - //only render if it forces it to do it + // only render if it forces it to do it if (this.live_mode) { if (!node.flags.collapsed) { ctx.shadowColor = "transparent"; @@ -8575,7 +8471,7 @@ LGraphNode.prototype.executeAction = function(action) ctx.shadowColor = "transparent"; } - //custom draw collapsed method (draw after shadows because they are affected) + // custom draw collapsed method (draw after shadows because they are affected) if ( node.flags.collapsed && node.onDrawCollapsed && @@ -8584,7 +8480,7 @@ LGraphNode.prototype.executeAction = function(action) return; } - //clip if required (mask) + // clip if required (mask) var shape = node._shape || LiteGraph.BOX_SHAPE; var size = temp_vec2; temp_vec2.set(node.size); @@ -8597,15 +8493,15 @@ LGraphNode.prototype.executeAction = function(action) node._collapsed_width = Math.min( node.size[0], ctx.measureText(title).width + - LiteGraph.NODE_TITLE_HEIGHT * 2 - ); //LiteGraph.NODE_COLLAPSED_WIDTH; + LiteGraph.NODE_TITLE_HEIGHT * 2, + ); // LiteGraph.NODE_COLLAPSED_WIDTH; size[0] = node._collapsed_width; size[1] = 0; } } if (node.clip_area) { - //Start clipping + // Start clipping ctx.save(); ctx.beginPath(); if (shape == LiteGraph.BOX_SHAPE) { @@ -8618,13 +8514,13 @@ LGraphNode.prototype.executeAction = function(action) size[1] * 0.5, size[0] * 0.5, 0, - Math.PI * 2 + Math.PI * 2, ); } ctx.clip(); } - //draw shape + // draw shape if (node.has_errors) { bgcolor = "red"; } @@ -8635,16 +8531,16 @@ LGraphNode.prototype.executeAction = function(action) color, bgcolor, node.is_selected, - node.mouseOver + node.mouseOver, ); ctx.shadowColor = "transparent"; - //draw foreground + // draw foreground if (node.onDrawForeground) { node.onDrawForeground(ctx, this, this.canvas); } - //connection slots + // connection slots ctx.textAlign = horizontal ? "center" : "left"; ctx.font = this.inner_text_font; @@ -8655,20 +8551,20 @@ LGraphNode.prototype.executeAction = function(action) ctx.lineWidth = 1; var max_y = 0; - var slot_pos = new Float32Array(2); //to reuse + var slot_pos = new Float32Array(2); // to reuse - //render inputs and outputs + // render inputs and outputs if (!node.flags.collapsed) { - //input connection slots + // input connection slots if (node.inputs) { for (var i = 0; i < node.inputs.length; i++) { var slot = node.inputs[i]; - + var slot_type = slot.type; var slot_shape = slot.shape; - + ctx.globalAlpha = editor_alpha; - //change opacity of incompatible slots when dragging a connection + // change opacity of incompatible slots when dragging a connection if ( this.connecting_output && !LiteGraph.isValidConnection( slot.type , out_slot.type) ) { ctx.globalAlpha = 0.4 * editor_alpha; } @@ -8692,12 +8588,12 @@ LGraphNode.prototype.executeAction = function(action) ctx.beginPath(); - if (slot_type == "array"){ + if (slot_type == "array") { slot_shape = LiteGraph.GRID_SHAPE; // place in addInput? addOutput instead? } - + var doStroke = true; - + if ( slot.type === LiteGraph.EVENT || slot.shape === LiteGraph.BOX_SHAPE @@ -8707,14 +8603,14 @@ LGraphNode.prototype.executeAction = function(action) pos[0] - 5 + 0.5, pos[1] - 8 + 0.5, 10, - 14 + 14, ); } else { ctx.rect( pos[0] - 6 + 0.5, pos[1] - 5 + 0.5, 14, - 10 + 10, ); } } else if (slot_shape === LiteGraph.ARROW_SHAPE) { @@ -8734,14 +8630,14 @@ LGraphNode.prototype.executeAction = function(action) ctx.rect(pos[0] + 2, pos[1] + 2, 2, 2); doStroke = false; } else { - if(low_quality) - ctx.rect(pos[0] - 4, pos[1] - 4, 8, 8 ); //faster - else - ctx.arc(pos[0], pos[1], 4, 0, Math.PI * 2); + if(low_quality) + ctx.rect(pos[0] - 4, pos[1] - 4, 8, 8 ); // faster + else + ctx.arc(pos[0], pos[1], 4, 0, Math.PI * 2); } ctx.fill(); - //render name + // render name if (render_text) { var text = slot.label != null ? slot.label : slot.name; if (text) { @@ -8756,22 +8652,22 @@ LGraphNode.prototype.executeAction = function(action) } } - //output connection slots + // output connection slots ctx.textAlign = horizontal ? "center" : "right"; ctx.strokeStyle = "black"; if (node.outputs) { for (var i = 0; i < node.outputs.length; i++) { var slot = node.outputs[i]; - + var slot_type = slot.type; var slot_shape = slot.shape; - - //change opacity of incompatible slots when dragging a connection + + // change opacity of incompatible slots when dragging a connection if (this.connecting_input && !LiteGraph.isValidConnection( slot_type , in_slot.type) ) { ctx.globalAlpha = 0.4 * editor_alpha; } - + var pos = node.getConnectionPos(false, i, slot_pos); pos[0] -= node.pos[0]; pos[1] -= node.pos[1]; @@ -8789,14 +8685,14 @@ LGraphNode.prototype.executeAction = function(action) this.default_connection_color_byType[slot_type] || this.default_connection_color.output_off; ctx.beginPath(); - //ctx.rect( node.size[0] - 14,i*14,10,10); + // ctx.rect( node.size[0] - 14,i*14,10,10); - if (slot_type == "array"){ + if (slot_type == "array") { slot_shape = LiteGraph.GRID_SHAPE; } - + var doStroke = true; - + if ( slot_type === LiteGraph.EVENT || slot_shape === LiteGraph.BOX_SHAPE @@ -8806,14 +8702,14 @@ LGraphNode.prototype.executeAction = function(action) pos[0] - 5 + 0.5, pos[1] - 8 + 0.5, 10, - 14 + 14, ); } else { ctx.rect( pos[0] - 6 + 0.5, pos[1] - 5 + 0.5, 14, - 10 + 10, ); } } else if (slot_shape === LiteGraph.ARROW_SHAPE) { @@ -8821,7 +8717,7 @@ LGraphNode.prototype.executeAction = function(action) ctx.lineTo(pos[0] - 4, pos[1] + 6 + 0.5); ctx.lineTo(pos[0] - 4, pos[1] - 6 + 0.5); ctx.closePath(); - } else if (slot_shape === LiteGraph.GRID_SHAPE) { + } else if (slot_shape === LiteGraph.GRID_SHAPE) { ctx.rect(pos[0] - 4, pos[1] - 4, 2, 2); ctx.rect(pos[0] - 1, pos[1] - 4, 2, 2); ctx.rect(pos[0] + 2, pos[1] - 4, 2, 2); @@ -8833,22 +8729,22 @@ LGraphNode.prototype.executeAction = function(action) ctx.rect(pos[0] + 2, pos[1] + 2, 2, 2); doStroke = false; } else { - if(low_quality) - ctx.rect(pos[0] - 4, pos[1] - 4, 8, 8 ); - else - ctx.arc(pos[0], pos[1], 4, 0, Math.PI * 2); + if(low_quality) + ctx.rect(pos[0] - 4, pos[1] - 4, 8, 8 ); + else + ctx.arc(pos[0], pos[1], 4, 0, Math.PI * 2); } - //trigger - //if(slot.node_id != null && slot.slot == -1) + // trigger + // if(slot.node_id != null && slot.slot == -1) // ctx.fillStyle = "#F85"; - //if(slot.links != null && slot.links.length) + // if(slot.links != null && slot.links.length) ctx.fill(); - if(!low_quality && doStroke) - ctx.stroke(); + if(!low_quality && doStroke) + ctx.stroke(); - //render output name + // render output name if (render_text) { var text = slot.label != null ? slot.label : slot.name; if (text) { @@ -8867,11 +8763,11 @@ LGraphNode.prototype.executeAction = function(action) ctx.globalAlpha = 1; if (node.widgets) { - var widgets_y = max_y; + var widgets_y = max_y; if (horizontal || node.widgets_up) { widgets_y = 2; } - if( node.widgets_start_y != null ) + if( node.widgets_start_y != null ) widgets_y = node.widgets_start_y; this.drawNodeWidgets( node, @@ -8879,15 +8775,15 @@ LGraphNode.prototype.executeAction = function(action) ctx, this.node_widget && this.node_widget[0] == node ? this.node_widget[1] - : null + : null, ); } } else if (this.render_collapsed_slots) { - //if collapsed + // if collapsed var input_slot = null; var output_slot = null; - //get first connected slot to render + // get first connected slot to render if (node.inputs) { for (var i = 0; i < node.inputs.length; i++) { var slot = node.inputs[i]; @@ -8910,7 +8806,7 @@ LGraphNode.prototype.executeAction = function(action) if (input_slot) { var x = 0; - var y = LiteGraph.NODE_TITLE_HEIGHT * -0.5; //center + var y = LiteGraph.NODE_TITLE_HEIGHT * -0.5; // center if (horizontal) { x = node._collapsed_width * 0.5; y = -LiteGraph.NODE_TITLE_HEIGHT; @@ -8935,7 +8831,7 @@ LGraphNode.prototype.executeAction = function(action) if (output_slot) { var x = node._collapsed_width; - var y = LiteGraph.NODE_TITLE_HEIGHT * -0.5; //center + var y = LiteGraph.NODE_TITLE_HEIGHT * -0.5; // center if (horizontal) { x = node._collapsed_width * 0.5; y = 0; @@ -8957,7 +8853,7 @@ LGraphNode.prototype.executeAction = function(action) ctx.arc(x, y, 4, 0, Math.PI * 2); } ctx.fill(); - //ctx.stroke(); + // ctx.stroke(); } } @@ -8968,60 +8864,59 @@ LGraphNode.prototype.executeAction = function(action) ctx.globalAlpha = 1.0; }; - //used by this.over_link_center - LGraphCanvas.prototype.drawLinkTooltip = function( ctx, link ) - { - var pos = link._pos; - ctx.fillStyle = "black"; - ctx.beginPath(); - ctx.arc( pos[0], pos[1], 3, 0, Math.PI * 2 ); - ctx.fill(); - - if(link.data == null) - return; - - if(this.onDrawLinkTooltip) - if( this.onDrawLinkTooltip(ctx,link,this) == true ) - return; - - var data = link.data; - var text = null; - - if( data.constructor === Number ) - text = data.toFixed(2); - else if( data.constructor === String ) - text = "\"" + data + "\""; - else if( data.constructor === Boolean ) - text = String(data); - else if (data.toToolTip) - text = data.toToolTip(); - else - text = "[" + data.constructor.name + "]"; - - if(text == null) - return; - text = text.substr(0,30); //avoid weird - - ctx.font = "14px Courier New"; - var info = ctx.measureText(text); - var w = info.width + 20; - var h = 24; - ctx.shadowColor = "black"; - ctx.shadowOffsetX = 2; - ctx.shadowOffsetY = 2; - ctx.shadowBlur = 3; - ctx.fillStyle = "#454"; - ctx.beginPath(); - ctx.roundRect( pos[0] - w*0.5, pos[1] - 15 - h, w, h, [3]); - ctx.moveTo( pos[0] - 10, pos[1] - 15 ); - ctx.lineTo( pos[0] + 10, pos[1] - 15 ); - ctx.lineTo( pos[0], pos[1] - 5 ); - ctx.fill(); + // used by this.over_link_center + LGraphCanvas.prototype.drawLinkTooltip = function( ctx, link ) { + var pos = link._pos; + ctx.fillStyle = "black"; + ctx.beginPath(); + ctx.arc( pos[0], pos[1], 3, 0, Math.PI * 2 ); + ctx.fill(); + + if(link.data == null) + return; + + if(this.onDrawLinkTooltip) + if( this.onDrawLinkTooltip(ctx,link,this) == true ) + return; + + var data = link.data; + var text = null; + + if( data.constructor === Number ) + text = data.toFixed(2); + else if( data.constructor === String ) + text = "\"" + data + "\""; + else if( data.constructor === Boolean ) + text = String(data); + else if (data.toToolTip) + text = data.toToolTip(); + else + text = "[" + data.constructor.name + "]"; + + if(text == null) + return; + text = text.substr(0,30); // avoid weird + + ctx.font = "14px Courier New"; + var info = ctx.measureText(text); + var w = info.width + 20; + var h = 24; + ctx.shadowColor = "black"; + ctx.shadowOffsetX = 2; + ctx.shadowOffsetY = 2; + ctx.shadowBlur = 3; + ctx.fillStyle = "#454"; + ctx.beginPath(); + ctx.roundRect( pos[0] - w*0.5, pos[1] - 15 - h, w, h, [3]); + ctx.moveTo( pos[0] - 10, pos[1] - 15 ); + ctx.lineTo( pos[0] + 10, pos[1] - 15 ); + ctx.lineTo( pos[0], pos[1] - 5 ); + ctx.fill(); ctx.shadowColor = "transparent"; - ctx.textAlign = "center"; - ctx.fillStyle = "#CEC"; - ctx.fillText(text, pos[0], pos[1] - 15 - h * 0.3); - } + ctx.textAlign = "center"; + ctx.fillStyle = "#CEC"; + ctx.fillText(text, pos[0], pos[1] - 15 - h * 0.3); + } /** * draws the shape of the given node in the canvas @@ -9036,16 +8931,16 @@ LGraphNode.prototype.executeAction = function(action) fgcolor, bgcolor, selected, - mouse_over + mouse_over, ) { - //bg rect + // bg rect ctx.strokeStyle = fgcolor; ctx.fillStyle = bgcolor; var title_height = LiteGraph.NODE_TITLE_HEIGHT; var low_quality = this.ds.scale < 0.5; - //render node area depending on shape + // render node area depending on shape var shape = node._shape || node.constructor.shape || LiteGraph.ROUND_SHAPE; @@ -9059,15 +8954,15 @@ LGraphNode.prototype.executeAction = function(action) } var area = tmp_area; - area[0] = 0; //x - area[1] = render_title ? -title_height : 0; //y - area[2] = size[0] + 1; //w - area[3] = render_title ? size[1] + title_height : size[1]; //h + area[0] = 0; // x + area[1] = render_title ? -title_height : 0; // y + area[2] = size[0] + 1; // w + area[3] = render_title ? size[1] + title_height : size[1]; // h var old_alpha = ctx.globalAlpha; - //full node shape - //if(node.flags.collapsed) + // full node shape + // if(node.flags.collapsed) { ctx.beginPath(); if (shape == LiteGraph.BOX_SHAPE || low_quality) { @@ -9081,7 +8976,7 @@ LGraphNode.prototype.executeAction = function(action) area[1], area[2], area[3], - shape == LiteGraph.CARD_SHAPE ? [this.round_radius,this.round_radius,0,0] : [this.round_radius] + shape == LiteGraph.CARD_SHAPE ? [this.round_radius,this.round_radius,0,0] : [this.round_radius], ); } else if (shape == LiteGraph.CIRCLE_SHAPE) { ctx.arc( @@ -9089,18 +8984,17 @@ LGraphNode.prototype.executeAction = function(action) size[1] * 0.5, size[0] * 0.5, 0, - Math.PI * 2 + Math.PI * 2, ); } ctx.fill(); - //separator - if(!node.flags.collapsed && render_title) - { - ctx.shadowColor = "transparent"; - ctx.fillStyle = "rgba(0,0,0,0.2)"; - ctx.fillRect(0, -1, area[2], 2); - } + // separator + if(!node.flags.collapsed && render_title) { + ctx.shadowColor = "transparent"; + ctx.fillStyle = "rgba(0,0,0,0.2)"; + ctx.fillRect(0, -1, area[2], 2); + } } ctx.shadowColor = "transparent"; @@ -9108,9 +9002,9 @@ LGraphNode.prototype.executeAction = function(action) node.onDrawBackground(ctx, this, this.canvas, this.graph_mouse ); } - //title bg (remember, it is rendered ABOVE the node) + // title bg (remember, it is rendered ABOVE the node) if (render_title || title_mode == LiteGraph.TRANSPARENT_TITLE) { - //title bar + // title bar if (node.onDrawTitleBar) { node.onDrawTitleBar( ctx, title_height, size, this.ds.scale, fgcolor ); } else if ( @@ -9127,7 +9021,7 @@ LGraphNode.prototype.executeAction = function(action) if (this.use_gradients) { var grad = LGraphCanvas.gradients[title_color]; if (!grad) { - grad = LGraphCanvas.gradients[ title_color ] = ctx.createLinearGradient(0, 0, 400, 0); + grad = LGraphCanvas.gradients[title_color] = ctx.createLinearGradient(0, 0, 400, 0); grad.addColorStop(0, title_color); // TODO refactor: validate color !! prevent DOMException grad.addColorStop(1, "#000"); } @@ -9136,17 +9030,17 @@ LGraphNode.prototype.executeAction = function(action) ctx.fillStyle = title_color; } - //ctx.globalAlpha = 0.5 * old_alpha; + // ctx.globalAlpha = 0.5 * old_alpha; ctx.beginPath(); if (shape == LiteGraph.BOX_SHAPE || low_quality) { ctx.rect(0, -title_height, size[0] + 1, title_height); - } else if ( shape == LiteGraph.ROUND_SHAPE || shape == LiteGraph.CARD_SHAPE ) { + } else if ( shape == LiteGraph.ROUND_SHAPE || shape == LiteGraph.CARD_SHAPE ) { ctx.roundRect( 0, -title_height, size[0] + 1, title_height, - node.flags.collapsed ? [this.round_radius] : [this.round_radius,this.round_radius,0,0] + node.flags.collapsed ? [this.round_radius] : [this.round_radius,this.round_radius,0,0], ); } ctx.fill(); @@ -9154,16 +9048,16 @@ LGraphNode.prototype.executeAction = function(action) } var colState = false; - if (LiteGraph.node_box_coloured_by_mode){ - if(LiteGraph.NODE_MODES_COLORS[node.mode]){ + if (LiteGraph.node_box_coloured_by_mode) { + if(LiteGraph.NODE_MODES_COLORS[node.mode]) { colState = LiteGraph.NODE_MODES_COLORS[node.mode]; } } - if (LiteGraph.node_box_coloured_when_on){ + if (LiteGraph.node_box_coloured_when_on) { colState = node.action_triggered ? "#FFF" : (node.execute_triggered ? "#AAA" : colState); } - - //title box + + // title box var box_size = 10; if (node.onDrawTitleBox) { node.onDrawTitleBox(ctx, title_height, size, this.ds.scale); @@ -9180,26 +9074,25 @@ LGraphNode.prototype.executeAction = function(action) title_height * -0.5, box_size * 0.5 + 1, 0, - Math.PI * 2 + Math.PI * 2, ); ctx.fill(); } - + ctx.fillStyle = node.boxcolor || colState || LiteGraph.NODE_DEFAULT_BOXCOLOR; - if(low_quality) - ctx.fillRect( title_height * 0.5 - box_size *0.5, title_height * -0.5 - box_size *0.5, box_size , box_size ); - else - { - ctx.beginPath(); - ctx.arc( - title_height * 0.5, - title_height * -0.5, - box_size * 0.5, - 0, - Math.PI * 2 - ); - ctx.fill(); - } + if(low_quality) + ctx.fillRect( title_height * 0.5 - box_size *0.5, title_height * -0.5 - box_size *0.5, box_size , box_size ); + else { + ctx.beginPath(); + ctx.arc( + title_height * 0.5, + title_height * -0.5, + box_size * 0.5, + 0, + Math.PI * 2, + ); + ctx.fill(); + } } else { if (low_quality) { ctx.fillStyle = "black"; @@ -9207,7 +9100,7 @@ LGraphNode.prototype.executeAction = function(action) (title_height - box_size) * 0.5 - 1, (title_height + box_size) * -0.5 - 1, box_size + 2, - box_size + 2 + box_size + 2, ); } ctx.fillStyle = node.boxcolor || colState || LiteGraph.NODE_DEFAULT_BOXCOLOR; @@ -9215,12 +9108,12 @@ LGraphNode.prototype.executeAction = function(action) (title_height - box_size) * 0.5, (title_height + box_size) * -0.5, box_size, - box_size + box_size, ); } ctx.globalAlpha = old_alpha; - //title text + // title text if (node.onDrawTitleText) { node.onDrawTitleText( ctx, @@ -9228,7 +9121,7 @@ LGraphNode.prototype.executeAction = function(action) size, this.ds.scale, this.title_text_font, - selected + selected, ); } if (!low_quality) { @@ -9246,9 +9139,9 @@ LGraphNode.prototype.executeAction = function(action) ctx.textAlign = "left"; var measure = ctx.measureText(title); ctx.fillText( - title.substr(0,20), //avoid urls too long + title.substr(0,20), // avoid urls too long title_height,// + measure.width * 0.5, - LiteGraph.NODE_TITLE_TEXT_Y - title_height + LiteGraph.NODE_TITLE_TEXT_Y - title_height, ); ctx.textAlign = "left"; } else { @@ -9256,41 +9149,40 @@ LGraphNode.prototype.executeAction = function(action) ctx.fillText( title, title_height, - LiteGraph.NODE_TITLE_TEXT_Y - title_height + LiteGraph.NODE_TITLE_TEXT_Y - title_height, ); } } } - //subgraph box - if (!node.flags.collapsed && node.subgraph && !node.skip_subgraph_button) { - var w = LiteGraph.NODE_TITLE_HEIGHT; - var x = node.size[0] - w; - var over = LiteGraph.isInsideRectangle( this.graph_mouse[0] - node.pos[0], this.graph_mouse[1] - node.pos[1], x+2, -w+2, w-4, w-4 ); - ctx.fillStyle = over ? "#888" : "#555"; - if( shape == LiteGraph.BOX_SHAPE || low_quality) - ctx.fillRect(x+2, -w+2, w-4, w-4); - else - { - ctx.beginPath(); - ctx.roundRect(x+2, -w+2, w-4, w-4,[4]); - ctx.fill(); - } - ctx.fillStyle = "#333"; - ctx.beginPath(); - ctx.moveTo(x + w * 0.2, -w * 0.6); - ctx.lineTo(x + w * 0.8, -w * 0.6); - ctx.lineTo(x + w * 0.5, -w * 0.3); - ctx.fill(); - } + // subgraph box + if (!node.flags.collapsed && node.subgraph && !node.skip_subgraph_button) { + var w = LiteGraph.NODE_TITLE_HEIGHT; + var x = node.size[0] - w; + var over = LiteGraph.isInsideRectangle( this.graph_mouse[0] - node.pos[0], this.graph_mouse[1] - node.pos[1], x+2, -w+2, w-4, w-4 ); + ctx.fillStyle = over ? "#888" : "#555"; + if( shape == LiteGraph.BOX_SHAPE || low_quality) + ctx.fillRect(x+2, -w+2, w-4, w-4); + else { + ctx.beginPath(); + ctx.roundRect(x+2, -w+2, w-4, w-4,[4]); + ctx.fill(); + } + ctx.fillStyle = "#333"; + ctx.beginPath(); + ctx.moveTo(x + w * 0.2, -w * 0.6); + ctx.lineTo(x + w * 0.8, -w * 0.6); + ctx.lineTo(x + w * 0.5, -w * 0.3); + ctx.fill(); + } - //custom title render + // custom title render if (node.onDrawTitle) { node.onDrawTitle(ctx); } } - //render selection marker + // render selection marker if (selected) { if (node.onBounding) { node.onBounding(area); @@ -9308,7 +9200,7 @@ LGraphNode.prototype.executeAction = function(action) -6 + area[0], -6 + area[1], 12 + area[2], - 12 + area[3] + 12 + area[3], ); } else if ( shape == LiteGraph.ROUND_SHAPE || @@ -9319,7 +9211,7 @@ LGraphNode.prototype.executeAction = function(action) -6 + area[1], 12 + area[2], 12 + area[3], - [this.round_radius * 2] + [this.round_radius * 2], ); } else if (shape == LiteGraph.CARD_SHAPE) { ctx.roundRect( @@ -9327,7 +9219,7 @@ LGraphNode.prototype.executeAction = function(action) -6 + area[1], 12 + area[2], 12 + area[3], - [this.round_radius * 2,2,this.round_radius * 2,2] + [this.round_radius * 2,2,this.round_radius * 2,2], ); } else if (shape == LiteGraph.CIRCLE_SHAPE) { ctx.arc( @@ -9335,7 +9227,7 @@ LGraphNode.prototype.executeAction = function(action) size[1] * 0.5, size[0] * 0.5 + 6, 0, - Math.PI * 2 + Math.PI * 2, ); } ctx.strokeStyle = LiteGraph.NODE_BOX_OUTLINE_COLOR; @@ -9343,7 +9235,7 @@ LGraphNode.prototype.executeAction = function(action) ctx.strokeStyle = fgcolor; ctx.globalAlpha = 1; } - + // these counter helps in conditioning drawing based on if the node has been executed or an action occurred if (node.execute_triggered>0) node.execute_triggered--; if (node.action_triggered>0) node.action_triggered--; @@ -9367,17 +9259,17 @@ LGraphNode.prototype.executeAction = function(action) margin_area[2] = visible_area[2] + 40; margin_area[3] = visible_area[3] + 40; - //draw connections + // draw connections ctx.lineWidth = this.connections_width; ctx.fillStyle = "#AAA"; ctx.strokeStyle = "#AAA"; ctx.globalAlpha = this.editor_alpha; - //for every node + // for every node var nodes = this.graph._nodes; for (var n = 0, l = nodes.length; n < l; ++n) { var node = nodes[n]; - //for every input (we render just inputs because it is easier as every slot can only have one input) + // for every input (we render just inputs because it is easier as every slot can only have one input) if (!node.inputs || !node.inputs.length) { continue; } @@ -9393,7 +9285,7 @@ LGraphNode.prototype.executeAction = function(action) continue; } - //find link info + // find link info var start_node = this.graph.getNodeById(link.origin_id); if (start_node == null) { continue; @@ -9403,18 +9295,18 @@ LGraphNode.prototype.executeAction = function(action) if (start_node_slot == -1) { start_node_slotpos = [ start_node.pos[0] + 10, - start_node.pos[1] + 10 + start_node.pos[1] + 10, ]; } else { start_node_slotpos = start_node.getConnectionPos( false, start_node_slot, - tempA + tempA, ); } var end_node_slotpos = node.getConnectionPos(true, i, tempB); - //compute link bounding + // compute link bounding link_bounding[0] = start_node_slotpos[0]; link_bounding[1] = start_node_slotpos[1]; link_bounding[2] = end_node_slotpos[0] - start_node_slotpos[0]; @@ -9428,7 +9320,7 @@ LGraphNode.prototype.executeAction = function(action) link_bounding[3] = Math.abs(link_bounding[3]); } - //skip links outside of the visible area of the canvas + // skip links outside of the visible area of the canvas if (!overlapBounding(link_bounding, margin_area)) { continue; } @@ -9454,10 +9346,10 @@ LGraphNode.prototype.executeAction = function(action) 0, null, start_dir, - end_dir + end_dir, ); - //event triggered rendered on top + // event triggered rendered on top if (link && link._last_time && now - link._last_time < 1000) { var f = 2.0 - (now - link._last_time) * 0.002; var tmp = ctx.globalAlpha; @@ -9471,7 +9363,7 @@ LGraphNode.prototype.executeAction = function(action) f, "white", start_dir, - end_dir + end_dir, ); ctx.globalAlpha = tmp; } @@ -9503,13 +9395,13 @@ LGraphNode.prototype.executeAction = function(action) color, start_dir, end_dir, - num_sublines + num_sublines, ) { if (link) { this.visible_links.push(link); } - //choose color + // choose color if (!color && link) { color = link.color || LGraphCanvas.link_type_colors[link.type]; } @@ -9534,7 +9426,7 @@ LGraphNode.prototype.executeAction = function(action) ctx.lineWidth = 0.5; } - //begin line shape + // begin line shape ctx.beginPath(); for (var i = 0; i < num_sublines; i += 1) { var offsety = (i - (num_sublines - 1) * 0.5) * 5; @@ -9579,7 +9471,7 @@ LGraphNode.prototype.executeAction = function(action) b[0] + end_offset_x, b[1] + end_offset_y + offsety, b[0], - b[1] + offsety + b[1] + offsety, ); } else if (this.links_render_mode == LiteGraph.LINEAR_LINK) { ctx.moveTo(a[0], a[1] + offsety); @@ -9618,11 +9510,11 @@ LGraphNode.prototype.executeAction = function(action) var l = 15; ctx.lineTo( a[0] + start_offset_x * l, - a[1] + start_offset_y * l + offsety + a[1] + start_offset_y * l + offsety, ); ctx.lineTo( b[0] + end_offset_x * l, - b[1] + end_offset_y * l + offsety + b[1] + end_offset_y * l + offsety, ); ctx.lineTo(b[0], b[1] + offsety); } else if (this.links_render_mode == LiteGraph.STRAIGHT_LINK) { @@ -9648,10 +9540,10 @@ LGraphNode.prototype.executeAction = function(action) ctx.lineTo(b[0], b[1]); } else { return; - } //unknown + } // unknown } - //rendering the outline of the connection can be a little bit slow + // rendering the outline of the connection can be a little bit slow if ( this.render_connections_border && this.ds.scale > 0.6 && @@ -9664,7 +9556,7 @@ LGraphNode.prototype.executeAction = function(action) ctx.lineWidth = this.connections_width; ctx.fillStyle = ctx.strokeStyle = color; ctx.stroke(); - //end line shape + // end line shape var pos = this.computeConnectionPoint(a, b, 0.5, start_dir, end_dir); if (link && link._pos) { @@ -9672,45 +9564,45 @@ LGraphNode.prototype.executeAction = function(action) link._pos[1] = pos[1]; } - //render arrow in the middle + // render arrow in the middle if ( this.ds.scale >= 0.6 && this.highquality_render && end_dir != LiteGraph.CENTER ) { - //render arrow + // render arrow if (this.render_connection_arrows) { - //compute two points in the connection + // compute two points in the connection var posA = this.computeConnectionPoint( a, b, 0.25, start_dir, - end_dir + end_dir, ); var posB = this.computeConnectionPoint( a, b, 0.26, start_dir, - end_dir + end_dir, ); var posC = this.computeConnectionPoint( a, b, 0.75, start_dir, - end_dir + end_dir, ); var posD = this.computeConnectionPoint( a, b, 0.76, start_dir, - end_dir + end_dir, ); - //compute the angle between them so the arrow points in the right direction + // compute the angle between them so the arrow points in the right direction var angleA = 0; var angleB = 0; if (this.render_curved_connections) { @@ -9720,7 +9612,7 @@ LGraphNode.prototype.executeAction = function(action) angleB = angleA = b[1] > a[1] ? 0 : Math.PI; } - //render arrow + // render arrow ctx.save(); ctx.translate(posA[0], posA[1]); ctx.rotate(angleA); @@ -9741,13 +9633,13 @@ LGraphNode.prototype.executeAction = function(action) ctx.restore(); } - //circle + // circle ctx.beginPath(); ctx.arc(pos[0], pos[1], 5, 0, Math.PI * 2); ctx.fill(); } - //render flowing points + // render flowing points if (flow) { ctx.fillStyle = color; for (var i = 0; i < 5; ++i) { @@ -9757,7 +9649,7 @@ LGraphNode.prototype.executeAction = function(action) b, f, start_dir, - end_dir + end_dir, ); ctx.beginPath(); ctx.arc(pos[0], pos[1], 5, 0, 2 * Math.PI); @@ -9766,13 +9658,13 @@ LGraphNode.prototype.executeAction = function(action) } }; - //returns the link center point based on curvature + // returns the link center point based on curvature LGraphCanvas.prototype.computeConnectionPoint = function( a, b, t, start_dir, - end_dir + end_dir, ) { start_dir = start_dir || LiteGraph.RIGHT; end_dir = end_dir || LiteGraph.LEFT; @@ -9838,21 +9730,21 @@ LGraphNode.prototype.executeAction = function(action) node.pos[0] - LiteGraph.NODE_TITLE_HEIGHT, node.pos[1] - LiteGraph.NODE_TITLE_HEIGHT, LiteGraph.NODE_TITLE_HEIGHT, - LiteGraph.NODE_TITLE_HEIGHT + LiteGraph.NODE_TITLE_HEIGHT, ); if (node.order == 0) { ctx.strokeRect( node.pos[0] - LiteGraph.NODE_TITLE_HEIGHT + 0.5, node.pos[1] - LiteGraph.NODE_TITLE_HEIGHT + 0.5, LiteGraph.NODE_TITLE_HEIGHT, - LiteGraph.NODE_TITLE_HEIGHT + LiteGraph.NODE_TITLE_HEIGHT, ); } ctx.fillStyle = "#FFF"; ctx.fillText( node.order, node.pos[0] + LiteGraph.NODE_TITLE_HEIGHT * -0.5, - node.pos[1] - 6 + node.pos[1] - 6, ); } ctx.globalAlpha = 1; @@ -9866,7 +9758,7 @@ LGraphNode.prototype.executeAction = function(action) node, posY, ctx, - active_widget + active_widget, ) { if (!node.widgets || !node.widgets.length) { return 0; @@ -9881,7 +9773,7 @@ LGraphNode.prototype.executeAction = function(action) var outline_color = LiteGraph.WIDGET_OUTLINE_COLOR; var background_color = LiteGraph.WIDGET_BGCOLOR; var text_color = LiteGraph.WIDGET_TEXT_COLOR; - var secondary_text_color = LiteGraph.WIDGET_SECONDARY_TEXT_COLOR; + var secondary_text_color = LiteGraph.WIDGET_SECONDARY_TEXT_COLOR; var margin = 15; for (var i = 0; i < widgets.length; ++i) { @@ -9894,10 +9786,10 @@ LGraphNode.prototype.executeAction = function(action) ctx.strokeStyle = outline_color; ctx.fillStyle = "#222"; ctx.textAlign = "left"; - //ctx.lineWidth = 2; - if(w.disabled) - ctx.globalAlpha *= 0.5; - var widget_width = w.width || width; + // ctx.lineWidth = 2; + if(w.disabled) + ctx.globalAlpha *= 0.5; + var widget_width = w.width || width; switch (w.type) { case "button": @@ -9907,8 +9799,8 @@ LGraphNode.prototype.executeAction = function(action) this.dirty_canvas = true; } ctx.fillRect(margin, y, widget_width - margin * 2, H); - if(show_text && !w.disabled) - ctx.strokeRect( margin, y, widget_width - margin * 2, H ); + if(show_text && !w.disabled) + ctx.strokeRect( margin, y, widget_width - margin * 2, H ); if (show_text) { ctx.textAlign = "center"; ctx.fillStyle = text_color; @@ -9921,19 +9813,19 @@ LGraphNode.prototype.executeAction = function(action) ctx.fillStyle = background_color; ctx.beginPath(); if (show_text) - ctx.roundRect(margin, y, widget_width - margin * 2, H, [H * 0.5]); - else - ctx.rect(margin, y, widget_width - margin * 2, H ); + ctx.roundRect(margin, y, widget_width - margin * 2, H, [H * 0.5]); + else + ctx.rect(margin, y, widget_width - margin * 2, H ); ctx.fill(); - if(show_text && !w.disabled) - ctx.stroke(); + if(show_text && !w.disabled) + ctx.stroke(); ctx.fillStyle = w.value ? "#89A" : "#333"; ctx.beginPath(); ctx.arc( widget_width - margin * 2, y + H * 0.5, H * 0.36, 0, Math.PI * 2 ); ctx.fill(); if (show_text) { ctx.fillStyle = secondary_text_color; - const label = w.label || w.name; + const label = w.label || w.name; if (label != null) { ctx.fillText(label, margin * 2, y + H * 0.7); } @@ -9944,7 +9836,7 @@ LGraphNode.prototype.executeAction = function(action) ? w.options.on || "true" : w.options.off || "false", widget_width - 40, - y + H * 0.7 + y + H * 0.7, ); } break; @@ -9953,16 +9845,16 @@ LGraphNode.prototype.executeAction = function(action) ctx.fillRect(margin, y, widget_width - margin * 2, H); var range = w.options.max - w.options.min; var nvalue = (w.value - w.options.min) / range; - if(nvalue < 0.0) nvalue = 0.0; - if(nvalue > 1.0) nvalue = 1.0; + if(nvalue < 0.0) nvalue = 0.0; + if(nvalue > 1.0) nvalue = 1.0; ctx.fillStyle = w.options.hasOwnProperty("slider_color") ? w.options.slider_color : (active_widget == w ? "#89A" : "#678"); ctx.fillRect(margin, y, nvalue * (widget_width - margin * 2), H); - if(show_text && !w.disabled) - ctx.strokeRect(margin, y, widget_width - margin * 2, H); + if(show_text && !w.disabled) + ctx.strokeRect(margin, y, widget_width - margin * 2, H); if (w.marker) { var marker_nvalue = (w.marker - w.options.min) / range; - if(marker_nvalue < 0.0) marker_nvalue = 0.0; - if(marker_nvalue > 1.0) marker_nvalue = 1.0; + if(marker_nvalue < 0.0) marker_nvalue = 0.0; + if(marker_nvalue > 1.0) marker_nvalue = 1.0; ctx.fillStyle = w.options.hasOwnProperty("marker_color") ? w.options.marker_color : "#AA9"; ctx.fillRect( margin + marker_nvalue * (widget_width - margin * 2), y, 2, H ); } @@ -9970,13 +9862,11 @@ LGraphNode.prototype.executeAction = function(action) ctx.textAlign = "center"; ctx.fillStyle = text_color; ctx.fillText( - w.label || w.name + " " + Number(w.value).toFixed( - w.options.precision != null - ? w.options.precision - : 3 - ), + w.label || w.name + " " + Number(w.value).toFixed(w.options.precision != null + ? w.options.precision + : 3), widget_width * 0.5, - y + H * 0.7 + y + H * 0.7, ); } break; @@ -9986,56 +9876,52 @@ LGraphNode.prototype.executeAction = function(action) ctx.strokeStyle = outline_color; ctx.fillStyle = background_color; ctx.beginPath(); - if(show_text) - ctx.roundRect(margin, y, widget_width - margin * 2, H, [H * 0.5] ); - else - ctx.rect(margin, y, widget_width - margin * 2, H ); + if(show_text) + ctx.roundRect(margin, y, widget_width - margin * 2, H, [H * 0.5] ); + else + ctx.rect(margin, y, widget_width - margin * 2, H ); ctx.fill(); if (show_text) { - if(!w.disabled) - ctx.stroke(); + if(!w.disabled) + ctx.stroke(); ctx.fillStyle = text_color; - if(!w.disabled) - { - ctx.beginPath(); - ctx.moveTo(margin + 16, y + 5); - ctx.lineTo(margin + 6, y + H * 0.5); - ctx.lineTo(margin + 16, y + H - 5); - ctx.fill(); - ctx.beginPath(); - ctx.moveTo(widget_width - margin - 16, y + 5); - ctx.lineTo(widget_width - margin - 6, y + H * 0.5); - ctx.lineTo(widget_width - margin - 16, y + H - 5); - ctx.fill(); - } + if(!w.disabled) { + ctx.beginPath(); + ctx.moveTo(margin + 16, y + 5); + ctx.lineTo(margin + 6, y + H * 0.5); + ctx.lineTo(margin + 16, y + H - 5); + ctx.fill(); + ctx.beginPath(); + ctx.moveTo(widget_width - margin - 16, y + 5); + ctx.lineTo(widget_width - margin - 6, y + H * 0.5); + ctx.lineTo(widget_width - margin - 16, y + H - 5); + ctx.fill(); + } ctx.fillStyle = secondary_text_color; ctx.fillText(w.label || w.name, margin * 2 + 5, y + H * 0.7); ctx.fillStyle = text_color; ctx.textAlign = "right"; if (w.type == "number") { ctx.fillText( - Number(w.value).toFixed( - w.options.precision !== undefined - ? w.options.precision - : 3 - ), + Number(w.value).toFixed(w.options.precision !== undefined + ? w.options.precision + : 3), widget_width - margin * 2 - 20, - y + H * 0.7 + y + H * 0.7, ); } else { - var v = w.value; - if( w.options.values ) - { - var values = w.options.values; - if( values.constructor === Function ) - values = values(); - if(values && values.constructor !== Array) - v = values[ w.value ]; - } + var v = w.value; + if( w.options.values ) { + var values = w.options.values; + if( values.constructor === Function ) + values = values(); + if(values && values.constructor !== Array) + v = values[w.value]; + } ctx.fillText( v, widget_width - margin * 2 - 20, - y + H * 0.7 + y + H * 0.7, ); } } @@ -10047,28 +9933,28 @@ LGraphNode.prototype.executeAction = function(action) ctx.fillStyle = background_color; ctx.beginPath(); if (show_text) - ctx.roundRect(margin, y, widget_width - margin * 2, H, [H * 0.5]); - else - ctx.rect( margin, y, widget_width - margin * 2, H ); + ctx.roundRect(margin, y, widget_width - margin * 2, H, [H * 0.5]); + else + ctx.rect( margin, y, widget_width - margin * 2, H ); ctx.fill(); - if (show_text) { - if(!w.disabled) - ctx.stroke(); - ctx.save(); - ctx.beginPath(); - ctx.rect(margin, y, widget_width - margin * 2, H); - ctx.clip(); - - //ctx.stroke(); + if (show_text) { + if(!w.disabled) + ctx.stroke(); + ctx.save(); + ctx.beginPath(); + ctx.rect(margin, y, widget_width - margin * 2, H); + ctx.clip(); + + // ctx.stroke(); ctx.fillStyle = secondary_text_color; - const label = w.label || w.name; + const label = w.label || w.name; if (label != null) { ctx.fillText(label, margin * 2, y + H * 0.7); } ctx.fillStyle = text_color; ctx.textAlign = "right"; - ctx.fillText(String(w.value).substr(0,30), widget_width - margin * 2, y + H * 0.7); //30 chars max - ctx.restore(); + ctx.fillText(String(w.value).substr(0,30), widget_width - margin * 2, y + H * 0.7); // 30 chars max + ctx.restore(); } break; default: @@ -10078,11 +9964,11 @@ LGraphNode.prototype.executeAction = function(action) break; } posY += (w.computeSize ? w.computeSize(widget_width)[1] : H) + 4; - ctx.globalAlpha = this.editor_alpha; + ctx.globalAlpha = this.editor_alpha; } ctx.restore(); - ctx.textAlign = "left"; + ctx.textAlign = "left"; }; /** @@ -10093,7 +9979,7 @@ LGraphNode.prototype.executeAction = function(action) node, pos, event, - active_widget + active_widget, ) { if (!node.widgets || !node.widgets.length || (!this.allow_interaction && !node.flags.allow_interaction)) { return null; @@ -10108,22 +9994,22 @@ LGraphNode.prototype.executeAction = function(action) for (var i = 0; i < node.widgets.length; ++i) { var w = node.widgets[i]; - if(!w || w.disabled) - continue; - var widget_height = w.computeSize ? w.computeSize(width)[1] : LiteGraph.NODE_WIDGET_HEIGHT; - var widget_width = w.width || width; - //outside - if ( w != active_widget && - (x < 6 || x > widget_width - 12 || y < w.last_y || y > w.last_y + widget_height || w.last_y === undefined) ) - continue; - - var old_value = w.value; - - //if ( w == active_widget || (x > 6 && x < widget_width - 12 && y > w.last_y && y < w.last_y + widget_height) ) { - //inside widget - switch (w.type) { - case "button": - if (event.type === LiteGraph.pointerevents_method+"down") { + if(!w || w.disabled) + continue; + var widget_height = w.computeSize ? w.computeSize(width)[1] : LiteGraph.NODE_WIDGET_HEIGHT; + var widget_width = w.width || width; + // outside + if ( w != active_widget && + (x < 6 || x > widget_width - 12 || y < w.last_y || y > w.last_y + widget_height || w.last_y === undefined) ) + continue; + + var old_value = w.value; + + // if ( w == active_widget || (x > 6 && x < widget_width - 12 && y > w.last_y && y < w.last_y + widget_height) ) { + // inside widget + switch (w.type) { + case "button": + if (event.type === LiteGraph.pointerevents_method+"down") { if (w.callback) { setTimeout(function() { w.callback(w, that, node, pos, event); @@ -10132,150 +10018,155 @@ LGraphNode.prototype.executeAction = function(action) w.clicked = true; this.dirty_canvas = true; } - break; - case "slider": - var old_value = w.value; - var nvalue = clamp((x - 15) / (widget_width - 30), 0, 1); - if(w.options.read_only) break; - w.value = w.options.min + (w.options.max - w.options.min) * nvalue; - if (old_value != w.value) { - setTimeout(function() { - inner_value_change(w, w.value); - }, 20); - } - this.dirty_canvas = true; - break; - case "number": - case "combo": - var old_value = w.value; - if (event.type == LiteGraph.pointerevents_method+"move" && w.type == "number") { + break; + case "slider": + var old_value = w.value; + var nvalue = clamp((x - 15) / (widget_width - 30), 0, 1); + if(w.options.read_only) break; + w.value = w.options.min + (w.options.max - w.options.min) * nvalue; + if (old_value != w.value) { + setTimeout(function() { + inner_value_change(w, w.value); + }, 20); + } + this.dirty_canvas = true; + break; + case "number": + case "combo": + var old_value = w.value; + if (event.type == LiteGraph.pointerevents_method+"move" && w.type == "number") { if(deltaX) - w.value += deltaX * 0.1 * (w.options.step || 1); - if ( w.options.min != null && w.value < w.options.min ) { - w.value = w.options.min; - } - if ( w.options.max != null && w.value > w.options.max ) { - w.value = w.options.max; - } - } else if (event.type == LiteGraph.pointerevents_method+"down") { - var values = w.options.values; - if (values && values.constructor === Function) { - values = w.options.values(w, node); - } - var values_list = null; - - if( w.type != "number") - values_list = values.constructor === Array ? values : Object.keys(values); - - var delta = x < 40 ? -1 : x > widget_width - 40 ? 1 : 0; - if (w.type == "number") { - w.value += delta * 0.1 * (w.options.step || 1); - if ( w.options.min != null && w.value < w.options.min ) { - w.value = w.options.min; - } - if ( w.options.max != null && w.value > w.options.max ) { - w.value = w.options.max; - } - } else if (delta) { //clicked in arrow, used for combos - var index = -1; - this.last_mouseclick = 0; //avoids dobl click event - if(values.constructor === Object) - index = values_list.indexOf( String( w.value ) ) + delta; - else - index = values_list.indexOf( w.value ) + delta; - if (index >= values_list.length) { - index = values_list.length - 1; - } - if (index < 0) { - index = 0; - } - if( values.constructor === Array ) - w.value = values[index]; - else - w.value = index; - } else { //combo clicked - var text_values = values != values_list ? Object.values(values) : values; - var menu = new LiteGraph.ContextMenu(text_values, { - scale: Math.max(1, this.ds.scale), - event: event, - className: "dark", - callback: inner_clicked.bind(w) - }, - ref_window); - function inner_clicked(v, option, event) { - if(values != values_list) - v = text_values.indexOf(v); - this.value = v; - inner_value_change(this, v); - that.dirty_canvas = true; - return false; - } - } - } //end mousedown - else if(event.type == LiteGraph.pointerevents_method+"up" && w.type == "number") - { - var delta = x < 40 ? -1 : x > widget_width - 40 ? 1 : 0; - if (event.click_time < 200 && delta == 0) { - this.prompt("Value",w.value,function(v) { - // check if v is a valid equation or a number - if (/^[0-9+\-*/()\s]+|\d+\.\d+$/.test(v)) { - try {//solve the equation if possible - v = eval(v); - } catch (e) { } - } - this.value = Number(v); - inner_value_change(this, this.value); - }.bind(w), - event); - } - } - - if( old_value != w.value ) - setTimeout( - function() { - inner_value_change(this, this.value); - }.bind(w), - 20 - ); - this.dirty_canvas = true; - break; - case "toggle": - if (event.type == LiteGraph.pointerevents_method+"down") { - w.value = !w.value; - setTimeout(function() { - inner_value_change(w, w.value); - }, 20); - } - break; - case "string": - case "text": - if (event.type == LiteGraph.pointerevents_method+"down") { - this.prompt("Value",w.value,function(v) { - inner_value_change(this, v); - }.bind(w), - event,w.options ? w.options.multiline : false ); - } - break; - default: - if (w.mouse) { - this.dirty_canvas = w.mouse(event, [x, y], node); - } - break; - } //end switch - - //value changed - if( old_value != w.value ) - { - if(node.onWidgetChanged) - node.onWidgetChanged( w.name,w.value,old_value,w ); + w.value += deltaX * 0.1 * (w.options.step || 1); + if ( w.options.min != null && w.value < w.options.min ) { + w.value = w.options.min; + } + if ( w.options.max != null && w.value > w.options.max ) { + w.value = w.options.max; + } + } else if (event.type == LiteGraph.pointerevents_method+"down") { + var values = w.options.values; + if (values && values.constructor === Function) { + values = w.options.values(w, node); + } + var values_list = null; + + if( w.type != "number") + values_list = values.constructor === Array ? values : Object.keys(values); + + var delta = x < 40 ? -1 : x > widget_width - 40 ? 1 : 0; + if (w.type == "number") { + w.value += delta * 0.1 * (w.options.step || 1); + if ( w.options.min != null && w.value < w.options.min ) { + w.value = w.options.min; + } + if ( w.options.max != null && w.value > w.options.max ) { + w.value = w.options.max; + } + } else if (delta) { // clicked in arrow, used for combos + var index = -1; + this.last_mouseclick = 0; // avoids dobl click event + if(values.constructor === Object) + index = values_list.indexOf( String( w.value ) ) + delta; + else + index = values_list.indexOf( w.value ) + delta; + if (index >= values_list.length) { + index = values_list.length - 1; + } + if (index < 0) { + index = 0; + } + if( values.constructor === Array ) + w.value = values[index]; + else + w.value = index; + } else { // combo clicked + var text_values = values != values_list ? Object.values(values) : values; + var menu = new LiteGraph.ContextMenu( + text_values, { + scale: Math.max(1, this.ds.scale), + event: event, + className: "dark", + callback: inner_clicked.bind(w), + }, + ref_window, + ); + function inner_clicked(v, option, event) { + if(values != values_list) + v = text_values.indexOf(v); + this.value = v; + inner_value_change(this, v); + that.dirty_canvas = true; + return false; + } + } + } else if(event.type == LiteGraph.pointerevents_method+"up" && w.type == "number") { // end mousedown + var delta = x < 40 ? -1 : x > widget_width - 40 ? 1 : 0; + if (event.click_time < 200 && delta == 0) { + this.prompt( + "Value",w.value,function(v) { + // check if v is a valid equation or a number + if (/^[0-9+\-*/()\s]+|\d+\.\d+$/.test(v)) { + try { // solve the equation if possible + v = eval(v); + } catch (e) { + console.error(e); + } + } + this.value = Number(v); + inner_value_change(this, this.value); + }.bind(w), + event, + ); + } + } + + if( old_value != w.value ) + setTimeout( + function() { + inner_value_change(this, this.value); + }.bind(w), + 20, + ); + this.dirty_canvas = true; + break; + case "toggle": + if (event.type == LiteGraph.pointerevents_method+"down") { + w.value = !w.value; + setTimeout(function() { + inner_value_change(w, w.value); + }, 20); + } + break; + case "string": + case "text": + if (event.type == LiteGraph.pointerevents_method+"down") { + this.prompt( + "Value",w.value,function(v) { + inner_value_change(this, v); + }.bind(w), + event,w.options ? w.options.multiline : false, + ); + } + break; + default: + if (w.mouse) { + this.dirty_canvas = w.mouse(event, [x, y], node); + } + break; + } // end switch + + // value changed + if( old_value != w.value ) { + if(node.onWidgetChanged) + node.onWidgetChanged( w.name,w.value,old_value,w ); node.graph._version++; - } + } - return w; - }//end for + return w; + }// end for function inner_value_change(widget, value) { - if(widget.type == "number"){ + if(widget.type == "number") { value = Number(value); } widget.value = value; @@ -10309,7 +10200,7 @@ LGraphNode.prototype.executeAction = function(action) if (!overlapBounding(this.visible_area, group._bounding)) { continue; - } //out of the visible area + } // out of the visible area ctx.fillStyle = group.color || "#335"; ctx.strokeStyle = group.color || "#335"; @@ -10331,7 +10222,7 @@ LGraphNode.prototype.executeAction = function(action) var font_size = group.font_size || LiteGraph.DEFAULT_GROUP_FONT_SIZE; ctx.font = font_size + "px Arial"; - ctx.textAlign = "left"; + ctx.textAlign = "left"; ctx.fillText(group.title, pos[0] + 4, pos[1] + font_size); } @@ -10407,12 +10298,12 @@ LGraphNode.prototype.executeAction = function(action) }; LGraphCanvas.prototype.onNodeSelectionChange = function(node) { - return; //disabled + return; // disabled }; /* this is an implementation for touch not in production and not ready */ - /*LGraphCanvas.prototype.touchHandler = function(event) { + /* LGraphCanvas.prototype.touchHandler = function(event) { //alert("foo"); var touches = event.changedTouches, first = touches[0], @@ -10442,7 +10333,7 @@ LGraphNode.prototype.executeAction = function(action) }else{ var window = this.getCanvasWindow(); } - + var document = window.document; var simulatedEvent = document.createEvent("MouseEvent"); @@ -10511,7 +10402,7 @@ LGraphNode.prototype.executeAction = function(action) "top": top, "right": right, "bottom": bottom, - "left": left + "left": left, }; } /** @@ -10542,7 +10433,7 @@ LGraphNode.prototype.executeAction = function(action) "top": align_to, "right": align_to, "bottom": align_to, - "left": align_to + "left": align_to, } } @@ -10599,41 +10490,48 @@ LGraphNode.prototype.executeAction = function(action) if (!graph) return; - function inner_onMenuAdded(base_category ,prev_menu){ - - var categories = LiteGraph.getNodeTypesCategories(canvas.filter || graph.filter).filter(function(category){return category.startsWith(base_category)}); + function inner_onMenuAdded(base_category ,prev_menu) { + + var categories = LiteGraph.getNodeTypesCategories(canvas.filter || graph.filter).filter(function(category) { + return category.startsWith(base_category) + }); var entries = []; - - categories.map(function(category){ - - if (!category) + + categories.map(function(category) { + + if (!category) return; - + var base_category_regex = new RegExp('^(' + base_category + ')'); var category_name = category.replace(base_category_regex,"").split('/')[0]; - var category_path = base_category === '' ? category_name + '/' : base_category + category_name + '/'; - + var category_path = base_category === '' ? category_name + '/' : base_category + category_name + '/'; + var name = category_name; - if(name.indexOf("::") != -1) //in case it has a namespace like "shader::math/rand" it hides the namespace + if(name.indexOf("::") != -1) // in case it has a namespace like "shader::math/rand" it hides the namespace name = name.split("::")[1]; - - var index = entries.findIndex(function(entry){return entry.value === category_path}); + + var index = entries.findIndex(function(entry) { + return entry.value === category_path + }); if (index === -1) { - entries.push({ value: category_path, content: name, has_submenu: true, callback : function(value, event, mouseEvent, contextMenu){ - inner_onMenuAdded(value.value, contextMenu) - }}); + entries.push({ + value: category_path, content: name, has_submenu: true, callback: function(value, event, mouseEvent, contextMenu) { + inner_onMenuAdded(value.value, contextMenu) + }, + }); } - + }); - + var nodes = LiteGraph.getNodeTypesInCategory(base_category.slice(0, -1), canvas.filter || graph.filter ); - nodes.map(function(node){ - + nodes.map(function(node) { + if (node.skip_list) return; - - var entry = { value: node.type, content: node.title, has_submenu: false , callback : function(value, event, mouseEvent, contextMenu){ - + + var entry = { + value: node.type, content: node.title, has_submenu: false , callback: function(value, event, mouseEvent, contextMenu) { + var first_event = contextMenu.getFirstEvent(); canvas.graph.beforeChange(); var node = LiteGraph.createNode(value.value); @@ -10644,21 +10542,21 @@ LGraphNode.prototype.executeAction = function(action) if(callback) callback(node); canvas.graph.afterChange(); - - } + + }, } - + entries.push(entry); - + }); - + new LiteGraph.ContextMenu( entries, { event: e, parentMenu: prev_menu }, ref_window ); - + } - + inner_onMenuAdded('',prev_menu); return false; - + }; LGraphCanvas.onMenuCollapseAll = function() {}; @@ -10670,7 +10568,7 @@ LGraphNode.prototype.executeAction = function(action) options, e, prev_menu, - node + node, ) { if (!node) { return; @@ -10694,14 +10592,14 @@ LGraphNode.prototype.executeAction = function(action) continue; } var label = entry[0]; - if(!entry[2]) - entry[2] = {}; + if(!entry[2]) + entry[2] = {}; if (entry[2].label) { label = entry[2].label; } - entry[2].removable = true; + entry[2].removable = true; var data = { content: label, value: entry }; if (entry[1] == LiteGraph.ACTION) { data.className = "event"; @@ -10716,7 +10614,7 @@ LGraphNode.prototype.executeAction = function(action) } if (!entries.length) { - console.log("no input entries"); + console.log("no input entries"); return; } @@ -10726,9 +10624,9 @@ LGraphNode.prototype.executeAction = function(action) event: e, callback: inner_clicked, parentMenu: prev_menu, - node: node + node: node, }, - ref_window + ref_window, ); function inner_clicked(v, e, prev) { @@ -10741,14 +10639,14 @@ LGraphNode.prototype.executeAction = function(action) } if (v.value) { - node.graph.beforeChange(); + node.graph.beforeChange(); node.addInput(v.value[0], v.value[1], v.value[2]); if (node.onNodeInputAdd) { // callback to the node when adding a slot node.onNodeInputAdd(v.value); } node.setDirtyCanvas(true, true); - node.graph.afterChange(); + node.graph.afterChange(); } } @@ -10760,7 +10658,7 @@ LGraphNode.prototype.executeAction = function(action) options, e, prev_menu, - node + node, ) { if (!node) { return; @@ -10780,7 +10678,7 @@ LGraphNode.prototype.executeAction = function(action) for (var i=0; i < options.length; i++) { var entry = options[i]; if (!entry) { - //separator? + // separator? entries.push(null); continue; } @@ -10791,14 +10689,14 @@ LGraphNode.prototype.executeAction = function(action) node.findOutputSlot(entry[0]) != -1 ) { continue; - } //skip the ones already on + } // skip the ones already on var label = entry[0]; - if(!entry[2]) - entry[2] = {}; + if(!entry[2]) + entry[2] = {}; if (entry[2].label) { label = entry[2].label; } - entry[2].removable = true; + entry[2].removable = true; var data = { content: label, value: entry }; if (entry[1] == LiteGraph.EVENT) { data.className = "event"; @@ -10810,9 +10708,9 @@ LGraphNode.prototype.executeAction = function(action) if (this.onMenuNodeOutputs) { entries = this.onMenuNodeOutputs(entries); } - if (LiteGraph.do_add_triggers_slots){ //canvas.allow_addOutSlot_onExecuted - if (node.findOutputSlot("onExecuted") == -1){ - entries.push({content: "On Executed", value: ["onExecuted", LiteGraph.EVENT, {nameLocked: true}], className: "event"}); //, opts: {} + if (LiteGraph.do_add_triggers_slots) { // canvas.allow_addOutSlot_onExecuted + if (node.findOutputSlot("onExecuted") == -1) { + entries.push({content: "On Executed", value: ["onExecuted", LiteGraph.EVENT, {nameLocked: true}], className: "event"}); // , opts: {} } } // add callback for modifing the menu elements onMenuNodeOutputs @@ -10831,9 +10729,9 @@ LGraphNode.prototype.executeAction = function(action) event: e, callback: inner_clicked, parentMenu: prev_menu, - node: node + node: node, }, - ref_window + ref_window, ); function inner_clicked(v, e, prev) { @@ -10855,7 +10753,7 @@ LGraphNode.prototype.executeAction = function(action) value && (value.constructor === Object || value.constructor === Array) ) { - //submenu why? + // submenu why? var entries = []; for (var i in value) { entries.push({ content: i, value: value[i] }); @@ -10864,18 +10762,18 @@ LGraphNode.prototype.executeAction = function(action) event: e, callback: inner_clicked, parentMenu: prev_menu, - node: node + node: node, }); return false; } else { - node.graph.beforeChange(); + node.graph.beforeChange(); node.addOutput(v.value[0], v.value[1], v.value[2]); if (node.onNodeOutputAdd) { // a callback to the node when adding a slot node.onNodeOutputAdd(v.value); } node.setDirtyCanvas(true, true); - node.graph.afterChange(); + node.graph.afterChange(); } } @@ -10887,7 +10785,7 @@ LGraphNode.prototype.executeAction = function(action) options, e, prev_menu, - node + node, ) { if (!node || !node.properties) { return; @@ -10900,13 +10798,13 @@ LGraphNode.prototype.executeAction = function(action) var entries = []; for (var i in node.properties) { var value = node.properties[i] !== undefined ? node.properties[i] : " "; - if( typeof value == "object" ) - value = JSON.stringify(value); - var info = node.getPropertyInfo(i); - if(info.type == "enum" || info.type == "combo") - value = LGraphCanvas.getPropertyPrintableValue( value, info.values ); + if( typeof value == "object" ) + value = JSON.stringify(value); + var info = node.getPropertyInfo(i); + if(info.type == "enum" || info.type == "combo") + value = LGraphCanvas.getPropertyPrintableValue( value, info.values ); - //value could contain invalid html characters, clean that + // value could contain invalid html characters, clean that value = LGraphCanvas.decodeHTML(value); entries.push({ content: @@ -10916,7 +10814,7 @@ LGraphNode.prototype.executeAction = function(action) "" + value + "", - value: i + value: i, }); } if (!entries.length) { @@ -10930,9 +10828,9 @@ LGraphNode.prototype.executeAction = function(action) callback: inner_clicked, parentMenu: prev_menu, allow_html: true, - node: node + node: node, }, - ref_window + ref_window, ); function inner_clicked(v, options, e, prev) { @@ -10940,9 +10838,7 @@ LGraphNode.prototype.executeAction = function(action) return; } var rect = this.getBoundingClientRect(); - canvas.showEditPropertyValue(node, v.value, { - position: [rect.left, rect.top] - }); + canvas.showEditPropertyValue(node, v.value, {position: [rect.left, rect.top]}); } return false; @@ -10958,65 +10854,65 @@ LGraphNode.prototype.executeAction = function(action) if (!node) { return; } - - var fApplyMultiNode = function(node){ - node.size = node.computeSize(); - if (node.onResize) - node.onResize(node.size); - } - - var graphcanvas = LGraphCanvas.active_canvas; - if (!graphcanvas.selected_nodes || Object.keys(graphcanvas.selected_nodes).length <= 1){ - fApplyMultiNode(node); - }else{ - for (var i in graphcanvas.selected_nodes) { - fApplyMultiNode(graphcanvas.selected_nodes[i]); - } - } - + + var fApplyMultiNode = function(node) { + node.size = node.computeSize(); + if (node.onResize) + node.onResize(node.size); + } + + var graphcanvas = LGraphCanvas.active_canvas; + if (!graphcanvas.selected_nodes || Object.keys(graphcanvas.selected_nodes).length <= 1) { + fApplyMultiNode(node); + }else{ + for (var i in graphcanvas.selected_nodes) { + fApplyMultiNode(graphcanvas.selected_nodes[i]); + } + } + node.setDirtyCanvas(true, true); }; LGraphCanvas.prototype.showLinkMenu = function(link, e) { var that = this; - // console.log(link); - var node_left = that.graph.getNodeById( link.origin_id ); - var node_right = that.graph.getNodeById( link.target_id ); - var fromType = false; - if (node_left && node_left.outputs && node_left.outputs[link.origin_slot]) fromType = node_left.outputs[link.origin_slot].type; + // console.log(link); + var node_left = that.graph.getNodeById( link.origin_id ); + var node_right = that.graph.getNodeById( link.target_id ); + var fromType = false; + if (node_left && node_left.outputs && node_left.outputs[link.origin_slot]) fromType = node_left.outputs[link.origin_slot].type; var destType = false; - if (node_right && node_right.outputs && node_right.outputs[link.target_slot]) destType = node_right.inputs[link.target_slot].type; - - var options = ["Add Node",null,"Delete",null]; - - + if (node_right && node_right.outputs && node_right.outputs[link.target_slot]) destType = node_right.inputs[link.target_slot].type; + + var options = ["Add Node",null,"Delete",null]; + + var menu = new LiteGraph.ContextMenu(options, { event: e, - title: link.data != null ? link.data.constructor.name : null, - callback: inner_clicked + title: link.data != null ? link.data.constructor.name : null, + callback: inner_clicked, }); function inner_clicked(v,options,e) { switch (v) { case "Add Node": - LGraphCanvas.onMenuAdd(null, null, e, menu, function(node){ - // console.debug("node autoconnect"); - if(!node.inputs || !node.inputs.length || !node.outputs || !node.outputs.length){ - return; - } - // leave the connection type checking inside connectByType - if (node_left.connectByType( link.origin_slot, node, fromType )){ - node.connectByType( link.target_slot, node_right, destType ); + LGraphCanvas.onMenuAdd(null, null, e, menu, function(node) { + // console.debug("node autoconnect"); + if(!node.inputs || !node.inputs.length || !node.outputs || !node.outputs.length) { + return; + } + // leave the connection type checking inside connectByType + if (node_left.connectByType( link.origin_slot, node, fromType )) { + node.connectByType( link.target_slot, node_right, destType ); node.pos[0] -= node.size[0] * 0.5; } - }); - break; - + }); + break; + case "Delete": that.graph.removeLink(link.id); break; default: - /*var nodeCreated = createDefaultNodeForSlot({ nodeFrom: node_left + /* var nodeCreated = createDefaultNodeForSlot({ nodeFrom: node_left ,slotFrom: link.origin_slot ,nodeTo: node ,slotTo: link.target_slot @@ -11029,264 +10925,271 @@ LGraphNode.prototype.executeAction = function(action) return false; }; - - LGraphCanvas.prototype.createDefaultNodeForSlot = function(optPass) { // addNodeMenu for connection + + LGraphCanvas.prototype.createDefaultNodeForSlot = function(optPass) { // addNodeMenu for connection var optPass = optPass || {}; - var opts = Object.assign({ nodeFrom: null // input - ,slotFrom: null // input - ,nodeTo: null // output - ,slotTo: null // output - ,position: [] // pass the event coords - ,nodeType: null // choose a nodetype to add, AUTO to set at first good - ,posAdd:[0,0] // adjust x,y - ,posSizeFix:[0,0] // alpha, adjust the position x,y based on the new node size w,h - } - ,optPass - ); + var opts = Object.assign( + { + nodeFrom: null, // input + slotFrom: null, // input + nodeTo: null, // output + slotTo: null, // output + position: [], // pass the event coords + nodeType: null, // choose a nodetype to add, AUTO to set at first good + posAdd: [0,0], // adjust x,y + posSizeFix: [0,0], // alpha, adjust the position x,y based on the new node size w,h + }, + optPass, + ); var that = this; - + var isFrom = opts.nodeFrom && opts.slotFrom!==null; var isTo = !isFrom && opts.nodeTo && opts.slotTo!==null; - - if (!isFrom && !isTo){ + + if (!isFrom && !isTo) { console.warn("No data passed to createDefaultNodeForSlot "+opts.nodeFrom+" "+opts.slotFrom+" "+opts.nodeTo+" "+opts.slotTo); return false; } - if (!opts.nodeType){ + if (!opts.nodeType) { console.warn("No type to createDefaultNodeForSlot"); return false; } - + var nodeX = isFrom ? opts.nodeFrom : opts.nodeTo; var slotX = isFrom ? opts.slotFrom : opts.slotTo; - + var iSlotConn = false; - switch (typeof slotX){ + switch (typeof slotX) { case "string": iSlotConn = isFrom ? nodeX.findOutputSlot(slotX,false) : nodeX.findInputSlot(slotX,false); slotX = isFrom ? nodeX.outputs[slotX] : nodeX.inputs[slotX]; - break; + break; case "object": // ok slotX iSlotConn = isFrom ? nodeX.findOutputSlot(slotX.name) : nodeX.findInputSlot(slotX.name); - break; + break; case "number": iSlotConn = slotX; slotX = isFrom ? nodeX.outputs[slotX] : nodeX.inputs[slotX]; - break; - case "undefined": + break; + case "undefined": default: // bad ? - //iSlotConn = 0; + // iSlotConn = 0; console.warn("Cant get slot information "+slotX); return false; } - - if (slotX===false || iSlotConn===false){ - console.warn("createDefaultNodeForSlot bad slotX "+slotX+" "+iSlotConn); - } - - // check for defaults nodes for this slottype - var fromSlotType = slotX.type==LiteGraph.EVENT?"_event_":slotX.type; - var slotTypesDefault = isFrom ? LiteGraph.slot_types_default_out : LiteGraph.slot_types_default_in; - if(slotTypesDefault && slotTypesDefault[fromSlotType]){ - if (slotX.link !== null) { - // is connected - }else{ - // is not not connected - } - nodeNewType = false; - if(typeof slotTypesDefault[fromSlotType] == "object" || typeof slotTypesDefault[fromSlotType] == "array"){ - for(var typeX in slotTypesDefault[fromSlotType]){ - if (opts.nodeType == slotTypesDefault[fromSlotType][typeX] || opts.nodeType == "AUTO"){ - nodeNewType = slotTypesDefault[fromSlotType][typeX]; - // console.log("opts.nodeType == slotTypesDefault[fromSlotType][typeX] :: "+opts.nodeType); - break; // -------- - } - } - }else{ - if (opts.nodeType == slotTypesDefault[fromSlotType] || opts.nodeType == "AUTO") nodeNewType = slotTypesDefault[fromSlotType]; - } - if (nodeNewType) { - var nodeNewOpts = false; - if (typeof nodeNewType == "object" && nodeNewType.node){ - nodeNewOpts = nodeNewType; - nodeNewType = nodeNewType.node; - } - - //that.graph.beforeChange(); - - var newNode = LiteGraph.createNode(nodeNewType); - if(newNode){ - // if is object pass options - if (nodeNewOpts){ - if (nodeNewOpts.properties) { - for (var i in nodeNewOpts.properties) { - newNode.addProperty( i, nodeNewOpts.properties[i] ); - } - } - if (nodeNewOpts.inputs) { - newNode.inputs = []; - for (var i in nodeNewOpts.inputs) { - newNode.addOutput( - nodeNewOpts.inputs[i][0], - nodeNewOpts.inputs[i][1] - ); - } - } - if (nodeNewOpts.outputs) { - newNode.outputs = []; - for (var i in nodeNewOpts.outputs) { - newNode.addOutput( - nodeNewOpts.outputs[i][0], - nodeNewOpts.outputs[i][1] - ); - } - } - if (nodeNewOpts.title) { - newNode.title = nodeNewOpts.title; - } - if (nodeNewOpts.json) { - newNode.configure(nodeNewOpts.json); - } - - } - - // add the node - that.graph.add(newNode); - newNode.pos = [ opts.position[0]+opts.posAdd[0]+(opts.posSizeFix[0]?opts.posSizeFix[0]*newNode.size[0]:0) - ,opts.position[1]+opts.posAdd[1]+(opts.posSizeFix[1]?opts.posSizeFix[1]*newNode.size[1]:0)]; //that.last_click_position; //[e.canvasX+30, e.canvasX+5];*/ - - //that.graph.afterChange(); - - // connect the two! - if (isFrom){ - opts.nodeFrom.connectByType( iSlotConn, newNode, fromSlotType ); - }else{ - opts.nodeTo.connectByTypeOutput( iSlotConn, newNode, fromSlotType ); - } - - // if connecting in between - if (isFrom && isTo){ - // TODO - } - - return true; - - }else{ - console.log("failed creating "+nodeNewType); - } - } - } - return false; - } - + + if (slotX===false || iSlotConn===false) { + console.warn("createDefaultNodeForSlot bad slotX "+slotX+" "+iSlotConn); + } + + // check for defaults nodes for this slottype + var fromSlotType = slotX.type==LiteGraph.EVENT?"_event_":slotX.type; + var slotTypesDefault = isFrom ? LiteGraph.slot_types_default_out : LiteGraph.slot_types_default_in; + if(slotTypesDefault && slotTypesDefault[fromSlotType]) { + if (slotX.link !== null) { + // is connected + }else{ + // is not not connected + } + nodeNewType = false; + if(Array.isArray(slotTypesDefault[fromSlotType]) || (typeof slotTypesDefault[fromSlotType] === "object")) { + for(var typeX in slotTypesDefault[fromSlotType]) { + if (opts.nodeType == slotTypesDefault[fromSlotType][typeX] || opts.nodeType == "AUTO") { + nodeNewType = slotTypesDefault[fromSlotType][typeX]; + // console.log("opts.nodeType == slotTypesDefault[fromSlotType][typeX] :: "+opts.nodeType); + break; // -------- + } + } + }else{ + if (opts.nodeType == slotTypesDefault[fromSlotType] || opts.nodeType == "AUTO") nodeNewType = slotTypesDefault[fromSlotType]; + } + if (nodeNewType) { + var nodeNewOpts = false; + if (typeof nodeNewType == "object" && nodeNewType.node) { + nodeNewOpts = nodeNewType; + nodeNewType = nodeNewType.node; + } + + // that.graph.beforeChange(); + + var newNode = LiteGraph.createNode(nodeNewType); + if(newNode) { + // if is object pass options + if (nodeNewOpts) { + if (nodeNewOpts.properties) { + for (var i in nodeNewOpts.properties) { + newNode.addProperty( i, nodeNewOpts.properties[i] ); + } + } + if (nodeNewOpts.inputs) { + newNode.inputs = []; + for (var i in nodeNewOpts.inputs) { + newNode.addOutput( + nodeNewOpts.inputs[i][0], + nodeNewOpts.inputs[i][1], + ); + } + } + if (nodeNewOpts.outputs) { + newNode.outputs = []; + for (var i in nodeNewOpts.outputs) { + newNode.addOutput( + nodeNewOpts.outputs[i][0], + nodeNewOpts.outputs[i][1], + ); + } + } + if (nodeNewOpts.title) { + newNode.title = nodeNewOpts.title; + } + if (nodeNewOpts.json) { + newNode.configure(nodeNewOpts.json); + } + + } + + // add the node + that.graph.add(newNode); + newNode.pos = [ + opts.position[0]+opts.posAdd[0]+(opts.posSizeFix[0]?opts.posSizeFix[0]*newNode.size[0]:0), + opts.position[1]+opts.posAdd[1]+(opts.posSizeFix[1]?opts.posSizeFix[1]*newNode.size[1]:0), + ]; // that.last_click_position; //[e.canvasX+30, e.canvasX+5];*/ + + // that.graph.afterChange(); + + // connect the two! + if (isFrom) { + opts.nodeFrom.connectByType( iSlotConn, newNode, fromSlotType ); + }else{ + opts.nodeTo.connectByTypeOutput( iSlotConn, newNode, fromSlotType ); + } + + // if connecting in between + if (isFrom && isTo) { + // TODO + } + + return true; + + }else{ + console.log("failed creating "+nodeNewType); + } + } + } + return false; + } + LGraphCanvas.prototype.showConnectionMenu = function(optPass) { // addNodeMenu for connection var optPass = optPass || {}; - var opts = Object.assign({ nodeFrom: null // input - ,slotFrom: null // input - ,nodeTo: null // output - ,slotTo: null // output - ,e: null - } - ,optPass - ); + var opts = Object.assign( + { + nodeFrom: null, // input + slotFrom: null, // input + nodeTo: null, // output + slotTo: null, // output + e: null, + } + ,optPass, + ); var that = this; - + var isFrom = opts.nodeFrom && opts.slotFrom; var isTo = !isFrom && opts.nodeTo && opts.slotTo; - - if (!isFrom && !isTo){ + + if (!isFrom && !isTo) { console.warn("No data passed to showConnectionMenu"); return false; } - + var nodeX = isFrom ? opts.nodeFrom : opts.nodeTo; var slotX = isFrom ? opts.slotFrom : opts.slotTo; - + var iSlotConn = false; - switch (typeof slotX){ + switch (typeof slotX) { case "string": iSlotConn = isFrom ? nodeX.findOutputSlot(slotX,false) : nodeX.findInputSlot(slotX,false); slotX = isFrom ? nodeX.outputs[slotX] : nodeX.inputs[slotX]; - break; + break; case "object": // ok slotX iSlotConn = isFrom ? nodeX.findOutputSlot(slotX.name) : nodeX.findInputSlot(slotX.name); - break; + break; case "number": iSlotConn = slotX; slotX = isFrom ? nodeX.outputs[slotX] : nodeX.inputs[slotX]; - break; + break; default: // bad ? - //iSlotConn = 0; + // iSlotConn = 0; console.warn("Cant get slot information "+slotX); return false; } - - var options = ["Add Node",null]; - - if (that.allow_searchbox){ - options.push("Search"); - options.push(null); - } - - // get defaults nodes for this slottype - var fromSlotType = slotX.type==LiteGraph.EVENT?"_event_":slotX.type; - var slotTypesDefault = isFrom ? LiteGraph.slot_types_default_out : LiteGraph.slot_types_default_in; - if(slotTypesDefault && slotTypesDefault[fromSlotType]){ - if(typeof slotTypesDefault[fromSlotType] == "object" || typeof slotTypesDefault[fromSlotType] == "array"){ - for(var typeX in slotTypesDefault[fromSlotType]){ - options.push(slotTypesDefault[fromSlotType][typeX]); - } - }else{ - options.push(slotTypesDefault[fromSlotType]); - } - } - - // build menu + + var options = ["Add Node",null]; + + if (that.allow_searchbox) { + options.push("Search"); + options.push(null); + } + + // get defaults nodes for this slottype + var fromSlotType = slotX.type==LiteGraph.EVENT?"_event_":slotX.type; + var slotTypesDefault = isFrom ? LiteGraph.slot_types_default_out : LiteGraph.slot_types_default_in; + if(slotTypesDefault && slotTypesDefault[fromSlotType]) { + if(Array.isArray(slotTypesDefault[fromSlotType]) || (typeof slotTypesDefault[fromSlotType] === "object")) { + for(var typeX in slotTypesDefault[fromSlotType]) { + options.push(slotTypesDefault[fromSlotType][typeX]); + } + }else{ + options.push(slotTypesDefault[fromSlotType]); + } + } + + // build menu var menu = new LiteGraph.ContextMenu(options, { event: opts.e, - title: (slotX && slotX.name!="" ? (slotX.name + (fromSlotType?" | ":"")) : "")+(slotX && fromSlotType ? fromSlotType : ""), - callback: inner_clicked + title: (slotX && slotX.name!="" ? (slotX.name + (fromSlotType?" | ":"")) : "")+(slotX && fromSlotType ? fromSlotType : ""), + callback: inner_clicked, }); - - // callback + + // callback function inner_clicked(v,options,e) { - //console.log("Process showConnectionMenu selection"); + // console.log("Process showConnectionMenu selection"); switch (v) { case "Add Node": - LGraphCanvas.onMenuAdd(null, null, e, menu, function(node){ - if (isFrom){ + LGraphCanvas.onMenuAdd(null, null, e, menu, function(node) { + if (isFrom) { opts.nodeFrom.connectByType( iSlotConn, node, fromSlotType ); }else{ opts.nodeTo.connectByTypeOutput( iSlotConn, node, fromSlotType ); } }); break; - case "Search": - if(isFrom){ - that.showSearchBox(e,{node_from: opts.nodeFrom, slot_from: slotX, type_filter_in: fromSlotType}); - }else{ - that.showSearchBox(e,{node_to: opts.nodeTo, slot_from: slotX, type_filter_out: fromSlotType}); - } - break; + case "Search": + if(isFrom) { + that.showSearchBox(e,{node_from: opts.nodeFrom, slot_from: slotX, type_filter_in: fromSlotType}); + }else{ + that.showSearchBox(e,{node_to: opts.nodeTo, slot_from: slotX, type_filter_out: fromSlotType}); + } + break; default: - // check for defaults nodes for this slottype - var nodeCreated = that.createDefaultNodeForSlot(Object.assign(opts,{ position: [opts.e.canvasX, opts.e.canvasY] - ,nodeType: v - })); - if (nodeCreated){ - // new node created - //console.log("node "+v+" created") - }else{ - // failed or v is not in defaults - } - break; - } - } - + // check for defaults nodes for this slottype + var nodeCreated = that.createDefaultNodeForSlot(Object.assign(opts,{ + position: [opts.e.canvasX, opts.e.canvasY], + nodeType: v, + })); + if (nodeCreated) { + // new node created + // console.log("node "+v+" created") + }else{ + // failed or v is not in defaults + } + break; + } + } + return false; }; @@ -11297,7 +11200,7 @@ LGraphNode.prototype.executeAction = function(action) var value = node[property]; // TODO refactor :: use createDialog ? - + var dialog = document.createElement("div"); dialog.is_modified = false; dialog.className = "graphdialog"; @@ -11319,7 +11222,7 @@ LGraphNode.prototype.executeAction = function(action) input.addEventListener("keydown", function(e) { dialog.is_modified = true; if (e.keyCode == 27) { - //ESC + // ESC dialog.close(); } else if (e.keyCode == 13) { inner(); // save @@ -11355,18 +11258,18 @@ LGraphNode.prototype.executeAction = function(action) canvas.parentNode.appendChild(dialog); if(input) input.focus(); - + var dialogCloseTimer = null; dialog.addEventListener("mouseleave", function(e) { if(LiteGraph.dialog_close_on_mouse_leave) if (!dialog.is_modified && LiteGraph.dialog_close_on_mouse_leave) - dialogCloseTimer = setTimeout(dialog.close, LiteGraph.dialog_close_on_mouse_leave_delay); //dialog.close(); + dialogCloseTimer = setTimeout(dialog.close, LiteGraph.dialog_close_on_mouse_leave_delay); // dialog.close(); }); dialog.addEventListener("mouseenter", function(e) { if(LiteGraph.dialog_close_on_mouse_leave) if(dialogCloseTimer) clearTimeout(dialogCloseTimer); }); - + function inner() { if(input) setValue(input.value); } @@ -11395,9 +11298,9 @@ LGraphNode.prototype.executeAction = function(action) dialog.is_modified = false; dialog.className = "graphdialog rounded"; if(multiline) - dialog.innerHTML = " "; - else - dialog.innerHTML = " "; + dialog.innerHTML = " "; + else + dialog.innerHTML = " "; dialog.close = function() { that.prompt_box = null; if (dialog.parentNode) { @@ -11408,7 +11311,7 @@ LGraphNode.prototype.executeAction = function(action) var graphcanvas = LGraphCanvas.active_canvas; var canvas = graphcanvas.canvas; canvas.parentNode.appendChild(dialog); - + if (this.ds.scale > 1) { dialog.style.transform = "scale(" + this.ds.scale + ")"; } @@ -11420,21 +11323,21 @@ LGraphNode.prototype.executeAction = function(action) return; if(LiteGraph.dialog_close_on_mouse_leave) if (!dialog.is_modified && LiteGraph.dialog_close_on_mouse_leave) - dialogCloseTimer = setTimeout(dialog.close, LiteGraph.dialog_close_on_mouse_leave_delay); //dialog.close(); + dialogCloseTimer = setTimeout(dialog.close, LiteGraph.dialog_close_on_mouse_leave_delay); // dialog.close(); }); LiteGraph.pointerListenerAdd(dialog,"enter", function(e) { if(LiteGraph.dialog_close_on_mouse_leave) if(dialogCloseTimer) clearTimeout(dialogCloseTimer); }); var selInDia = dialog.querySelectorAll("select"); - if (selInDia){ + if (selInDia) { // if filtering, check focus changed to comboboxes and prevent closing selInDia.forEach(function(selIn) { selIn.addEventListener("click", function(e) { prevent_timeout++; }); selIn.addEventListener("blur", function(e) { - prevent_timeout = 0; + prevent_timeout = 0; }); selIn.addEventListener("change", function(e) { prevent_timeout = -1; @@ -11460,7 +11363,7 @@ LGraphNode.prototype.executeAction = function(action) input.addEventListener("keydown", function(e) { dialog.is_modified = true; if (e.keyCode == 27) { - //ESC + // ESC dialog.close(); } else if (e.keyCode == 13 && e.target.localName != "textarea") { if (callback) { @@ -11509,22 +11412,23 @@ LGraphNode.prototype.executeAction = function(action) LGraphCanvas.search_limit = -1; LGraphCanvas.prototype.showSearchBox = function(event, options) { // proposed defaults - var def_options = { slot_from: null - ,node_from: null - ,node_to: null - ,do_type_filter: LiteGraph.search_filter_enabled // TODO check for registered_slot_[in/out]_types not empty // this will be checked for functionality enabled : filter on slot type, in and out - ,type_filter_in: false // these are default: pass to set initially set values - ,type_filter_out: false - ,show_general_if_none_on_typefilter: true - ,show_general_after_typefiltered: true - ,hide_on_mouse_leave: LiteGraph.search_hide_on_mouse_leave - ,show_all_if_empty: true - ,show_all_on_open: LiteGraph.search_show_all_on_open - }; + var def_options = { + slot_from: null, + node_from: null, + node_to: null, + do_type_filter: LiteGraph.search_filter_enabled, // TODO check for registered_slot_[in/out]_types not empty // this will be checked for functionality enabled : filter on slot type, in and out + type_filter_in: false, // these are default: pass to set initially set values + type_filter_out: false, + show_general_if_none_on_typefilter: true, + show_general_after_typefiltered: true, + hide_on_mouse_leave: LiteGraph.search_hide_on_mouse_leave, + show_all_if_empty: true, + show_all_on_open: LiteGraph.search_show_all_on_open, + }; options = Object.assign(def_options, options || {}); - - //console.log(options); - + + // console.log(options); + var that = this; var input_html = ""; var graphcanvas = LGraphCanvas.active_canvas; @@ -11534,35 +11438,34 @@ LGraphNode.prototype.executeAction = function(action) var dialog = document.createElement("div"); dialog.className = "litegraph litesearchbox graphdialog rounded"; dialog.innerHTML = "Search "; - if (options.do_type_filter){ + if (options.do_type_filter) { dialog.innerHTML += ""; dialog.innerHTML += ""; } dialog.innerHTML += "
"; - + if( root_document.fullscreenElement ) - root_document.fullscreenElement.appendChild(dialog); - else - { - root_document.body.appendChild(dialog); - root_document.body.style.overflow = "hidden"; - } + root_document.fullscreenElement.appendChild(dialog); + else { + root_document.body.appendChild(dialog); + root_document.body.style.overflow = "hidden"; + } // dialog element has been appended - - if (options.do_type_filter){ + + if (options.do_type_filter) { var selIn = dialog.querySelector(".slot_in_type_filter"); var selOut = dialog.querySelector(".slot_out_type_filter"); } - + dialog.close = function() { that.search_box = null; - this.blur(); + this.blur(); canvas.focus(); - root_document.body.style.overflow = ""; + root_document.body.style.overflow = ""; setTimeout(function() { that.canvas.focus(); - }, 20); //important, if canvas loses focus keys wont be captured + }, 20); // important, if canvas loses focus keys wont be captured if (dialog.parentNode) { dialog.parentNode.removeChild(dialog); } @@ -11573,7 +11476,7 @@ LGraphNode.prototype.executeAction = function(action) } // hide on mouse leave - if(options.hide_on_mouse_leave){ + if(options.hide_on_mouse_leave) { var prevent_timeout = false; var timeout_close = null; LiteGraph.pointerListenerAdd(dialog,"enter", function(e) { @@ -11583,7 +11486,7 @@ LGraphNode.prototype.executeAction = function(action) } }); LiteGraph.pointerListenerAdd(dialog,"leave", function(e) { - if (prevent_timeout){ + if (prevent_timeout) { return; } timeout_close = setTimeout(function() { @@ -11591,12 +11494,12 @@ LGraphNode.prototype.executeAction = function(action) }, 500); }); // if filtering, check focus changed to comboboxes and prevent closing - if (options.do_type_filter){ + if (options.do_type_filter) { selIn.addEventListener("click", function(e) { prevent_timeout++; }); selIn.addEventListener("blur", function(e) { - prevent_timeout = 0; + prevent_timeout = 0; }); selIn.addEventListener("change", function(e) { prevent_timeout = -1; @@ -11605,7 +11508,7 @@ LGraphNode.prototype.executeAction = function(action) prevent_timeout++; }); selOut.addEventListener("blur", function(e) { - prevent_timeout = 0; + prevent_timeout = 0; }); selOut.addEventListener("change", function(e) { prevent_timeout = -1; @@ -11632,13 +11535,13 @@ LGraphNode.prototype.executeAction = function(action) }); input.addEventListener("keydown", function(e) { if (e.keyCode == 38) { - //UP + // UP changeSelection(false); } else if (e.keyCode == 40) { - //DOWN + // DOWN changeSelection(true); } else if (e.keyCode == 27) { - //ESC + // ESC dialog.close(); } else if (e.keyCode == 13) { refreshHelper(); @@ -11658,67 +11561,67 @@ LGraphNode.prototype.executeAction = function(action) } e.preventDefault(); e.stopPropagation(); - e.stopImmediatePropagation(); - return true; + e.stopImmediatePropagation(); + return true; }); } - + // if should filter on type, load and fill selected and choose elements if passed - if (options.do_type_filter){ - if (selIn){ + if (options.do_type_filter) { + if (selIn) { var aSlots = LiteGraph.slot_types_in; var nSlots = aSlots.length; // this for object :: Object.keys(aSlots).length; - + if (options.type_filter_in == LiteGraph.EVENT || options.type_filter_in == LiteGraph.ACTION) options.type_filter_in = "_event_"; /* this will filter on * .. but better do it manually in case else if(options.type_filter_in === "" || options.type_filter_in === 0) options.type_filter_in = "*";*/ - - for (var iK=0; iK (rect.height - 200)) + // To avoid out of screen problems + if(event.layerY > (rect.height - 200)) helper.style.maxHeight = (rect.height - event.layerY - 20) + "px"; - /* + /* var offsetx = -20; var offsety = -20; if (rect) { @@ -11761,12 +11664,10 @@ LGraphNode.prototype.executeAction = function(action) name = extra.type; } - graphcanvas.graph.beforeChange(); + graphcanvas.graph.beforeChange(); var node = LiteGraph.createNode(name); if (node) { - node.pos = graphcanvas.convertEventToCanvasOffset( - event - ); + node.pos = graphcanvas.convertEventToCanvasOffset(event); graphcanvas.graph.add(node, false); } @@ -11781,7 +11682,7 @@ LGraphNode.prototype.executeAction = function(action) for (var i in extra.data.inputs) { node.addOutput( extra.data.inputs[i][0], - extra.data.inputs[i][1] + extra.data.inputs[i][1], ); } } @@ -11790,7 +11691,7 @@ LGraphNode.prototype.executeAction = function(action) for (var i in extra.data.outputs) { node.addOutput( extra.data.outputs[i][0], - extra.data.outputs[i][1] + extra.data.outputs[i][1], ); } } @@ -11804,56 +11705,56 @@ LGraphNode.prototype.executeAction = function(action) } // join node after inserting - if (options.node_from){ + if (options.node_from) { var iS = false; - switch (typeof options.slot_from){ + switch (typeof options.slot_from) { case "string": - iS = options.node_from.findOutputSlot(options.slot_from); - break; + iS = options.node_from.findOutputSlot(options.slot_from); + break; case "object": - if (options.slot_from.name){ + if (options.slot_from.name) { iS = options.node_from.findOutputSlot(options.slot_from.name); }else{ iS = -1; } if (iS==-1 && typeof options.slot_from.slot_index !== "undefined") iS = options.slot_from.slot_index; - break; + break; case "number": iS = options.slot_from; - break; + break; default: iS = 0; // try with first if no name set } - if (typeof options.node_from.outputs[iS] !== "undefined"){ - if (iS!==false && iS>-1){ + if (typeof options.node_from.outputs[iS] !== "undefined") { + if (iS!==false && iS>-1) { options.node_from.connectByType( iS, node, options.node_from.outputs[iS].type ); } }else{ // console.warn("cant find slot " + options.slot_from); } } - if (options.node_to){ + if (options.node_to) { var iS = false; - switch (typeof options.slot_from){ + switch (typeof options.slot_from) { case "string": - iS = options.node_to.findInputSlot(options.slot_from); - break; + iS = options.node_to.findInputSlot(options.slot_from); + break; case "object": - if (options.slot_from.name){ + if (options.slot_from.name) { iS = options.node_to.findInputSlot(options.slot_from.name); }else{ iS = -1; } if (iS==-1 && typeof options.slot_from.slot_index !== "undefined") iS = options.slot_from.slot_index; - break; + break; case "number": iS = options.slot_from; - break; + break; default: iS = 0; // try with first if no name set } - if (typeof options.node_to.inputs[iS] !== "undefined"){ - if (iS!==false && iS>-1){ + if (typeof options.node_to.inputs[iS] !== "undefined") { + if (iS!==false && iS>-1) { // try connection options.node_to.connectByTypeOutput(iS,node,options.node_to.inputs[iS].type); } @@ -11861,7 +11762,7 @@ LGraphNode.prototype.executeAction = function(action) // console.warn("cant find slot_nodeTO " + options.slot_from); } } - + graphcanvas.graph.afterChange(); } } @@ -11912,26 +11813,26 @@ LGraphNode.prototype.executeAction = function(action) } else { var c = 0; str = str.toLowerCase(); - var filter = graphcanvas.filter || graphcanvas.graph.filter; + var filter = graphcanvas.filter || graphcanvas.graph.filter; // filter by type preprocess - if(options.do_type_filter && that.search_box){ + if(options.do_type_filter && that.search_box) { var sIn = that.search_box.querySelector(".slot_in_type_filter"); var sOut = that.search_box.querySelector(".slot_out_type_filter"); }else{ var sIn = false; var sOut = false; } - - //extras + + // extras for (var i in LiteGraph.searchbox_extras) { var extra = LiteGraph.searchbox_extras[i]; if ((!options.show_all_if_empty || str) && extra.desc.toLowerCase().indexOf(str) === -1) { continue; } - var ctor = LiteGraph.registered_node_types[ extra.type ]; - if( ctor && ctor.filter != filter ) - continue; + var ctor = LiteGraph.registered_node_types[extra.type]; + if( ctor && ctor.filter != filter ) + continue; if( ! inner_test_filter(extra.type) ) continue; addResult( extra.desc, "searchbox_extra" ); @@ -11940,33 +11841,33 @@ LGraphNode.prototype.executeAction = function(action) } } - var filtered = null; - if (Array.prototype.filter) { //filter supported - var keys = Object.keys( LiteGraph.registered_node_types ); //types + var filtered = null; + if (Array.prototype.filter) { // filter supported + var keys = Object.keys( LiteGraph.registered_node_types ); // types var filtered = keys.filter( inner_test_filter ); } else { - filtered = []; + filtered = []; for (var i in LiteGraph.registered_node_types) { - if( inner_test_filter(i) ) - filtered.push(i); + if( inner_test_filter(i) ) + filtered.push(i); + } + } + + for (var i = 0; i < filtered.length; i++) { + addResult(filtered[i]); + if ( LGraphCanvas.search_limit !== -1 && c++ > LGraphCanvas.search_limit ) { + break; } } - for (var i = 0; i < filtered.length; i++) { - addResult(filtered[i]); - if ( LGraphCanvas.search_limit !== -1 && c++ > LGraphCanvas.search_limit ) { - break; - } - } - // add general type if filtering if (options.show_general_after_typefiltered - && (sIn.value || sOut.value) - ){ + && (sIn.value || sOut.value) + ) { filtered_extra = []; for (var i in LiteGraph.registered_node_types) { - if( inner_test_filter(i, {inTypeOverride: sIn&&sIn.value?"*":false, outTypeOverride: sOut&&sOut.value?"*":false}) ) - filtered_extra.push(i); + if( inner_test_filter(i, {inTypeOverride: sIn&&sIn.value?"*":false, outTypeOverride: sOut&&sOut.value?"*":false}) ) + filtered_extra.push(i); } for (var i = 0; i < filtered_extra.length; i++) { addResult(filtered_extra[i], "generic_type"); @@ -11975,15 +11876,15 @@ LGraphNode.prototype.executeAction = function(action) } } } - + // check il filtering gave no results - if ((sIn.value || sOut.value) && + if ((sIn.value || sOut.value) && ( (helper.childNodes.length == 0 && options.show_general_if_none_on_typefilter) ) - ){ + ) { filtered_extra = []; for (var i in LiteGraph.registered_node_types) { - if( inner_test_filter(i, {skipFilter: true}) ) - filtered_extra.push(i); + if( inner_test_filter(i, {skipFilter: true}) ) + filtered_extra.push(i); } for (var i = 0; i < filtered_extra.length; i++) { addResult(filtered_extra[i], "not_in_filter"); @@ -11992,57 +11893,57 @@ LGraphNode.prototype.executeAction = function(action) } } } - - function inner_test_filter( type, optsIn ) - { + + function inner_test_filter( type, optsIn ) { var optsIn = optsIn || {}; - var optsDef = { skipFilter: false - ,inTypeOverride: false - ,outTypeOverride: false - }; + var optsDef = { + skipFilter: false, + inTypeOverride: false, + outTypeOverride: false, + }; var opts = Object.assign(optsDef,optsIn); - var ctor = LiteGraph.registered_node_types[ type ]; - if(filter && ctor.filter != filter ) - return false; + var ctor = LiteGraph.registered_node_types[type]; + if(filter && ctor.filter != filter ) + return false; if ((!options.show_all_if_empty || str) && type.toLowerCase().indexOf(str) === -1) return false; - + // filter by slot IN, OUT types - if(options.do_type_filter && !opts.skipFilter){ + if(options.do_type_filter && !opts.skipFilter) { var sType = type; - + var sV = sIn.value; if (opts.inTypeOverride!==false) sV = opts.inTypeOverride; - //if (sV.toLowerCase() == "_event_") sV = LiteGraph.EVENT; // -1 - - if(sIn && sV){ - //console.log("will check filter against "+sV); - if (LiteGraph.registered_slot_in_types[sV] && LiteGraph.registered_slot_in_types[sV].nodes){ // type is stored - //console.debug("check "+sType+" in "+LiteGraph.registered_slot_in_types[sV].nodes); + // if (sV.toLowerCase() == "_event_") sV = LiteGraph.EVENT; // -1 + + if(sIn && sV) { + // console.log("will check filter against "+sV); + if (LiteGraph.registered_slot_in_types[sV] && LiteGraph.registered_slot_in_types[sV].nodes) { // type is stored + // console.debug("check "+sType+" in "+LiteGraph.registered_slot_in_types[sV].nodes); var doesInc = LiteGraph.registered_slot_in_types[sV].nodes.includes(sType); - if (doesInc!==false){ - //console.log(sType+" HAS "+sV); + if (doesInc!==false) { + // console.log(sType+" HAS "+sV); }else{ - /*console.debug(LiteGraph.registered_slot_in_types[sV]); + /* console.debug(LiteGraph.registered_slot_in_types[sV]); console.log(+" DONT includes "+type);*/ return false; } } } - + var sV = sOut.value; if (opts.outTypeOverride!==false) sV = opts.outTypeOverride; - //if (sV.toLowerCase() == "_event_") sV = LiteGraph.EVENT; // -1 - - if(sOut && sV){ - //console.log("search will check filter against "+sV); - if (LiteGraph.registered_slot_out_types[sV] && LiteGraph.registered_slot_out_types[sV].nodes){ // type is stored - //console.debug("check "+sType+" in "+LiteGraph.registered_slot_out_types[sV].nodes); + // if (sV.toLowerCase() == "_event_") sV = LiteGraph.EVENT; // -1 + + if(sOut && sV) { + // console.log("search will check filter against "+sV); + if (LiteGraph.registered_slot_out_types[sV] && LiteGraph.registered_slot_out_types[sV].nodes) { // type is stored + // console.debug("check "+sType+" in "+LiteGraph.registered_slot_out_types[sV].nodes); var doesInc = LiteGraph.registered_slot_out_types[sV].nodes.includes(sType); - if (doesInc!==false){ - //console.log(sType+" HAS "+sV); + if (doesInc!==false) { + // console.log(sType+" HAS "+sV); }else{ - /*console.debug(LiteGraph.registered_slot_out_types[sV]); + /* console.debug(LiteGraph.registered_slot_out_types[sV]); console.log(+" DONT includes "+type);*/ return false; } @@ -12050,7 +11951,7 @@ LGraphNode.prototype.executeAction = function(action) } } return true; - } + } } function addResult(type, className) { @@ -12083,7 +11984,7 @@ LGraphNode.prototype.executeAction = function(action) var that = this; var info = node.getPropertyInfo(property); - var type = info.type; + var type = info.type; var input_html = ""; @@ -12093,8 +11994,8 @@ LGraphNode.prototype.executeAction = function(action) input_html = ""; - var textarea = panel.alt_content.querySelector("textarea"); - var fDoneWith = function(){ - panel.toggleAltContent(false); //if(node_prop_div) node_prop_div.style.display = "block"; // panel.close(); - panel.toggleFooterVisibility(true); - textarea.parentNode.removeChild(textarea); - panel.classList.add("settings"); - panel.classList.remove("centered"); - inner_refresh(); - } - textarea.value = node.properties[propname]; - textarea.addEventListener("keydown", function(e){ - if(e.code == "Enter" && e.ctrlKey ) - { - node.setProperty(propname, textarea.value); - fDoneWith(); - } - }); - panel.toggleAltContent(true); - panel.toggleFooterVisibility(false); - textarea.style.height = "calc(100% - 40px)"; - /*}*/ - var assign = panel.addButton( "Assign", function(){ - node.setProperty(propname, textarea.value); + panel.alt_content.innerHTML = ""; + var textarea = panel.alt_content.querySelector("textarea"); + var fDoneWith = function() { + panel.toggleAltContent(false); // if(node_prop_div) node_prop_div.style.display = "block"; // panel.close(); + panel.toggleFooterVisibility(true); + textarea.parentNode.removeChild(textarea); + panel.classList.add("settings"); + panel.classList.remove("centered"); + inner_refresh(); + } + textarea.value = node.properties[propname]; + textarea.addEventListener("keydown", function(e) { + if(e.code == "Enter" && e.ctrlKey ) { + node.setProperty(propname, textarea.value); + fDoneWith(); + } + }); + panel.toggleAltContent(true); + panel.toggleFooterVisibility(false); + textarea.style.height = "calc(100% - 40px)"; + /* }*/ + var assign = panel.addButton( "Assign", function() { + node.setProperty(propname, textarea.value); fDoneWith(); - }); - panel.alt_content.appendChild(assign); //panel.content.appendChild(assign); - var button = panel.addButton( "Close", fDoneWith); - button.style.float = "right"; - panel.alt_content.appendChild(button); // panel.content.appendChild(button); - } + }); + panel.alt_content.appendChild(assign); // panel.content.appendChild(assign); + var button = panel.addButton( "Close", fDoneWith); + button.style.float = "right"; + panel.alt_content.appendChild(button); // panel.content.appendChild(button); + } - inner_refresh(); + inner_refresh(); - this.canvas.parentNode.appendChild( panel ); - } - - LGraphCanvas.prototype.showSubgraphPropertiesDialog = function(node) - { - console.log("showing subgraph properties dialog"); - - var old_panel = this.canvas.parentNode.querySelector(".subgraph_dialog"); - if(old_panel) - old_panel.close(); - - var panel = this.createPanel("Subgraph Inputs",{closable:true, width: 500}); - panel.node = node; - panel.classList.add("subgraph_dialog"); - - function inner_refresh() - { - panel.clear(); - - //show currents - if(node.inputs) - for(var i = 0; i < node.inputs.length; ++i) - { - var input = node.inputs[i]; - if(input.not_subgraph_input) - continue; - var html = " "; - var elem = panel.addHTML(html,"subgraph_property"); - elem.dataset["name"] = input.name; - elem.dataset["slot"] = i; - elem.querySelector(".name").innerText = input.name; - elem.querySelector(".type").innerText = input.type; - elem.querySelector("button").addEventListener("click",function(e){ - node.removeInput( Number( this.parentNode.dataset["slot"] ) ); - inner_refresh(); - }); - } - } - - //add extra - var html = " + NameType"; - var elem = panel.addHTML(html,"subgraph_property extra", true); - elem.querySelector("button").addEventListener("click", function(e){ - var elem = this.parentNode; - var name = elem.querySelector(".name").value; - var type = elem.querySelector(".type").value; - if(!name || node.findInputSlot(name) != -1) - return; - node.addInput(name,type); - elem.querySelector(".name").value = ""; - elem.querySelector(".type").value = ""; - inner_refresh(); - }); - - inner_refresh(); - this.canvas.parentNode.appendChild(panel); - return panel; - } + this.canvas.parentNode.appendChild( panel ); + } + + LGraphCanvas.prototype.showSubgraphPropertiesDialog = function(node) { + console.log("showing subgraph properties dialog"); + + var old_panel = this.canvas.parentNode.querySelector(".subgraph_dialog"); + if(old_panel) + old_panel.close(); + + var panel = this.createPanel("Subgraph Inputs",{closable: true, width: 500}); + panel.node = node; + panel.classList.add("subgraph_dialog"); + + function inner_refresh() { + panel.clear(); + + // show currents + if(node.inputs) + for(var i = 0; i < node.inputs.length; ++i) { + var input = node.inputs[i]; + if(input.not_subgraph_input) + continue; + var html = " "; + var elem = panel.addHTML(html,"subgraph_property"); + elem.dataset["name"] = input.name; + elem.dataset["slot"] = i; + elem.querySelector(".name").innerText = input.name; + elem.querySelector(".type").innerText = input.type; + elem.querySelector("button").addEventListener("click",function(e) { + node.removeInput( Number( this.parentNode.dataset["slot"] ) ); + inner_refresh(); + }); + } + } + + // add extra + var html = " + NameType"; + var elem = panel.addHTML(html,"subgraph_property extra", true); + elem.querySelector("button").addEventListener("click", function(e) { + var elem = this.parentNode; + var name = elem.querySelector(".name").value; + var type = elem.querySelector(".type").value; + if(!name || node.findInputSlot(name) != -1) + return; + node.addInput(name,type); + elem.querySelector(".name").value = ""; + elem.querySelector(".type").value = ""; + inner_refresh(); + }); + + inner_refresh(); + this.canvas.parentNode.appendChild(panel); + return panel; + } LGraphCanvas.prototype.showSubgraphPropertiesDialogRight = function (node) { // console.log("showing subgraph properties dialog"); @@ -12847,7 +12729,7 @@ LGraphNode.prototype.executeAction = function(action) function inner_refresh() { panel.clear(); - //show currents + // show currents if (node.outputs) for (var i = 0; i < node.outputs.length; ++i) { var input = node.outputs[i]; @@ -12866,7 +12748,7 @@ LGraphNode.prototype.executeAction = function(action) } } - //add extra + // add extra var html = " + NameType"; var elem = panel.addHTML(html, "subgraph_property extra", true); elem.querySelector(".name").addEventListener("keydown", function (e) { @@ -12893,38 +12775,36 @@ LGraphNode.prototype.executeAction = function(action) this.canvas.parentNode.appendChild(panel); return panel; } - LGraphCanvas.prototype.checkPanels = function() - { - if(!this.canvas) - return; - var panels = this.canvas.parentNode.querySelectorAll(".litegraph.dialog"); - for(var i = 0; i < panels.length; ++i) - { - var panel = panels[i]; - if( !panel.node ) - continue; - if( !panel.node.graph || panel.graph != this.graph ) - panel.close(); - } - } + LGraphCanvas.prototype.checkPanels = function() { + if(!this.canvas) + return; + var panels = this.canvas.parentNode.querySelectorAll(".litegraph.dialog"); + for(var i = 0; i < panels.length; ++i) { + var panel = panels[i]; + if( !panel.node ) + continue; + if( !panel.node.graph || panel.graph != this.graph ) + panel.close(); + } + } LGraphCanvas.onMenuNodeCollapse = function(value, options, e, menu, node) { - node.graph.beforeChange(/*?*/); - - var fApplyMultiNode = function(node){ - node.collapse(); - } - - var graphcanvas = LGraphCanvas.active_canvas; - if (!graphcanvas.selected_nodes || Object.keys(graphcanvas.selected_nodes).length <= 1){ - fApplyMultiNode(node); - }else{ - for (var i in graphcanvas.selected_nodes) { - fApplyMultiNode(graphcanvas.selected_nodes[i]); - } - } - - node.graph.afterChange(/*?*/); + node.graph.beforeChange(/* ?*/); + + var fApplyMultiNode = function(node) { + node.collapse(); + } + + var graphcanvas = LGraphCanvas.active_canvas; + if (!graphcanvas.selected_nodes || Object.keys(graphcanvas.selected_nodes).length <= 1) { + fApplyMultiNode(node); + }else{ + for (var i in graphcanvas.selected_nodes) { + fApplyMultiNode(graphcanvas.selected_nodes[i]); + } + } + + node.graph.afterChange(/* ?*/); }; LGraphCanvas.onMenuNodePin = function(value, options, e, menu, node) { @@ -12934,7 +12814,7 @@ LGraphNode.prototype.executeAction = function(action) LGraphCanvas.onMenuNodeMode = function(value, options, e, menu, node) { new LiteGraph.ContextMenu( LiteGraph.NODE_MODES, - { event: e, callback: inner_clicked, parentMenu: menu, node: node } + { event: e, callback: inner_clicked, parentMenu: menu, node: node }, ); function inner_clicked(v) { @@ -12942,23 +12822,23 @@ LGraphNode.prototype.executeAction = function(action) return; } var kV = Object.values(LiteGraph.NODE_MODES).indexOf(v); - var fApplyMultiNode = function(node){ - if (kV>=0 && LiteGraph.NODE_MODES[kV]) - node.changeMode(kV); - else{ - console.warn("unexpected mode: "+v); - node.changeMode(LiteGraph.ALWAYS); - } - } - - var graphcanvas = LGraphCanvas.active_canvas; - if (!graphcanvas.selected_nodes || Object.keys(graphcanvas.selected_nodes).length <= 1){ - fApplyMultiNode(node); - }else{ - for (var i in graphcanvas.selected_nodes) { - fApplyMultiNode(graphcanvas.selected_nodes[i]); - } - } + var fApplyMultiNode = function(node) { + if (kV>=0 && LiteGraph.NODE_MODES[kV]) + node.changeMode(kV); + else{ + console.warn("unexpected mode: "+v); + node.changeMode(LiteGraph.ALWAYS); + } + } + + var graphcanvas = LGraphCanvas.active_canvas; + if (!graphcanvas.selected_nodes || Object.keys(graphcanvas.selected_nodes).length <= 1) { + fApplyMultiNode(node); + }else{ + for (var i in graphcanvas.selected_nodes) { + fApplyMultiNode(graphcanvas.selected_nodes[i]); + } + } } return false; @@ -12973,7 +12853,7 @@ LGraphNode.prototype.executeAction = function(action) values.push({ value: null, content: - "No color" + "No color", }); for (var i in LGraphCanvas.node_colors) { @@ -12987,7 +12867,7 @@ LGraphNode.prototype.executeAction = function(action) color.bgcolor + "'>" + i + - "" + "", }; values.push(value); } @@ -12995,7 +12875,7 @@ LGraphNode.prototype.executeAction = function(action) event: e, callback: inner_clicked, parentMenu: menu, - node: node + node: node, }); function inner_clicked(v) { @@ -13004,29 +12884,29 @@ LGraphNode.prototype.executeAction = function(action) } var color = v.value ? LGraphCanvas.node_colors[v.value] : null; - - var fApplyColor = function(node){ - if (color) { - if (node.constructor === LiteGraph.LGraphGroup) { - node.color = color.groupcolor; - } else { - node.color = color.color; - node.bgcolor = color.bgcolor; - } - } else { - delete node.color; - delete node.bgcolor; - } - } - - var graphcanvas = LGraphCanvas.active_canvas; - if (!graphcanvas.selected_nodes || Object.keys(graphcanvas.selected_nodes).length <= 1){ - fApplyColor(node); - }else{ - for (var i in graphcanvas.selected_nodes) { - fApplyColor(graphcanvas.selected_nodes[i]); - } - } + + var fApplyColor = function(node) { + if (color) { + if (node.constructor === LiteGraph.LGraphGroup) { + node.color = color.groupcolor; + } else { + node.color = color.color; + node.bgcolor = color.bgcolor; + } + } else { + delete node.color; + delete node.bgcolor; + } + } + + var graphcanvas = LGraphCanvas.active_canvas; + if (!graphcanvas.selected_nodes || Object.keys(graphcanvas.selected_nodes).length <= 1) { + fApplyColor(node); + }else{ + for (var i in graphcanvas.selected_nodes) { + fApplyColor(graphcanvas.selected_nodes[i]); + } + } node.setDirtyCanvas(true, true); } @@ -13042,29 +12922,29 @@ LGraphNode.prototype.executeAction = function(action) event: e, callback: inner_clicked, parentMenu: menu, - node: node + node: node, }); function inner_clicked(v) { if (!node) { return; } - node.graph.beforeChange(/*?*/); //node - - var fApplyMultiNode = function(node){ - node.shape = v; - } + node.graph.beforeChange(/* ?*/); // node - var graphcanvas = LGraphCanvas.active_canvas; - if (!graphcanvas.selected_nodes || Object.keys(graphcanvas.selected_nodes).length <= 1){ - fApplyMultiNode(node); - }else{ - for (var i in graphcanvas.selected_nodes) { - fApplyMultiNode(graphcanvas.selected_nodes[i]); - } - } - - node.graph.afterChange(/*?*/); //node + var fApplyMultiNode = function(node) { + node.shape = v; + } + + var graphcanvas = LGraphCanvas.active_canvas; + if (!graphcanvas.selected_nodes || Object.keys(graphcanvas.selected_nodes).length <= 1) { + fApplyMultiNode(node); + }else{ + for (var i in graphcanvas.selected_nodes) { + fApplyMultiNode(graphcanvas.selected_nodes[i]); + } + } + + node.graph.afterChange(/* ?*/); // node node.setDirtyCanvas(true); } @@ -13076,83 +12956,83 @@ LGraphNode.prototype.executeAction = function(action) throw "no node passed"; } - var graph = node.graph; - graph.beforeChange(); - - - var fApplyMultiNode = function(node){ - if (node.removable === false) { - return; - } - graph.remove(node); - } - - var graphcanvas = LGraphCanvas.active_canvas; - if (!graphcanvas.selected_nodes || Object.keys(graphcanvas.selected_nodes).length <= 1){ - fApplyMultiNode(node); - }else{ - for (var i in graphcanvas.selected_nodes) { - fApplyMultiNode(graphcanvas.selected_nodes[i]); - } - } - - graph.afterChange(); + var graph = node.graph; + graph.beforeChange(); + + + var fApplyMultiNode = function(node) { + if (node.removable === false) { + return; + } + graph.remove(node); + } + + var graphcanvas = LGraphCanvas.active_canvas; + if (!graphcanvas.selected_nodes || Object.keys(graphcanvas.selected_nodes).length <= 1) { + fApplyMultiNode(node); + }else{ + for (var i in graphcanvas.selected_nodes) { + fApplyMultiNode(graphcanvas.selected_nodes[i]); + } + } + + graph.afterChange(); node.setDirtyCanvas(true, true); }; LGraphCanvas.onMenuNodeToSubgraph = function(value, options, e, menu, node) { - var graph = node.graph; - var graphcanvas = LGraphCanvas.active_canvas; - if(!graphcanvas) //?? - return; + var graph = node.graph; + var graphcanvas = LGraphCanvas.active_canvas; + if(!graphcanvas) // ?? + return; - var nodes_list = Object.values( graphcanvas.selected_nodes || {} ); - if( !nodes_list.length ) - nodes_list = [ node ]; + var nodes_list = Object.values( graphcanvas.selected_nodes || {} ); + if( !nodes_list.length ) + nodes_list = [ node ]; - var subgraph_node = LiteGraph.createNode("graph/subgraph"); - subgraph_node.pos = node.pos.concat(); - graph.add(subgraph_node); + var subgraph_node = LiteGraph.createNode("graph/subgraph"); + subgraph_node.pos = node.pos.concat(); + graph.add(subgraph_node); - subgraph_node.buildFromNodes( nodes_list ); + subgraph_node.buildFromNodes( nodes_list ); - graphcanvas.deselectAllNodes(); + graphcanvas.deselectAllNodes(); node.setDirtyCanvas(true, true); }; LGraphCanvas.onMenuNodeClone = function(value, options, e, menu, node) { - - node.graph.beforeChange(); - - var newSelected = {}; - - var fApplyMultiNode = function(node){ - if (node.clonable === false) { - return; - } - var newnode = node.clone(); - if (!newnode) { - return; - } - newnode.pos = [node.pos[0] + 5, node.pos[1] + 5]; - node.graph.add(newnode); - newSelected[newnode.id] = newnode; - } - - var graphcanvas = LGraphCanvas.active_canvas; - if (!graphcanvas.selected_nodes || Object.keys(graphcanvas.selected_nodes).length <= 1){ - fApplyMultiNode(node); - }else{ - for (var i in graphcanvas.selected_nodes) { - fApplyMultiNode(graphcanvas.selected_nodes[i]); - } - } - - if(Object.keys(newSelected).length){ - graphcanvas.selectNodes(newSelected); - } - - node.graph.afterChange(); + + node.graph.beforeChange(); + + var newSelected = {}; + + var fApplyMultiNode = function(node) { + if (node.clonable === false) { + return; + } + var newnode = node.clone(); + if (!newnode) { + return; + } + newnode.pos = [node.pos[0] + 5, node.pos[1] + 5]; + node.graph.add(newnode); + newSelected[newnode.id] = newnode; + } + + var graphcanvas = LGraphCanvas.active_canvas; + if (!graphcanvas.selected_nodes || Object.keys(graphcanvas.selected_nodes).length <= 1) { + fApplyMultiNode(node); + }else{ + for (var i in graphcanvas.selected_nodes) { + fApplyMultiNode(graphcanvas.selected_nodes[i]); + } + } + + if(Object.keys(newSelected).length) { + graphcanvas.selectNodes(newSelected); + } + + node.graph.afterChange(); node.setDirtyCanvas(true, true); }; @@ -13165,17 +13045,17 @@ LGraphNode.prototype.executeAction = function(action) pale_blue: { color: "#2a363b", bgcolor: "#3f5159", - groupcolor: "#3f789e" + groupcolor: "#3f789e", }, cyan: { color: "#233", bgcolor: "#355", groupcolor: "#8AA" }, purple: { color: "#323", bgcolor: "#535", groupcolor: "#a1309b" }, yellow: { color: "#432", bgcolor: "#653", groupcolor: "#b58b2a" }, - black: { color: "#222", bgcolor: "#000", groupcolor: "#444" } + black: { color: "#222", bgcolor: "#000", groupcolor: "#444" }, }; LGraphCanvas.prototype.getCanvasMenuOptions = function() { var options = null; - var that = this; + var that = this; if (this.getMenuOptions) { options = this.getMenuOptions(); } else { @@ -13183,13 +13063,13 @@ LGraphNode.prototype.executeAction = function(action) { content: "Add Node", has_submenu: true, - callback: LGraphCanvas.onMenuAdd + callback: LGraphCanvas.onMenuAdd, }, { content: "Add Group", callback: LGraphCanvas.onGroupAdd }, - //{ content: "Arrange", callback: that.graph.arrange }, - //{content:"Collapse All", callback: LGraphCanvas.onMenuCollapseAll } + // { content: "Arrange", callback: that.graph.arrange }, + // {content:"Collapse All", callback: LGraphCanvas.onMenuCollapseAll } ]; - /*if (LiteGraph.showCanvasOptions){ + /* if (LiteGraph.showCanvasOptions){ options.push({ content: "Options", callback: that.showShowGraphOptionsPanel }); }*/ @@ -13204,7 +13084,7 @@ LGraphNode.prototype.executeAction = function(action) if (this._graph_stack && this._graph_stack.length > 0) { options.push(null, { content: "Close subgraph", - callback: this.closeSubgraph.bind(this) + callback: this.closeSubgraph.bind(this), }); } } @@ -13219,7 +13099,7 @@ LGraphNode.prototype.executeAction = function(action) return options; }; - //called by processContextMenu to extract the menu list + // called by processContextMenu to extract the menu list LGraphCanvas.prototype.getNodeMenuOptions = function(node) { var options = null; @@ -13231,52 +13111,50 @@ LGraphNode.prototype.executeAction = function(action) content: "Inputs", has_submenu: true, disabled: true, - callback: LGraphCanvas.showMenuNodeOptionalInputs + callback: LGraphCanvas.showMenuNodeOptionalInputs, }, { content: "Outputs", has_submenu: true, disabled: true, - callback: LGraphCanvas.showMenuNodeOptionalOutputs + callback: LGraphCanvas.showMenuNodeOptionalOutputs, }, null, { content: "Properties", has_submenu: true, - callback: LGraphCanvas.onShowMenuNodeProperties + callback: LGraphCanvas.onShowMenuNodeProperties, }, null, { content: "Title", - callback: LGraphCanvas.onShowPropertyEditor + callback: LGraphCanvas.onShowPropertyEditor, }, { content: "Mode", has_submenu: true, - callback: LGraphCanvas.onMenuNodeMode + callback: LGraphCanvas.onMenuNodeMode, }]; - if(node.resizable !== false){ - options.push({ - content: "Resize", callback: LGraphCanvas.onMenuResizeNode - }); + if(node.resizable !== false) { + options.push({content: "Resize", callback: LGraphCanvas.onMenuResizeNode}); } options.push( { content: "Collapse", - callback: LGraphCanvas.onMenuNodeCollapse + callback: LGraphCanvas.onMenuNodeCollapse, }, { content: "Pin", callback: LGraphCanvas.onMenuNodePin }, { content: "Colors", has_submenu: true, - callback: LGraphCanvas.onMenuNodeColors + callback: LGraphCanvas.onMenuNodeColors, }, { content: "Shapes", has_submenu: true, - callback: LGraphCanvas.onMenuNodeShapes + callback: LGraphCanvas.onMenuNodeShapes, }, - null + null, ); } @@ -13305,16 +13183,16 @@ LGraphNode.prototype.executeAction = function(action) if (node.clonable !== false) { options.push({ content: "Clone", - callback: LGraphCanvas.onMenuNodeClone + callback: LGraphCanvas.onMenuNodeClone, }); } - - if(0) //TODO - options.push({ - content: "To Subgraph", - callback: LGraphCanvas.onMenuNodeToSubgraph - }); - + /* + if(0) // TODO + options.push({ + content: "To Subgraph", + callback: LGraphCanvas.onMenuNodeToSubgraph, + }); + */ if (Object.keys(this.selected_nodes).length > 1) { options.push({ content: "Align Selected To", @@ -13323,11 +13201,11 @@ LGraphNode.prototype.executeAction = function(action) }) } - options.push(null, { - content: "Remove", - disabled: !(node.removable !== false && !node.block_delete ), - callback: LGraphCanvas.onMenuNodeRemove - }); + options.push(null, { + content: "Remove", + disabled: !(node.removable !== false && !node.block_delete ), + callback: LGraphCanvas.onMenuNodeRemove, + }); if (node.graph && node.graph.onGetNodeMenuOptions) { node.graph.onGetNodeMenuOptions(options, node); @@ -13342,16 +13220,16 @@ LGraphNode.prototype.executeAction = function(action) { content: "Color", has_submenu: true, - callback: LGraphCanvas.onMenuNodeColors + callback: LGraphCanvas.onMenuNodeColors, }, { content: "Font size", property: "font_size", type: "Number", - callback: LGraphCanvas.onShowPropertyEditor + callback: LGraphCanvas.onShowPropertyEditor, }, null, - { content: "Remove", callback: LGraphCanvas.onMenuNodeRemove } + { content: "Remove", callback: LGraphCanvas.onMenuNodeRemove }, ]; return o; @@ -13366,13 +13244,13 @@ LGraphNode.prototype.executeAction = function(action) var options = { event: event, callback: inner_option_clicked, - extra: node + extra: node, }; - if(node) - options.title = node.type; + if(node) + options.title = node.type; - //check if mouse is in input + // check if mouse is in input var slot = null; if (node) { slot = node.getSlotInPosition(event.canvasX, event.canvasY); @@ -13380,7 +13258,7 @@ LGraphNode.prototype.executeAction = function(action) } if (slot) { - //on slot + // on slot menu_info = []; if (node.getSlotMenuOptions) { menu_info = node.getSlotMenuOptions(slot); @@ -13394,17 +13272,15 @@ LGraphNode.prototype.executeAction = function(action) menu_info.push({ content: "Disconnect Links", slot: slot }); } var _slot = slot.input || slot.output; - if (_slot.removable){ - menu_info.push( - _slot.locked - ? "Cannot remove" - : { content: "Remove Slot", slot: slot } - ); - } - if (!_slot.nameLocked){ - menu_info.push({ content: "Rename Slot", slot: slot }); - } - + if (_slot.removable) { + menu_info.push(_slot.locked + ? "Cannot remove" + : { content: "Remove Slot", slot: slot }); + } + if (!_slot.nameLocked) { + menu_info.push({ content: "Rename Slot", slot: slot }); + } + } options.title = (slot.input ? slot.input.type : slot.output.type) || "*"; @@ -13416,30 +13292,30 @@ LGraphNode.prototype.executeAction = function(action) } } else { if (node) { - //on node + // on node menu_info = this.getNodeMenuOptions(node); } else { menu_info = this.getCanvasMenuOptions(); var group = this.graph.getGroupOnPos( event.canvasX, - event.canvasY + event.canvasY, ); if (group) { - //on group + // on group menu_info.push(null, { content: "Edit Group", has_submenu: true, submenu: { title: "Group", extra: group, - options: this.getGroupMenuOptions(group) - } + options: this.getGroupMenuOptions(group), + }, }); } } } - //show menu + // show menu if (!menu_info) { return; } @@ -13478,14 +13354,14 @@ LGraphNode.prototype.executeAction = function(action) : node.getOutputInfo(info.slot); var dialog = that.createDialog( "Name", - options + options, ); var input = dialog.querySelector("input"); if (input && slot_info) { input.value = slot_info.label || ""; } - var inner = function(){ - node.graph.beforeChange(); + var inner = function() { + node.graph.beforeChange(); if (input.value) { if (slot_info) { slot_info.label = input.value; @@ -13499,7 +13375,7 @@ LGraphNode.prototype.executeAction = function(action) input.addEventListener("keydown", function(e) { dialog.is_modified = true; if (e.keyCode == 27) { - //ESC + // ESC dialog.close(); } else if (e.keyCode == 13) { inner(); // save @@ -13512,12 +13388,12 @@ LGraphNode.prototype.executeAction = function(action) input.focus(); } - //if(v.callback) + // if(v.callback) // return v.callback.call(that, node, options, e, menu, that, event ); } }; - //API ************************************************* + // API ************************************************* function compareObjects(a, b) { for (var i in a) { if (a[i] != b[i]) { @@ -13529,9 +13405,7 @@ LGraphNode.prototype.executeAction = function(action) LiteGraph.compareObjects = compareObjects; function distance(a, b) { - return Math.sqrt( - (b[0] - a[0]) * (b[0] - a[0]) + (b[1] - a[1]) * (b[1] - a[1]) - ); + return Math.sqrt((b[0] - a[0]) * (b[0] - a[0]) + (b[1] - a[1]) * (b[1] - a[1])); } LiteGraph.distance = distance; @@ -13558,7 +13432,7 @@ LGraphNode.prototype.executeAction = function(action) } LiteGraph.isInsideRectangle = isInsideRectangle; - //[minx,miny,maxx,maxy] + // [minx,miny,maxx,maxy] function growBounding(bounding, x, y) { if (x < bounding[0]) { bounding[0] = x; @@ -13574,7 +13448,7 @@ LGraphNode.prototype.executeAction = function(action) } LiteGraph.growBounding = growBounding; - //point inside bounding box + // point inside bounding box function isInsideBounding(p, bb) { if ( p[0] < bb[0][0] || @@ -13588,7 +13462,7 @@ LGraphNode.prototype.executeAction = function(action) } LiteGraph.isInsideBounding = isInsideBounding; - //bounding overlap, format: [ startx, starty, width, height ] + // bounding overlap, format: [ startx, starty, width, height ] function overlapBounding(a, b) { var A_end_x = a[0] + a[2]; var A_end_y = a[1] + a[3]; @@ -13607,13 +13481,13 @@ LGraphNode.prototype.executeAction = function(action) } LiteGraph.overlapBounding = overlapBounding; - //Convert a hex value to its decimal value - the inputted hex must be in the + // Convert a hex value to its decimal value - the inputted hex must be in the // format of a hex triplet - the kind we use for HTML colours. The function // will return an array with three values. function hex2num(hex) { if (hex.charAt(0) == "#") { hex = hex.slice(1); - } //Remove the '#' char - if there is one. + } // Remove the '#' char - if there is one. hex = hex.toUpperCase(); var hex_alphabets = "0123456789ABCDEF"; var value = new Array(3); @@ -13630,7 +13504,7 @@ LGraphNode.prototype.executeAction = function(action) LiteGraph.hex2num = hex2num; - //Give a array with three values as the argument and the function will return + // Give a array with three values as the argument and the function will return // the corresponding hex triplet. function num2hex(triplet) { var hex_alphabets = "0123456789ABCDEF"; @@ -13666,12 +13540,10 @@ LGraphNode.prototype.executeAction = function(action) this.options = options; var that = this; - //to link a menu with its parent + // to link a menu with its parent if (options.parentMenu) { if (options.parentMenu.constructor !== this.constructor) { - console.error( - "parentMenu must be of class ContextMenu, ignoring it" - ); + console.error("parentMenu must be of class ContextMenu, ignoring it"); options.parentMenu = null; } else { this.parentMenu = options.parentMenu; @@ -13680,16 +13552,14 @@ LGraphNode.prototype.executeAction = function(action) } } - var eventClass = null; - if(options.event) //use strings because comparing classes between windows doesnt work - eventClass = options.event.constructor.name; + var eventClass = null; + if(options.event) // use strings because comparing classes between windows doesnt work + eventClass = options.event.constructor.name; if ( eventClass !== "MouseEvent" && eventClass !== "CustomEvent" && eventClass !== "PointerEvent" ) { - console.error( - "Event passed to ContextMenu is not of type MouseEvent or CustomEvent. Ignoring it. ("+eventClass+")" - ); + console.error("Event passed to ContextMenu is not of type MouseEvent or CustomEvent. Ignoring it. ("+eventClass+")"); options.event = null; } @@ -13703,40 +13573,42 @@ LGraphNode.prototype.executeAction = function(action) root.style.pointerEvents = "none"; setTimeout(function() { root.style.pointerEvents = "auto"; - }, 100); //delay so the mouse up event is not caught by this element + }, 100); // delay so the mouse up event is not caught by this element - //this prevents the default context browser menu to open in case this menu was created when pressing right button - LiteGraph.pointerListenerAdd(root,"up", + // this prevents the default context browser menu to open in case this menu was created when pressing right button + LiteGraph.pointerListenerAdd( + root,"up", function(e) { - //console.log("pointerevents: ContextMenu up root prevent"); + // console.log("pointerevents: ContextMenu up root prevent"); e.preventDefault(); return true; }, - true + true, ); root.addEventListener( "contextmenu", function(e) { if (e.button != 2) { - //right button + // right button return false; } e.preventDefault(); return false; }, - true + true, ); - LiteGraph.pointerListenerAdd(root,"down", + LiteGraph.pointerListenerAdd( + root,"down", function(e) { - //console.log("pointerevents: ContextMenu down"); + // console.log("pointerevents: ContextMenu down"); if (e.button == 2) { that.close(); e.preventDefault(); return true; } }, - true + true, ); function on_mouse_wheel(e) { @@ -13756,7 +13628,7 @@ LGraphNode.prototype.executeAction = function(action) this.root = root; - //title + // title if (options.title) { var element = document.createElement("div"); element.className = "litemenu-title"; @@ -13764,7 +13636,7 @@ LGraphNode.prototype.executeAction = function(action) root.appendChild(element); } - //entries + // entries var num = 0; for (var i=0; i < values.length; i++) { var name = values.constructor == Array ? values[i] : i; @@ -13776,8 +13648,8 @@ LGraphNode.prototype.executeAction = function(action) num++; } - //close on leave? touch enabled devices won't work TODO use a global device detector and condition on that - /*LiteGraph.pointerListenerAdd(root,"leave", function(e) { + // close on leave? touch enabled devices won't work TODO use a global device detector and condition on that + /* LiteGraph.pointerListenerAdd(root,"leave", function(e) { console.log("pointerevents: ContextMenu leave"); if (that.lock) { return; @@ -13789,14 +13661,14 @@ LGraphNode.prototype.executeAction = function(action) //that.close(e); });*/ - LiteGraph.pointerListenerAdd(root,"enter", function(e) { - //console.log("pointerevents: ContextMenu enter"); + LiteGraph.pointerListenerAdd(root,"enter", function(e) { + // console.log("pointerevents: ContextMenu enter"); if (root.closing_timer) { clearTimeout(root.closing_timer); } }); - //insert before checking position + // insert before checking position var root_document = document; if (options.event) { root_document = options.event.target.ownerDocument; @@ -13806,12 +13678,12 @@ LGraphNode.prototype.executeAction = function(action) root_document = document; } - if( root_document.fullscreenElement ) - root_document.fullscreenElement.appendChild(root); - else - root_document.body.appendChild(root); + if( root_document.fullscreenElement ) + root_document.fullscreenElement.appendChild(root); + else + root_document.body.appendChild(root); - //compute best position + // compute best position var left = options.left || 0; var top = options.top || 0; if (options.event) { @@ -13828,8 +13700,8 @@ LGraphNode.prototype.executeAction = function(action) var body_rect = document.body.getBoundingClientRect(); var root_rect = root.getBoundingClientRect(); - if(body_rect.height == 0) - console.error("document.body height is 0. That is dangerous, set html,body { height: 100%; }"); + if(body_rect.height == 0) + console.error("document.body height is 0. That is dangerous, set html,body { height: 100%; }"); if (body_rect.width && left > body_rect.width - root_rect.width - 10) { left = body_rect.width - root_rect.width - 10; @@ -13858,8 +13730,8 @@ LGraphNode.prototype.executeAction = function(action) if (value === null) { element.classList.add("separator"); - //element.innerHTML = "
" - //continue; + // element.innerHTML = "
" + // continue; } else { element.innerHTML = value && value.title ? value.title : name; element.value = value; @@ -13891,7 +13763,7 @@ LGraphNode.prototype.executeAction = function(action) element.addEventListener("click", inner_onclick); } if (!disabled && options.autoopen) { - LiteGraph.pointerListenerAdd(element,"enter",inner_over); + LiteGraph.pointerListenerAdd(element,"enter",inner_over); } function inner_over(e) { @@ -13899,11 +13771,11 @@ LGraphNode.prototype.executeAction = function(action) if (!value || !value.has_submenu) { return; } - //if it is a submenu, autoopen like the item was clicked + // if it is a submenu, autoopen like the item was clicked inner_onclick.call(this, e); } - //menu option clicked + // menu option clicked function inner_onclick(e) { var value = this.value; var close_parent = true; @@ -13912,7 +13784,7 @@ LGraphNode.prototype.executeAction = function(action) that.current_submenu.close(e); } - //global callback + // global callback if (options.callback) { var r = options.callback.call( this, @@ -13920,28 +13792,28 @@ LGraphNode.prototype.executeAction = function(action) options, e, that, - options.node + options.node, ); if (r === true) { close_parent = false; } } - //special cases + // special cases if (value) { if ( value.callback && !options.ignore_item_callbacks && value.disabled !== true ) { - //item callback + // item callback var r = value.callback.call( this, value, options, e, that, - options.extra + options.extra, ); if (r === true) { close_parent = false; @@ -13959,7 +13831,7 @@ LGraphNode.prototype.executeAction = function(action) value.submenu.ignore_item_callbacks, title: value.submenu.title, extra: value.submenu.extra, - autoopen: options.autoopen + autoopen: options.autoopen, }); close_parent = false; } @@ -13996,26 +13868,26 @@ LGraphNode.prototype.executeAction = function(action) if (this.root.closing_timer) { clearTimeout(this.root.closing_timer); } - + // TODO implement : LiteGraph.contextMenuClosed(); :: keep track of opened / closed / current ContextMenu // on key press, allow filtering/selecting the context menu elements }; - //this code is used to trigger events easily (used in the context menu mouseleave + // this code is used to trigger events easily (used in the context menu mouseleave ContextMenu.trigger = function(element, event_name, params, origin) { var evt = document.createEvent("CustomEvent"); - evt.initCustomEvent(event_name, true, true, params); //canBubble, cancelable, detail + evt.initCustomEvent(event_name, true, true, params); // canBubble, cancelable, detail evt.srcElement = origin; if (element.dispatchEvent) { element.dispatchEvent(evt); } else if (element.__events) { element.__events.dispatchEvent(evt); } - //else nothing seems binded here so nothing to do + // else nothing seems binded here so nothing to do return evt; }; - //returns the top most menu + // returns the top most menu ContextMenu.prototype.getTopMenu = function() { if (this.options.parentMenu) { return this.options.parentMenu.getTopMenu(); @@ -14074,7 +13946,7 @@ LGraphNode.prototype.executeAction = function(action) LiteGraph.extendClass = function(target, origin) { for (var i in origin) { - //copy class properties + // copy class properties if (target.hasOwnProperty(i)) { continue; } @@ -14082,218 +13954,207 @@ LGraphNode.prototype.executeAction = function(action) } if (origin.prototype) { - //copy prototype properties + // copy prototype properties for (var i in origin.prototype) { - //only enumerable + // only enumerable if (!origin.prototype.hasOwnProperty(i)) { continue; } if (target.prototype.hasOwnProperty(i)) { - //avoid overwriting existing ones + // avoid overwriting existing ones continue; } - //copy getters + // copy getters if (origin.prototype.__lookupGetter__(i)) { target.prototype.__defineGetter__( i, - origin.prototype.__lookupGetter__(i) + origin.prototype.__lookupGetter__(i), ); } else { target.prototype[i] = origin.prototype[i]; } - //and setters + // and setters if (origin.prototype.__lookupSetter__(i)) { target.prototype.__defineSetter__( i, - origin.prototype.__lookupSetter__(i) + origin.prototype.__lookupSetter__(i), ); } } } }; - //used by some widgets to render a curve editor - function CurveEditor( points ) - { - this.points = points; - this.selected = -1; - this.nearest = -1; - this.size = null; //stores last size used - this.must_update = true; - this.margin = 5; - } + // used by some widgets to render a curve editor + function CurveEditor( points ) { + this.points = points; + this.selected = -1; + this.nearest = -1; + this.size = null; // stores last size used + this.must_update = true; + this.margin = 5; + } - CurveEditor.sampleCurve = function(f,points) - { - if(!points) - return; - for(var i = 0; i < points.length - 1; ++i) - { - var p = points[i]; - var pn = points[i+1]; - if(pn[0] < f) - continue; - var r = (pn[0] - p[0]); - if( Math.abs(r) < 0.00001 ) - return p[1]; - var local_f = (f - p[0]) / r; - return p[1] * (1.0 - local_f) + pn[1] * local_f; - } - return 0; - } + CurveEditor.sampleCurve = function(f,points) { + if(!points) + return; + for(var i = 0; i < points.length - 1; ++i) { + var p = points[i]; + var pn = points[i+1]; + if(pn[0] < f) + continue; + var r = (pn[0] - p[0]); + if( Math.abs(r) < 0.00001 ) + return p[1]; + var local_f = (f - p[0]) / r; + return p[1] * (1.0 - local_f) + pn[1] * local_f; + } + return 0; + } - CurveEditor.prototype.draw = function( ctx, size, graphcanvas, background_color, line_color, inactive ) - { - var points = this.points; - if(!points) - return; - this.size = size; - var w = size[0] - this.margin * 2; - var h = size[1] - this.margin * 2; - - line_color = line_color || "#666"; - - ctx.save(); - ctx.translate(this.margin,this.margin); - - if(background_color) - { - ctx.fillStyle = "#111"; - ctx.fillRect(0,0,w,h); - ctx.fillStyle = "#222"; - ctx.fillRect(w*0.5,0,1,h); - ctx.strokeStyle = "#333"; - ctx.strokeRect(0,0,w,h); - } - ctx.strokeStyle = line_color; - if(inactive) - ctx.globalAlpha = 0.5; - ctx.beginPath(); - for(var i = 0; i < points.length; ++i) - { - var p = points[i]; - ctx.lineTo( p[0] * w, (1.0 - p[1]) * h ); - } - ctx.stroke(); - ctx.globalAlpha = 1; - if(!inactive) - for(var i = 0; i < points.length; ++i) - { - var p = points[i]; - ctx.fillStyle = this.selected == i ? "#FFF" : (this.nearest == i ? "#DDD" : "#AAA"); - ctx.beginPath(); - ctx.arc( p[0] * w, (1.0 - p[1]) * h, 2, 0, Math.PI * 2 ); - ctx.fill(); - } - ctx.restore(); - } + CurveEditor.prototype.draw = function( ctx, size, graphcanvas, background_color, line_color, inactive ) { + var points = this.points; + if(!points) + return; + this.size = size; + var w = size[0] - this.margin * 2; + var h = size[1] - this.margin * 2; - //localpos is mouse in curve editor space - CurveEditor.prototype.onMouseDown = function( localpos, graphcanvas ) - { - var points = this.points; - if(!points) - return; - if( localpos[1] < 0 ) - return; - - //this.captureInput(true); - var w = this.size[0] - this.margin * 2; - var h = this.size[1] - this.margin * 2; - var x = localpos[0] - this.margin; - var y = localpos[1] - this.margin; - var pos = [x,y]; - var max_dist = 30 / graphcanvas.ds.scale; - //search closer one - this.selected = this.getCloserPoint(pos, max_dist); - //create one - if(this.selected == -1) - { - var point = [x / w, 1 - y / h]; - points.push(point); - points.sort(function(a,b){ return a[0] - b[0]; }); - this.selected = points.indexOf(point); - this.must_update = true; - } - if(this.selected != -1) - return true; - } + line_color = line_color || "#666"; - CurveEditor.prototype.onMouseMove = function( localpos, graphcanvas ) - { - var points = this.points; - if(!points) - return; - var s = this.selected; - if(s < 0) - return; - var x = (localpos[0] - this.margin) / (this.size[0] - this.margin * 2 ); - var y = (localpos[1] - this.margin) / (this.size[1] - this.margin * 2 ); - var curvepos = [(localpos[0] - this.margin),(localpos[1] - this.margin)]; - var max_dist = 30 / graphcanvas.ds.scale; - this._nearest = this.getCloserPoint(curvepos, max_dist); - var point = points[s]; - if(point) - { - var is_edge_point = s == 0 || s == points.length - 1; - if( !is_edge_point && (localpos[0] < -10 || localpos[0] > this.size[0] + 10 || localpos[1] < -10 || localpos[1] > this.size[1] + 10) ) - { - points.splice(s,1); - this.selected = -1; - return; - } - if( !is_edge_point ) //not edges - point[0] = clamp(x, 0, 1); - else - point[0] = s == 0 ? 0 : 1; - point[1] = 1.0 - clamp(y, 0, 1); - points.sort(function(a,b){ return a[0] - b[0]; }); - this.selected = points.indexOf(point); - this.must_update = true; - } - } + ctx.save(); + ctx.translate(this.margin,this.margin); - CurveEditor.prototype.onMouseUp = function( localpos, graphcanvas ) - { - this.selected = -1; - return false; - } + if(background_color) { + ctx.fillStyle = "#111"; + ctx.fillRect(0,0,w,h); + ctx.fillStyle = "#222"; + ctx.fillRect(w*0.5,0,1,h); + ctx.strokeStyle = "#333"; + ctx.strokeRect(0,0,w,h); + } + ctx.strokeStyle = line_color; + if(inactive) + ctx.globalAlpha = 0.5; + ctx.beginPath(); + for(var i = 0; i < points.length; ++i) { + var p = points[i]; + ctx.lineTo( p[0] * w, (1.0 - p[1]) * h ); + } + ctx.stroke(); + ctx.globalAlpha = 1; + if(!inactive) + for(var i = 0; i < points.length; ++i) { + var p = points[i]; + ctx.fillStyle = this.selected == i ? "#FFF" : (this.nearest == i ? "#DDD" : "#AAA"); + ctx.beginPath(); + ctx.arc( p[0] * w, (1.0 - p[1]) * h, 2, 0, Math.PI * 2 ); + ctx.fill(); + } + ctx.restore(); + } - CurveEditor.prototype.getCloserPoint = function(pos, max_dist) - { - var points = this.points; - if(!points) - return -1; - max_dist = max_dist || 30; - var w = (this.size[0] - this.margin * 2); - var h = (this.size[1] - this.margin * 2); - var num = points.length; - var p2 = [0,0]; - var min_dist = 1000000; - var closest = -1; - var last_valid = -1; - for(var i = 0; i < num; ++i) - { - var p = points[i]; - p2[0] = p[0] * w; - p2[1] = (1.0 - p[1]) * h; - if(p2[0] < pos[0]) - last_valid = i; - var dist = vec2.distance(pos,p2); - if(dist > min_dist || dist > max_dist) - continue; - closest = i; - min_dist = dist; - } - return closest; - } + // localpos is mouse in curve editor space + CurveEditor.prototype.onMouseDown = function( localpos, graphcanvas ) { + var points = this.points; + if(!points) + return; + if( localpos[1] < 0 ) + return; + + // this.captureInput(true); + var w = this.size[0] - this.margin * 2; + var h = this.size[1] - this.margin * 2; + var x = localpos[0] - this.margin; + var y = localpos[1] - this.margin; + var pos = [x,y]; + var max_dist = 30 / graphcanvas.ds.scale; + // search closer one + this.selected = this.getCloserPoint(pos, max_dist); + // create one + if(this.selected == -1) { + var point = [x / w, 1 - y / h]; + points.push(point); + points.sort(function(a,b) { + return a[0] - b[0]; + }); + this.selected = points.indexOf(point); + this.must_update = true; + } + if(this.selected != -1) + return true; + } - LiteGraph.CurveEditor = CurveEditor; + CurveEditor.prototype.onMouseMove = function( localpos, graphcanvas ) { + var points = this.points; + if(!points) + return; + var s = this.selected; + if(s < 0) + return; + var x = (localpos[0] - this.margin) / (this.size[0] - this.margin * 2 ); + var y = (localpos[1] - this.margin) / (this.size[1] - this.margin * 2 ); + var curvepos = [(localpos[0] - this.margin),(localpos[1] - this.margin)]; + var max_dist = 30 / graphcanvas.ds.scale; + this._nearest = this.getCloserPoint(curvepos, max_dist); + var point = points[s]; + if(point) { + var is_edge_point = s == 0 || s == points.length - 1; + if( !is_edge_point && (localpos[0] < -10 || localpos[0] > this.size[0] + 10 || localpos[1] < -10 || localpos[1] > this.size[1] + 10) ) { + points.splice(s,1); + this.selected = -1; + return; + } + if( !is_edge_point ) // not edges + point[0] = clamp(x, 0, 1); + else + point[0] = s == 0 ? 0 : 1; + point[1] = 1.0 - clamp(y, 0, 1); + points.sort(function(a,b) { + return a[0] - b[0]; + }); + this.selected = points.indexOf(point); + this.must_update = true; + } + } + + CurveEditor.prototype.onMouseUp = function( localpos, graphcanvas ) { + this.selected = -1; + return false; + } + + CurveEditor.prototype.getCloserPoint = function(pos, max_dist) { + var points = this.points; + if(!points) + return -1; + max_dist = max_dist || 30; + var w = (this.size[0] - this.margin * 2); + var h = (this.size[1] - this.margin * 2); + var num = points.length; + var p2 = [0,0]; + var min_dist = 1000000; + var closest = -1; + var last_valid = -1; + for(var i = 0; i < num; ++i) { + var p = points[i]; + p2[0] = p[0] * w; + p2[1] = (1.0 - p[1]) * h; + if(p2[0] < pos[0]) + last_valid = i; + var dist = vec2.distance(pos,p2); + if(dist > min_dist || dist > max_dist) + continue; + closest = i; + min_dist = dist; + } + return closest; + } + + LiteGraph.CurveEditor = CurveEditor; - //used to create nodes from wrapping functions + // used to create nodes from wrapping functions LiteGraph.getParameterNames = function(func) { return (func + "") .replace(/[/][/].*$/gm, "") // strip single-line comments @@ -14306,101 +14167,105 @@ LGraphNode.prototype.executeAction = function(action) .filter(Boolean); // split & filter [""] }; - /* helper for interaction: pointer, touch, mouse Listeners + /* helper for interaction: pointer, touch, mouse Listeners used by LGraphCanvas DragAndScale ContextMenu*/ - LiteGraph.pointerListenerAdd = function(oDOM, sEvIn, fCall, capture=false) { - if (!oDOM || !oDOM.addEventListener || !sEvIn || typeof fCall!=="function"){ - //console.log("cant pointerListenerAdd "+oDOM+", "+sEvent+", "+fCall); - return; // -- break -- - } - - var sMethod = LiteGraph.pointerevents_method; - var sEvent = sEvIn; - - // UNDER CONSTRUCTION - // convert pointerevents to touch event when not available - if (sMethod=="pointer" && !window.PointerEvent){ - console.warn("sMethod=='pointer' && !window.PointerEvent"); - console.log("Converting pointer["+sEvent+"] : down move up cancel enter TO touchstart touchmove touchend, etc .."); - switch(sEvent){ - case "down":{ - sMethod = "touch"; - sEvent = "start"; - break; - } - case "move":{ - sMethod = "touch"; - //sEvent = "move"; - break; - } - case "up":{ - sMethod = "touch"; - sEvent = "end"; - break; - } - case "cancel":{ - sMethod = "touch"; - //sEvent = "cancel"; - break; - } - case "enter":{ - console.log("debug: Should I send a move event?"); // ??? - break; - } - // case "over": case "out": not used at now - default:{ - console.warn("PointerEvent not available in this browser ? The event "+sEvent+" would not be called"); - } - } - } + LiteGraph.pointerListenerAdd = function(oDOM, sEvIn, fCall, capture=false) { + if (!oDOM || !oDOM.addEventListener || !sEvIn || typeof fCall!=="function") { + // console.log("cant pointerListenerAdd "+oDOM+", "+sEvent+", "+fCall); + return; // -- break -- + } + + var sMethod = LiteGraph.pointerevents_method; + var sEvent = sEvIn; + + // UNDER CONSTRUCTION + // convert pointerevents to touch event when not available + if (sMethod=="pointer" && !window.PointerEvent) { + console.warn("sMethod=='pointer' && !window.PointerEvent"); + console.log("Converting pointer["+sEvent+"] : down move up cancel enter TO touchstart touchmove touchend, etc .."); + switch(sEvent) { + case "down":{ + sMethod = "touch"; + sEvent = "start"; + break; + } + case "move":{ + sMethod = "touch"; + // sEvent = "move"; + break; + } + case "up":{ + sMethod = "touch"; + sEvent = "end"; + break; + } + case "cancel":{ + sMethod = "touch"; + // sEvent = "cancel"; + break; + } + case "enter":{ + console.log("debug: Should I send a move event?"); // ??? + break; + } + // case "over": case "out": not used at now + default:{ + console.warn("PointerEvent not available in this browser ? The event "+sEvent+" would not be called"); + } + } + } - switch(sEvent){ - //both pointer and move events - case "down": case "up": case "move": case "over": case "out": case "enter": - { - oDOM.addEventListener(sMethod+sEvent, fCall, capture); - } - // only pointerevents - case "leave": case "cancel": case "gotpointercapture": case "lostpointercapture": - { - if (sMethod!="mouse"){ - return oDOM.addEventListener(sMethod+sEvent, fCall, capture); - } - } - // not "pointer" || "mouse" - default: - return oDOM.addEventListener(sEvent, fCall, capture); - } - } - LiteGraph.pointerListenerRemove = function(oDOM, sEvent, fCall, capture=false) { - if (!oDOM || !oDOM.removeEventListener || !sEvent || typeof fCall!=="function"){ - //console.log("cant pointerListenerRemove "+oDOM+", "+sEvent+", "+fCall); - return; // -- break -- - } - switch(sEvent){ - //both pointer and move events - case "down": case "up": case "move": case "over": case "out": case "enter": - { - if (LiteGraph.pointerevents_method=="pointer" || LiteGraph.pointerevents_method=="mouse"){ - oDOM.removeEventListener(LiteGraph.pointerevents_method+sEvent, fCall, capture); - } - } - // only pointerevents - case "leave": case "cancel": case "gotpointercapture": case "lostpointercapture": - { - if (LiteGraph.pointerevents_method=="pointer"){ - return oDOM.removeEventListener(LiteGraph.pointerevents_method+sEvent, fCall, capture); - } - } - // not "pointer" || "mouse" - default: - return oDOM.removeEventListener(sEvent, fCall, capture); - } - } + switch(sEvent) { + // both pointer and move events + case "down": case "up": case "move": case "over": case "out": case "enter": + { + oDOM.addEventListener(sMethod+sEvent, fCall, capture); + } + break; + // only pointerevents + case "leave": case "cancel": case "gotpointercapture": case "lostpointercapture": + { + if (sMethod!="mouse") { + return oDOM.addEventListener(sMethod+sEvent, fCall, capture); + } + } + break; + // not "pointer" || "mouse" + default: + return oDOM.addEventListener(sEvent, fCall, capture); + } + } + LiteGraph.pointerListenerRemove = function(oDOM, sEvent, fCall, capture=false) { + if (!oDOM || !oDOM.removeEventListener || !sEvent || typeof fCall!=="function") { + // console.log("cant pointerListenerRemove "+oDOM+", "+sEvent+", "+fCall); + return; // -- break -- + } + switch(sEvent) { + // both pointer and move events + case "down": case "up": case "move": case "over": case "out": case "enter": + { + if (LiteGraph.pointerevents_method=="pointer" || LiteGraph.pointerevents_method=="mouse") { + oDOM.removeEventListener(LiteGraph.pointerevents_method+sEvent, fCall, capture); + } + } + break; + // only pointerevents + case "leave": case "cancel": case "gotpointercapture": case "lostpointercapture": + { + if (LiteGraph.pointerevents_method=="pointer") { + return oDOM.removeEventListener(LiteGraph.pointerevents_method+sEvent, fCall, capture); + } + } + break; + // not "pointer" || "mouse" + default: + return oDOM.removeEventListener(sEvent, fCall, capture); + } + } function clamp(v, a, b) { return a > v ? a : b < v ? b : v; - }; + } global.clamp = clamp; if (typeof window != "undefined" && !window["requestAnimationFrame"]) { diff --git a/build/litegraph.core.min.js b/build/litegraph.core.min.js deleted file mode 100644 index 2c3db303f..000000000 --- a/build/litegraph.core.min.js +++ /dev/null @@ -1,342 +0,0 @@ -(function(X){function w(a){h.debug&&console.log("Graph created");this.list_of_graphcanvas=null;this.clear();a&&this.configure(a)}function Y(a,b,c,d,e,f){this.id=a;this.type=b;this.origin_id=c;this.origin_slot=d;this.target_id=e;this.target_slot=f;this._data=null;this._pos=new Float32Array(2)}function v(a){this._ctor(a)}function P(a){this._ctor(a)}function L(a,b){this.offset=new Float32Array([0,0]);this.scale=1;this.max_scale=10;this.min_scale=.1;this.onredraw=null;this.enabled=!0;this.last_mouse= -[0,0];this.element=null;this.visible_area=new Float32Array(4);a&&(this.element=a,b||this.bindEvents(a))}function q(a,b,c){this.options=c=c||{};this.background_image=q.DEFAULT_BACKGROUND_IMAGE;a&&a.constructor===String&&(a=document.querySelector(a));this.ds=new L;this.zoom_modify_alpha=!0;this.title_text_font=""+h.NODE_TEXT_SIZE+"px Arial";this.inner_text_font="normal "+h.NODE_SUBTEXT_SIZE+"px Arial";this.node_title_color=h.NODE_TITLE_COLOR;this.default_link_color=h.LINK_COLOR;this.default_connection_color= -{input_off:"#778",input_on:"#7F7",output_off:"#778",output_on:"#7F7"};this.default_connection_color_byType={};this.default_connection_color_byTypeOff={};this.highquality_render=!0;this.use_gradients=!1;this.editor_alpha=1;this.pause_rendering=!1;this.clear_background=!0;this.clear_background_color="#222";this.read_only=!1;this.render_only_selected=!0;this.live_mode=!1;this.allow_interaction=this.allow_dragnodes=this.allow_dragcanvas=this.show_info=!0;this.multi_select=!1;this.allow_reconnect_links= -this.allow_searchbox=!0;this.drag_mode=this.align_to_grid=!1;this.filter=this.dragging_rectangle=null;this.set_canvas_dirty_on_mouse_event=!0;this.always_render_background=!1;this.render_canvas_border=this.render_shadows=!0;this.render_connections_shadows=!1;this.render_connections_border=!0;this.render_connection_arrows=this.render_curved_connections=!1;this.render_collapsed_slots=!0;this.render_execution_order=!1;this.render_link_tooltip=this.render_title_colored=!0;this.links_render_mode=h.SPLINE_LINK; -this.mouse=[0,0];this.canvas_mouse=this.graph_mouse=[0,0];this.onAfterChange=this.onBeforeChange=this.onConnectingChange=this.onSelectionChange=this.onNodeMoved=this.onDrawLinkTooltip=this.onDrawOverlay=this.onDrawForeground=this.onDrawBackground=this.onMouse=this.onSearchBoxSelection=this.onSearchBox=null;this.connections_width=3;this.round_radius=8;this.over_link_center=this.node_widget=this.current_node=null;this.last_mouse_position=[0,0];this.visible_area=this.ds.visible_area;this.visible_links= -[];this.viewport=c.viewport||null;b&&b.attachCanvas(this);this.setCanvas(a,c.skip_events);this.clear();c.skip_render||this.startRendering();this.autoresize=c.autoresize}function ba(a,b){return Math.sqrt((b[0]-a[0])*(b[0]-a[0])+(b[1]-a[1])*(b[1]-a[1]))}function K(a,b,c,d,e,f){return ca&&db?!0:!1}function Z(a,b){var c=a[0]+a[2],d=a[1]+a[3],e=b[1]+b[3];return a[0]>b[0]+b[2]||a[1]>e||cg.width-k.width-10&&(e=g.width-k.width-10),g.height&&a>g.height-k.height-10&&(a=g.height-k.height-10));f.style.left=e+"px";f.style.top=a+"px";b.scale&&(f.style.transform="scale("+b.scale+")")}function T(a){this.points=a;this.nearest=this.selected=-1;this.size=null;this.must_update=!0;this.margin=5}function ca(a,b,c){return b>a?b:c(a^16*Math.random()>>a/4).toString(16))},isValidConnection:function(a,b){if(""==a||"*"===a)a=0;if(""==b||"*"===b)b=0;if(!a||!b||a==b||a==h.EVENT&& -b==h.ACTION)return!0;a=String(a);b=String(b);a=a.toLowerCase();b=b.toLowerCase();if(-1==a.indexOf(",")&&-1==b.indexOf(","))return a==b;a=a.split(",");b=b.split(",");for(var c=0;cg&&(g=l.size[n]);k+=l.size[b==h.VERTICAL_LAYOUT?0:1]+a+h.NODE_TITLE_HEIGHT}c+=g+a}this.setDirtyCanvas(!0,!0)};w.prototype.getTime=function(){return this.globaltime};w.prototype.getFixedTime=function(){return this.fixedtime};w.prototype.getElapsedTime=function(){return this.elapsed_time};w.prototype.sendEventToAllNodes= -function(a,b,c){c=c||h.ALWAYS;var d=this._nodes_in_order?this._nodes_in_order:this._nodes;if(d)for(var e=0,f=d.length;e=h.MAX_NUMBER_OF_NODES)throw"LiteGraph: max number of nodes in a graph reached";if(h.use_uuids){if(null==a.id||-1==a.id)a.id=h.uuidv4()}else null==a.id||-1== -a.id?a.id=++this.last_node_id:this.last_node_idb.length||(this._pos[0]=b[0],this._pos[1]=b[1])},get:function(){return this._pos},enumerable:!0});this.id=h.use_uuids?h.uuidv4():-1;this.type=null;this.inputs=[];this.outputs=[];this.connections=[];this.properties={};this.properties_info=[];this.flags={}};v.prototype.configure=function(a){this.graph&& -this.graph._version++;for(var b in a)if("properties"==b)for(var c in a.properties){if(this.properties[c]=a.properties[c],this.onPropertyChanged)this.onPropertyChanged(c,a.properties[c])}else null!=a[b]&&("object"==typeof a[b]?this[b]&&this[b].configure?this[b].configure(a[b]):this[b]=h.cloneObject(a[b],this[b]):this[b]=a[b]);a.title||(this.title=this.constructor.title);if(this.inputs)for(c=0;c=this.outputs.length)){var c=this.outputs[a];if(c&&(c._data=b,this.outputs[a].links))for(c=0;c=this.outputs.length)){var c=this.outputs[a];if(c&&(c.type=b,this.outputs[a].links))for(c=0;c=this.inputs.length||null==this.inputs[a].link)){a=this.graph.links[this.inputs[a].link];if(!a)return null;if(!b)return a.data;b=this.graph.getNodeById(a.origin_id);if(!b)return a.data;if(b.updateOutputData)b.updateOutputData(a.origin_slot);else if(b.onExecute)b.onExecute();return a.data}};v.prototype.getInputDataType=function(a){if(!this.inputs||a>=this.inputs.length||null==this.inputs[a].link)return null;a=this.graph.links[this.inputs[a].link];if(!a)return null; -var b=this.graph.getNodeById(a.origin_id);return b?(a=b.outputs[a.origin_slot])?a.type:null:a.type};v.prototype.getInputDataByName=function(a,b){a=this.findInputSlot(a);return-1==a?null:this.getInputData(a,b)};v.prototype.isInputConnected=function(a){return this.inputs?a=this.inputs.length)return null;a=this.inputs[a];return a&&null!==a.link?(a=this.graph.links[a.link])?this.graph.getNodeById(a.origin_id):null:null};v.prototype.getInputOrProperty=function(a){if(!this.inputs||!this.inputs.length)return this.properties?this.properties[a]:null;for(var b=0,c=this.inputs.length;b=this.outputs.length?null:this.outputs[a]._data};v.prototype.getOutputInfo=function(a){return this.outputs?a=this.outputs.length)return null;a=this.outputs[a];if(!a.links||0==a.links.length)return null;for(var b=[],c=0;ca&&this.pos[1]-e-cb)return!0;return!1};v.prototype.getSlotInPosition=function(a,b){var c=new Float32Array(2);if(this.inputs)for(var d=0,e=this.inputs.length;d=this.outputs.length)return h.debug&&console.log("Connect: Error, slot number not found"),null;b&&b.constructor===Number&&(b=this.graph.getNodeById(b)); -if(!b)throw"target node is null";if(b==this)return null;if(c.constructor===String){if(c=b.findInputSlot(c),-1==c)return h.debug&&console.log("Connect: Error, no slot of name "+c),null}else if(c===h.EVENT)if(h.do_add_triggers_slots)b.changeMode(h.ON_TRIGGER),c=b.findInputSlot("onTrigger");else return null;else if(!b.inputs||c>=b.inputs.length)return h.debug&&console.log("Connect: Error, slot number not found"),null;var d=b.inputs[c],e=this.outputs[a];if(!this.outputs[a])return null;b.onBeforeConnectInput&& -(c=b.onBeforeConnectInput(c));if(!1===c||null===c||!h.isValidConnection(e.type,d.type))return this.setDirtyCanvas(!1,!0),null;if(b.onConnectInput&&!1===b.onConnectInput(c,e.type,e,this,a)||this.onConnectOutput&&!1===this.onConnectOutput(a,d.type,d,b,c))return null;b.inputs[c]&&null!=b.inputs[c].link&&(this.graph.beforeChange(),b.disconnectInput(c,{doProcessChange:!1}));if(null!==e.links&&e.links.length)switch(e.type){case h.EVENT:h.allow_multi_output_for_events||(this.graph.beforeChange(),this.disconnectOutput(a, -!1,{doProcessChange:!1}))}var f=h.use_uuids?h.uuidv4():++this.graph.last_link_id;f=new Y(f,d.type||e.type,this.id,a,b.id,c);this.graph.links[f.id]=f;null==e.links&&(e.links=[]);e.links.push(f.id);b.inputs[c].link=f.id;this.graph&&this.graph._version++;if(this.onConnectionsChange)this.onConnectionsChange(h.OUTPUT,a,!0,f,e);if(b.onConnectionsChange)b.onConnectionsChange(h.INPUT,c,!0,f,d);this.graph&&this.graph.onNodeConnectionChange&&(this.graph.onNodeConnectionChange(h.INPUT,b,c,this,a),this.graph.onNodeConnectionChange(h.OUTPUT, -this,a,b,c));this.setDirtyCanvas(!1,!0);this.graph.afterChange();this.graph.connectionChange(this,f);return f};v.prototype.disconnectOutput=function(a,b){if(a.constructor===String){if(a=this.findOutputSlot(a),-1==a)return h.debug&&console.log("Connect: Error, no slot of name "+a),!1}else if(!this.outputs||a>=this.outputs.length)return h.debug&&console.log("Connect: Error, slot number not found"),!1;var c=this.outputs[a];if(!c||!c.links||0==c.links.length)return!1;if(b){b.constructor===Number&&(b= -this.graph.getNodeById(b));if(!b)throw"Target Node not found";for(var d=0,e=c.links.length;d=this.inputs.length)return h.debug&&console.log("Connect: Error, slot number not found"),!1;var b=this.inputs[a];if(!b)return!1;var c=this.inputs[a].link;if(null!=c){this.inputs[a].link=null;var d=this.graph.links[c];if(d){var e=this.graph.getNodeById(d.origin_id);if(!e)return!1;var f=e.outputs[d.origin_slot];if(!f||!f.links||0==f.links.length)return!1;for(var g=0,k=f.links.length;gb&&this.inputs[b].pos)return c[0]=this.pos[0]+this.inputs[b].pos[0],c[1]=this.pos[1]+ -this.inputs[b].pos[1],c;if(!a&&d>b&&this.outputs[b].pos)return c[0]=this.pos[0]+this.outputs[b].pos[0],c[1]=this.pos[1]+this.outputs[b].pos[1],c;if(this.horizontal)return c[0]=this.pos[0]+this.size[0]/d*(b+.5),c[1]=a?this.pos[1]-h.NODE_TITLE_HEIGHT:this.pos[1]+this.size[1],c;c[0]=a?this.pos[0]+e:this.pos[0]+this.size[0]+1-e;c[1]=this.pos[1]+(b+.7)*h.NODE_SLOT_HEIGHT+(this.constructor.slot_start_y||0);return c};v.prototype.alignToGrid=function(){this.pos[0]=h.CANVAS_GRID_SIZE*Math.round(this.pos[0]/ -h.CANVAS_GRID_SIZE);this.pos[1]=h.CANVAS_GRID_SIZE*Math.round(this.pos[1]/h.CANVAS_GRID_SIZE)};v.prototype.trace=function(a){this.console||(this.console=[]);this.console.push(a);this.console.length>v.MAX_CONSOLE&&this.console.shift();if(this.graph.onNodeTrace)this.graph.onNodeTrace(this,a)};v.prototype.setDirtyCanvas=function(a,b){this.graph&&this.graph.sendActionToCanvas("setDirty",[a,b])};v.prototype.loadImage=function(a){var b=new Image;b.src=h.node_images_path+a;b.ready=!1;var c=this;b.onload= -function(){this.ready=!0;c.setDirtyCanvas(!0)};return b};v.prototype.captureInput=function(a){if(this.graph&&this.graph.list_of_graphcanvas)for(var b=this.graph.list_of_graphcanvas,c=0;cb.length||(this._pos[0]=b[0],this._pos[1]=b[1])},get:function(){return this._pos},enumerable:!0});Object.defineProperty(this,"size",{set:function(b){!b||2>b.length||(this._size[0]=Math.max(140,b[0]),this._size[1]=Math.max(80,b[1]))},get:function(){return this._size},enumerable:!0})};P.prototype.configure=function(a){this.title=a.title;this._bounding.set(a.bounding);this.color=a.color;this.font_size=a.font_size};P.prototype.serialize=function(){var a= -this._bounding;return{title:this.title,bounding:[Math.round(a[0]),Math.round(a[1]),Math.round(a[2]),Math.round(a[3])],color:this.color,font_size:this.font_size}};P.prototype.move=function(a,b,c){this._pos[0]+=a;this._pos[1]+=b;if(!c)for(c=0;c=this.viewport[0]&&d=this.viewport[1]&&cthis.max_scale&&(a=this.max_scale);if(a!=this.scale&&this.element){var c=this.element.getBoundingClientRect();if(c&&(b= -b||[.5*c.width,.5*c.height],c=this.convertCanvasToOffset(b),this.scale=a,.01>Math.abs(this.scale-1)&&(this.scale=1),a=this.convertCanvasToOffset(b),a=[a[0]-c[0],a[1]-c[1]],this.offset[0]+=a[0],this.offset[1]+=a[1],this.onredraw))this.onredraw(this)}};L.prototype.changeDeltaScale=function(a,b){this.changeScale(this.scale*a,b)};L.prototype.reset=function(){this.scale=1;this.offset[0]=0;this.offset[1]=0};X.LGraphCanvas=h.LGraphCanvas=q;q.DEFAULT_BACKGROUND_IMAGE="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAIAAAD/gAIDAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAQBJREFUeNrs1rEKwjAUhlETUkj3vP9rdmr1Ysammk2w5wdxuLgcMHyptfawuZX4pJSWZTnfnu/lnIe/jNNxHHGNn//HNbbv+4dr6V+11uF527arU7+u63qfa/bnmh8sWLBgwYJlqRf8MEptXPBXJXa37BSl3ixYsGDBMliwFLyCV/DeLIMFCxYsWLBMwSt4Be/NggXLYMGCBUvBK3iNruC9WbBgwYJlsGApeAWv4L1ZBgsWLFiwYJmCV/AK3psFC5bBggULloJX8BpdwXuzYMGCBctgwVLwCl7Be7MMFixYsGDBsu8FH1FaSmExVfAxBa/gvVmwYMGCZbBg/W4vAQYA5tRF9QYlv/QAAAAASUVORK5CYII="; -q.link_type_colors={"-1":h.EVENT_LINK_COLOR,number:"#AAA",node:"#DCA"};q.gradients={};q.prototype.clear=function(){this.fps=this.render_time=this.last_draw_time=this.frame=0;this.dragging_rectangle=null;this.selected_nodes={};this.selected_group=null;this.visible_nodes=[];this.connecting_node=this.node_capturing_input=this.node_over=this.node_dragged=null;this.highlighted_links={};this.dragging_canvas=!1;this.dirty_bgcanvas=this.dirty_canvas=!0;this.node_widget=this.node_in_panel=this.dirty_area= -null;this.last_mouse=[0,0];this.last_mouseclick=0;this.pointer_is_double=this.pointer_is_down=!1;this.visible_area.set([0,0,0,0]);if(this.onClear)this.onClear()};q.prototype.setGraph=function(a,b){this.graph!=a&&(b||this.clear(),!a&&this.graph?this.graph.detachCanvas(this):(a.attachCanvas(this),this._graph_stack&&(this._graph_stack=null),this.setDirty(!0,!0)))};q.prototype.getTopGraph=function(){return this._graph_stack.length?this._graph_stack[0]:this.graph};q.prototype.openSubgraph=function(a){if(!a)throw"graph cannot be null"; -if(this.graph==a)throw"graph cannot be the same";this.clear();this.graph&&(this._graph_stack||(this._graph_stack=[]),this._graph_stack.push(this.graph));a.attachCanvas(this);this.checkPanels();this.setDirty(!0,!0)};q.prototype.closeSubgraph=function(){if(this._graph_stack&&0!=this._graph_stack.length){var a=this.graph._subgraph_node,b=this._graph_stack.pop();this.selected_nodes={};this.highlighted_links={};b.attachCanvas(this);this.setDirty(!0,!0);a&&(this.centerOnNode(a),this.selectNodes([a]));this.ds.offset= -[0,0];this.ds.scale=1}};q.prototype.getCurrentGraph=function(){return this.graph};q.prototype.setCanvas=function(a,b){if(a&&a.constructor===String&&(a=document.getElementById(a),!a))throw"Error creating LiteGraph canvas: Canvas not found";if(a!==this.canvas&&(!a&&this.canvas&&(b||this.unbindEvents()),this.canvas=a,this.ds.element=a)){a.className+=" lgraphcanvas";a.data=this;a.tabindex="1";this.bgcanvas=null;this.bgcanvas||(this.bgcanvas=document.createElement("canvas"),this.bgcanvas.width=this.canvas.width, -this.bgcanvas.height=this.canvas.height);if(null==a.getContext){if("canvas"!=a.localName)throw"Element supplied for LGraphCanvas must be a element, you passed a "+a.localName;throw"This browser doesn't support Canvas";}null==(this.ctx=a.getContext("2d"))&&(a.webgl_enabled||console.warn("This canvas seems to be WebGL, enabling WebGL renderer"),this.enableWebGL());b||this.bindEvents()}};q.prototype._doNothing=function(a){a.preventDefault();return!1};q.prototype._doReturnTrue=function(a){a.preventDefault(); -return!0};q.prototype.bindEvents=function(){if(this._events_binded)console.warn("LGraphCanvas: events already binded");else{var a=this.canvas,b=this.getCanvasWindow().document;this._mousedown_callback=this.processMouseDown.bind(this);this._mousewheel_callback=this.processMouseWheel.bind(this);this._mousemove_callback=this.processMouseMove.bind(this);this._mouseup_callback=this.processMouseUp.bind(this);h.pointerListenerAdd(a,"down",this._mousedown_callback,!0);a.addEventListener("mousewheel",this._mousewheel_callback, -!1);h.pointerListenerAdd(a,"up",this._mouseup_callback,!0);h.pointerListenerAdd(a,"move",this._mousemove_callback);a.addEventListener("contextmenu",this._doNothing);a.addEventListener("DOMMouseScroll",this._mousewheel_callback,!1);this._key_callback=this.processKey.bind(this);a.setAttribute("tabindex",1);a.addEventListener("keydown",this._key_callback,!0);b.addEventListener("keyup",this._key_callback,!0);this._ondrop_callback=this.processDrop.bind(this);a.addEventListener("dragover",this._doNothing, -!1);a.addEventListener("dragend",this._doNothing,!1);a.addEventListener("drop",this._ondrop_callback,!1);a.addEventListener("dragenter",this._doReturnTrue,!1);this._events_binded=!0}};q.prototype.unbindEvents=function(){if(this._events_binded){var a=this.getCanvasWindow().document;h.pointerListenerRemove(this.canvas,"move",this._mousedown_callback);h.pointerListenerRemove(this.canvas,"up",this._mousedown_callback);h.pointerListenerRemove(this.canvas,"down",this._mousedown_callback);this.canvas.removeEventListener("mousewheel", -this._mousewheel_callback);this.canvas.removeEventListener("DOMMouseScroll",this._mousewheel_callback);this.canvas.removeEventListener("keydown",this._key_callback);a.removeEventListener("keyup",this._key_callback);this.canvas.removeEventListener("contextmenu",this._doNothing);this.canvas.removeEventListener("drop",this._ondrop_callback);this.canvas.removeEventListener("dragenter",this._doReturnTrue);this._ondrop_callback=this._key_callback=this._mousewheel_callback=this._mousedown_callback=null; -this._events_binded=!1}else console.warn("LGraphCanvas: no events binded")};q.getFileExtension=function(a){var b=a.indexOf("?");-1!=b&&(a=a.substr(0,b));b=a.lastIndexOf(".");return-1==b?"":a.substr(b+1).toLowerCase()};q.prototype.enableWebGL=function(){if("undefined"===typeof GL)throw"litegl.js must be included to use a WebGL canvas";if("undefined"===typeof enableWebGLCanvas)throw"webglCanvas.js must be included to use this feature";this.gl=this.ctx=enableWebGLCanvas(this.canvas);this.ctx.webgl=!0; -this.bgcanvas=this.canvas;this.bgctx=this.gl;this.canvas.webgl_enabled=!0};q.prototype.setDirty=function(a,b){a&&(this.dirty_canvas=!0);b&&(this.dirty_bgcanvas=!0)};q.prototype.getCanvasWindow=function(){if(!this.canvas)return window;var a=this.canvas.ownerDocument;return a.defaultView||a.parentWindow};q.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))};q.prototype.stopRendering=function(){this.is_rendering=!1};q.prototype.blockClick=function(){this.block_click=!0;this.last_mouseclick=0};q.prototype.processMouseDown=function(a){this.set_canvas_dirty_on_mouse_event&&(this.dirty_canvas=!0);if(this.graph){this.adjustMouseEvent(a);var b=this.getCanvasWindow();q.active_canvas=this;var c=this,d=a.clientX,e=a.clientY;this.ds.viewport=this.viewport;d=!this.viewport||this.viewport&&d>=this.viewport[0]&&d=this.viewport[1]&&ee-this.last_mouseclick&&g;this.mouse[0]=a.clientX;this.mouse[1]=a.clientY; -this.graph_mouse[0]=a.canvasX;this.graph_mouse[1]=a.canvasY;this.last_click_position=[this.mouse[0],this.mouse[1]];this.pointer_is_double=this.pointer_is_down&&g?!0:!1;this.pointer_is_down=!0;this.canvas.focus();h.closeAllContextMenus(b);if(!this.onMouse||1!=this.onMouse(a)){if(1!=a.which||this.pointer_is_double)if(2==a.which)if(h.middle_click_slot_add_default_node){if(f&&this.allow_interaction&&!d&&!this.read_only&&!this.connecting_node&&!f.flags.collapsed&&!this.live_mode){e=d=g=!1;if(f.outputs)for(n= -0,k=f.outputs.length;nf.size[0]-h.NODE_TITLE_HEIGHT&&0>k[1]&&(c=this,setTimeout(function(){c.openSubgraph(f.subgraph)},10)),this.live_mode&&(n=g=!0));n?f.is_selected||this.processNodeSelected(f,a):(this.allow_dragnodes&&(this.graph.beforeChange(),this.node_dragged=f),this.processNodeSelected(f,a));this.dirty_canvas=!0}}else if(!d){if(!this.read_only)for(n=0;nk[0]+4||a.canvasY -k[1]+4)){this.showLinkMenu(g,a);this.over_link_center=null;break}this.selected_group=this.graph.getGroupOnPos(a.canvasX,a.canvasY);this.selected_group_resizing=!1;this.selected_group&&!this.read_only&&(a.ctrlKey&&(this.dragging_rectangle=null),10>ba([a.canvasX,a.canvasY],[this.selected_group.pos[0]+this.selected_group.size[0],this.selected_group.pos[1]+this.selected_group.size[1]])*this.ds.scale?this.selected_group_resizing=!0:this.selected_group.recomputeInsideNodes());e&&!this.read_only&&this.allow_searchbox&& -(this.showSearchBox(a),a.preventDefault(),a.stopPropagation());g=!0}!d&&g&&this.allow_dragcanvas&&(this.dragging_canvas=!0)}this.last_mouse[0]=a.clientX;this.last_mouse[1]=a.clientY;this.last_mouseclick=h.getTime();this.last_mouse_dragging=!0;this.graph.change();(!b.document.activeElement||"input"!=b.document.activeElement.nodeName.toLowerCase()&&"textarea"!=b.document.activeElement.nodeName.toLowerCase())&&a.preventDefault();a.stopPropagation();if(this.onMouseDown)this.onMouseDown(a);return!1}}}}; -q.prototype.processMouseMove=function(a){this.autoresize&&this.resize();this.set_canvas_dirty_on_mouse_event&&(this.dirty_canvas=!0);if(this.graph){q.active_canvas=this;this.adjustMouseEvent(a);var b=[a.clientX,a.clientY];this.mouse[0]=b[0];this.mouse[1]=b[1];var c=[b[0]-this.last_mouse[0],b[1]-this.last_mouse[1]];this.last_mouse=b;this.graph_mouse[0]=a.canvasX;this.graph_mouse[1]=a.canvasY;if(this.block_click)return a.preventDefault(),!1;a.dragging=this.last_mouse_dragging;this.node_widget&&(this.processNodeWidgets(this.node_widget[0], -this.graph_mouse,a,this.node_widget[1]),this.dirty_canvas=!0);var d=this.graph.getNodeOnPos(a.canvasX,a.canvasY,this.visible_nodes);if(this.dragging_rectangle)this.dragging_rectangle[2]=a.canvasX-this.dragging_rectangle[0],this.dragging_rectangle[3]=a.canvasY-this.dragging_rectangle[1],this.dirty_canvas=!0;else if(this.selected_group&&!this.read_only)this.selected_group_resizing?this.selected_group.size=[a.canvasX-this.selected_group.pos[0],a.canvasY-this.selected_group.pos[1]]:(this.selected_group.move(c[0]/ -this.ds.scale,c[1]/this.ds.scale,a.ctrlKey),this.selected_group._nodes.length&&(this.dirty_canvas=!0)),this.dirty_bgcanvas=!0;else if(this.dragging_canvas)this.ds.offset[0]+=c[0]/this.ds.scale,this.ds.offset[1]+=c[1]/this.ds.scale,this.dirty_bgcanvas=this.dirty_canvas=!0;else if((this.allow_interaction||d&&d.flags.allow_interaction)&&!this.read_only){this.connecting_node&&(this.dirty_canvas=!0);b=0;for(var e=this.graph._nodes.length;bg[0]+4||a.canvasYg[1]+4)){e=f;break}e!=this.over_link_center&&(this.over_link_center=e,this.dirty_canvas=!0);this.canvas&&(this.canvas.style.cursor="")}if(this.node_capturing_input&& -this.node_capturing_input!=d&&this.node_capturing_input.onMouseMove)this.node_capturing_input.onMouseMove(a,[a.canvasX-this.node_capturing_input.pos[0],a.canvasY-this.node_capturing_input.pos[1]],this);if(this.node_dragged&&!this.live_mode){for(b in this.selected_nodes)d=this.selected_nodes[b],d.pos[0]+=c[0]/this.ds.scale,d.pos[1]+=c[1]/this.ds.scale,d.is_selected||this.processNodeSelected(d,a);this.dirty_bgcanvas=this.dirty_canvas=!0}this.resizing_node&&!this.live_mode&&(c=[a.canvasX-this.resizing_node.pos[0], -a.canvasY-this.resizing_node.pos[1]],b=this.resizing_node.computeSize(),c[0]=Math.max(b[0],c[0]),c[1]=Math.max(b[1],c[1]),this.resizing_node.setSize(c),this.canvas.style.cursor="se-resize",this.dirty_bgcanvas=this.dirty_canvas=!0)}a.preventDefault();return!1}};q.prototype.processMouseUp=function(a){var b=void 0===a.isPrimary||a.isPrimary;if(!b)return!1;this.set_canvas_dirty_on_mouse_event&&(this.dirty_canvas=!0);if(this.graph){var c=this.getCanvasWindow().document;q.active_canvas=this;this.options.skip_events|| -(h.pointerListenerRemove(c,"move",this._mousemove_callback,!0),h.pointerListenerAdd(this.canvas,"move",this._mousemove_callback,!0),h.pointerListenerRemove(c,"up",this._mouseup_callback,!0));this.adjustMouseEvent(a);c=h.getTime();a.click_time=c-this.last_mouseclick;this.last_mouse_dragging=!1;this.last_click_position=null;this.block_click&&(this.block_click=!1);if(1==a.which){this.node_widget&&this.processNodeWidgets(this.node_widget[0],this.graph_mouse,a);this.node_widget=null;this.selected_group&& -(this.selected_group.move(this.selected_group.pos[0]-Math.round(this.selected_group.pos[0]),this.selected_group.pos[1]-Math.round(this.selected_group.pos[1]),a.ctrlKey),this.selected_group.pos[0]=Math.round(this.selected_group.pos[0]),this.selected_group.pos[1]=Math.round(this.selected_group.pos[1]),this.selected_group._nodes.length&&(this.dirty_canvas=!0),this.selected_group=null);this.selected_group_resizing=!1;var d=this.graph.getNodeOnPos(a.canvasX,a.canvasY,this.visible_nodes);if(this.dragging_rectangle){if(this.graph){c= -this.graph._nodes;var e=new Float32Array(4),f=Math.abs(this.dragging_rectangle[2]),g=Math.abs(this.dragging_rectangle[3]),k=0>this.dragging_rectangle[3]?this.dragging_rectangle[1]-g:this.dragging_rectangle[1];this.dragging_rectangle[0]=0>this.dragging_rectangle[2]?this.dragging_rectangle[0]-f:this.dragging_rectangle[0];this.dragging_rectangle[1]=k;this.dragging_rectangle[2]=f;this.dragging_rectangle[3]=g;if(!d||10a.click_time&&K(a.canvasX,a.canvasY,d.pos[0],d.pos[1]-h.NODE_TITLE_HEIGHT,h.NODE_TITLE_HEIGHT,h.NODE_TITLE_HEIGHT)&&d.collapse();this.dirty_bgcanvas=this.dirty_canvas=!0;this.node_dragged.pos[0]=Math.round(this.node_dragged.pos[0]);this.node_dragged.pos[1]=Math.round(this.node_dragged.pos[1]);(this.graph.config.align_to_grid||this.align_to_grid)&&this.node_dragged.alignToGrid();if(this.onNodeMoved)this.onNodeMoved(this.node_dragged);this.graph.afterChange(this.node_dragged); -this.node_dragged=null}else{d=this.graph.getNodeOnPos(a.canvasX,a.canvasY,this.visible_nodes);!d&&300>a.click_time&&this.deselectAllNodes();this.dirty_canvas=!0;this.dragging_canvas=!1;if(this.node_over&&this.node_over.onMouseUp)this.node_over.onMouseUp(a,[a.canvasX-this.node_over.pos[0],a.canvasY-this.node_over.pos[1]],this);if(this.node_capturing_input&&this.node_capturing_input.onMouseUp)this.node_capturing_input.onMouseUp(a,[a.canvasX-this.node_capturing_input.pos[0],a.canvasY-this.node_capturing_input.pos[1]])}}else 2== -a.which?(this.dirty_canvas=!0,this.dragging_canvas=!1):3==a.which&&(this.dirty_canvas=!0,this.dragging_canvas=!1);b&&(this.pointer_is_double=this.pointer_is_down=!1);this.graph.change();a.stopPropagation();a.preventDefault();return!1}};q.prototype.processMouseWheel=function(a){if(this.graph&&this.allow_dragcanvas){var b=null!=a.wheelDeltaY?a.wheelDeltaY:-60*a.detail;this.adjustMouseEvent(a);var c=a.clientX,d=a.clientY;if(!this.viewport||this.viewport&&c>=this.viewport[0]&&c=this.viewport[1]&&db&&(c*=1/1.1),this.ds.changeScale(c,[a.clientX,a.clientY]),this.graph.change(),a.preventDefault(),!1}};q.prototype.isOverNodeBox=function(a,b,c){var d=h.NODE_TITLE_HEIGHT;return K(b,c,a.pos[0]+2,a.pos[1]+2-d,d-4,d-4)?!0:!1};q.prototype.isOverNodeInput=function(a,b,c,d){if(a.inputs)for(var e=0,f=a.inputs.length;eb.nodes[d].pos[0]&&(c[0]=b.nodes[d].pos[0]),c[1]>b.nodes[d].pos[1]&&(c[1]=b.nodes[d].pos[1])):c=[b.nodes[d].pos[0],b.nodes[d].pos[1]];var e=[];for(d=0;d=this.viewport[0]&&b=this.viewport[1]&&cc-this.graph._last_trigger_time)&&this.drawBackCanvas();(this.dirty_canvas||a)&&this.drawFrontCanvas();this.fps=this.render_time?1/this.render_time:0;this.frame+=1}};q.prototype.drawFrontCanvas=function(){this.dirty_canvas=!1;this.ctx||(this.ctx=this.bgcanvas.getContext("2d"));var a=this.ctx;if(a){var b=this.canvas;a.start2D&& -!this.viewport&&(a.start2D(),a.restore(),a.setTransform(1,0,0,1,0,0));var c=this.viewport||this.dirty_area;c&&(a.save(),a.beginPath(),a.rect(c[0],c[1],c[2],c[3]),a.clip());this.clear_background&&(c?a.clearRect(c[0],c[1],c[2],c[3]):a.clearRect(0,0,b.width,b.height));this.bgcanvas==this.canvas?this.drawBackCanvas():a.drawImage(this.bgcanvas,0,0);if(this.onRender)this.onRender(b,a);this.show_info&&this.renderInfo(a,c?c[0]:0,c?c[1]:0);if(this.graph){a.save();this.ds.toCanvasContext(a);b=this.computeVisibleNodes(null, -this.visible_nodes);for(var d=0;d> ";b.fillText(d+c.getTitle(),.5*a.width,40);b.restore()}c=!1;this.onRenderBackground&&(c=this.onRenderBackground(a,b));this.viewport||(b.restore(),b.setTransform(1,0,0,1,0,0));this.visible_links.length=0;if(this.graph){b.save();this.ds.toCanvasContext(b);1.5>this.ds.scale&&!c&&this.clear_background_color&&(b.fillStyle=this.clear_background_color,b.fillRect(this.visible_area[0],this.visible_area[1],this.visible_area[2],this.visible_area[3]));if(this.background_image&&.5this.ds.scale;if(this.live_mode){if(!a.flags.collapsed&&(b.shadowColor="transparent",a.onDrawForeground))a.onDrawForeground(b,this,this.canvas)}else{var f=this.editor_alpha;b.globalAlpha=f;this.render_shadows&&!e?(b.shadowColor=h.DEFAULT_SHADOW_COLOR,b.shadowOffsetX=2*this.ds.scale,b.shadowOffsetY=2*this.ds.scale,b.shadowBlur=3*this.ds.scale):b.shadowColor="transparent";if(!a.flags.collapsed||!a.onDrawCollapsed||1!=a.onDrawCollapsed(b,this)){var g=a._shape||h.BOX_SHAPE; -N.set(a.size);var k=a.horizontal;if(a.flags.collapsed){b.font=this.inner_text_font;var m=a.getTitle?a.getTitle():a.title;null!=m&&(a._collapsed_width=Math.min(a.size[0],b.measureText(m).width+2*h.NODE_TITLE_HEIGHT),N[0]=a._collapsed_width,N[1]=0)}a.clip_area&&(b.save(),b.beginPath(),g==h.BOX_SHAPE?b.rect(0,0,N[0],N[1]):g==h.ROUND_SHAPE?b.roundRect(0,0,N[0],N[1],[10]):g==h.CIRCLE_SHAPE&&b.arc(.5*N[0],.5*N[1],.5*N[0],0,2*Math.PI),b.clip());a.has_errors&&(d="red");this.drawNodeShape(a,b,N,c,d,a.is_selected, -a.mouseOver);b.shadowColor="transparent";if(a.onDrawForeground)a.onDrawForeground(b,this,this.canvas);b.textAlign=k?"center":"left";b.font=this.inner_text_font;d=!e;var l=this.connecting_output;g=this.connecting_input;b.lineWidth=1;m=0;var n=new Float32Array(2);if(!a.flags.collapsed){if(a.inputs)for(c=0;cthis.ds.scale,m=a._shape||a.constructor.shape||h.ROUND_SHAPE,l=a.constructor.title_mode,n=!0;l==h.TRANSPARENT_TITLE||l==h.NO_TITLE?n=!1:l==h.AUTOHIDE_TITLE&& -g&&(n=!0);C[0]=0;C[1]=n?-e:0;C[2]=c[0]+1;C[3]=n?c[1]+e:c[1];g=b.globalAlpha;b.beginPath();m==h.BOX_SHAPE||k?b.fillRect(C[0],C[1],C[2],C[3]):m==h.ROUND_SHAPE||m==h.CARD_SHAPE?b.roundRect(C[0],C[1],C[2],C[3],m==h.CARD_SHAPE?[this.round_radius,this.round_radius,0,0]:[this.round_radius]):m==h.CIRCLE_SHAPE&&b.arc(.5*c[0],.5*c[1],.5*c[0],0,2*Math.PI);b.fill();!a.flags.collapsed&&n&&(b.shadowColor="transparent",b.fillStyle="rgba(0,0,0,0.2)",b.fillRect(0,-1,C[2],2));b.shadowColor="transparent";if(a.onDrawBackground)a.onDrawBackground(b, -this,this.canvas,this.graph_mouse);if(n||l==h.TRANSPARENT_TITLE){if(a.onDrawTitleBar)a.onDrawTitleBar(b,e,c,this.ds.scale,d);else if(l!=h.TRANSPARENT_TITLE&&(a.constructor.title_color||this.render_title_colored)){n=a.constructor.title_color||d;a.flags.collapsed&&(b.shadowColor=h.DEFAULT_SHADOW_COLOR);if(this.use_gradients){var p=q.gradients[n];p||(p=q.gradients[n]=b.createLinearGradient(0,0,400,0),p.addColorStop(0,n),p.addColorStop(1,"#000"));b.fillStyle=p}else b.fillStyle=n;b.beginPath();m==h.BOX_SHAPE|| -k?b.rect(0,-e,c[0]+1,e):(m==h.ROUND_SHAPE||m==h.CARD_SHAPE)&&b.roundRect(0,-e,c[0]+1,e,a.flags.collapsed?[this.round_radius]:[this.round_radius,this.round_radius,0,0]);b.fill();b.shadowColor="transparent"}n=!1;h.node_box_coloured_by_mode&&h.NODE_MODES_COLORS[a.mode]&&(n=h.NODE_MODES_COLORS[a.mode]);h.node_box_coloured_when_on&&(n=a.action_triggered?"#FFF":a.execute_triggered?"#AAA":n);if(a.onDrawTitleBox)a.onDrawTitleBox(b,e,c,this.ds.scale);else m==h.ROUND_SHAPE||m==h.CIRCLE_SHAPE||m==h.CARD_SHAPE? -(k&&(b.fillStyle="black",b.beginPath(),b.arc(.5*e,-.5*e,6,0,2*Math.PI),b.fill()),b.fillStyle=a.boxcolor||n||h.NODE_DEFAULT_BOXCOLOR,k?b.fillRect(.5*e-5,-.5*e-5,10,10):(b.beginPath(),b.arc(.5*e,-.5*e,5,0,2*Math.PI),b.fill())):(k&&(b.fillStyle="black",b.fillRect(.5*(e-10)-1,-.5*(e+10)-1,12,12)),b.fillStyle=a.boxcolor||n||h.NODE_DEFAULT_BOXCOLOR,b.fillRect(.5*(e-10),-.5*(e+10),10,10));b.globalAlpha=g;if(a.onDrawTitleText)a.onDrawTitleText(b,e,c,this.ds.scale,this.title_text_font,f);!k&&(b.font=this.title_text_font, -g=String(a.getTitle()))&&(b.fillStyle=f?h.NODE_SELECTED_TITLE_COLOR:a.constructor.title_text_color||this.node_title_color,a.flags.collapsed?(b.textAlign="left",b.measureText(g),b.fillText(g.substr(0,20),e,h.NODE_TITLE_TEXT_Y-e),b.textAlign="left"):(b.textAlign="left",b.fillText(g,e,h.NODE_TITLE_TEXT_Y-e)));a.flags.collapsed||!a.subgraph||a.skip_subgraph_button||(g=h.NODE_TITLE_HEIGHT,n=a.size[0]-g,p=h.isInsideRectangle(this.graph_mouse[0]-a.pos[0],this.graph_mouse[1]-a.pos[1],n+2,-g+2,g-4,g-4),b.fillStyle= -p?"#888":"#555",m==h.BOX_SHAPE||k?b.fillRect(n+2,-g+2,g-4,g-4):(b.beginPath(),b.roundRect(n+2,-g+2,g-4,g-4,[4]),b.fill()),b.fillStyle="#333",b.beginPath(),b.moveTo(n+.2*g,.6*-g),b.lineTo(n+.8*g,.6*-g),b.lineTo(n+.5*g,.3*-g),b.fill());if(a.onDrawTitle)a.onDrawTitle(b)}if(f){if(a.onBounding)a.onBounding(C);l==h.TRANSPARENT_TITLE&&(C[1]-=e,C[3]+=e);b.lineWidth=1;b.globalAlpha=.8;b.beginPath();m==h.BOX_SHAPE?b.rect(-6+C[0],-6+C[1],12+C[2],12+C[3]):m==h.ROUND_SHAPE||m==h.CARD_SHAPE&&a.flags.collapsed? -b.roundRect(-6+C[0],-6+C[1],12+C[2],12+C[3],[2*this.round_radius]):m==h.CARD_SHAPE?b.roundRect(-6+C[0],-6+C[1],12+C[2],12+C[3],[2*this.round_radius,2,2*this.round_radius,2]):m==h.CIRCLE_SHAPE&&b.arc(.5*c[0],.5*c[1],.5*c[0]+6,0,2*Math.PI);b.strokeStyle=h.NODE_BOX_OUTLINE_COLOR;b.stroke();b.strokeStyle=d;b.globalAlpha=1}0H[2]&&(H[0]+=H[2],H[2]=Math.abs(H[2]));0>H[3]&&(H[1]+=H[3],H[3]=Math.abs(H[3]));if(Z(H,aa)){var r=m.outputs[l];l=f.inputs[g];if(r&&l&&(m=r.dir||(m.horizontal?h.DOWN:h.RIGHT),l=l.dir||(f.horizontal?h.UP:h.LEFT),this.renderLink(a,n,p,k,!1,0,null,m,l),k&&k._last_time&&1E3>b-k._last_time)){r=2-.002*(b-k._last_time);var u=a.globalAlpha;a.globalAlpha=u* -r;this.renderLink(a,n,p,k,!0,r,"white",m,l);a.globalAlpha=u}}}}}}a.globalAlpha=1};q.prototype.renderLink=function(a,b,c,d,e,f,g,k,m,l){d&&this.visible_links.push(d);!g&&d&&(g=d.color||q.link_type_colors[d.type]);g||(g=this.default_link_color);null!=d&&this.highlighted_links[d.id]&&(g="#FFF");k=k||h.RIGHT;m=m||h.LEFT;var n=ba(b,c);this.render_connections_border&&.6b[1]?0:Math.PI,a.save(),a.translate(p[0],p[1]),a.rotate(n),a.beginPath(),a.moveTo(-5,-3),a.lineTo(0,7),a.lineTo(5,-3),a.fill(),a.restore(),a.save(),a.translate(d[0],d[1]),a.rotate(l),a.beginPath(),a.moveTo(-5,-3),a.lineTo(0,7),a.lineTo(5,-3),a.fill(),a.restore()),a.beginPath(),a.arc(e[0],e[1],5,0,2*Math.PI),a.fill());if(f)for(a.fillStyle=g,p=0;5>p;++p)f=(.001*h.getTime()+.2*p)%1,e=this.computeConnectionPoint(b,c,f,k, -m),a.beginPath(),a.arc(e[0],e[1],5,0,2*Math.PI),a.fill()};q.prototype.computeConnectionPoint=function(a,b,c,d,e){d=d||h.RIGHT;e=e||h.LEFT;var f=ba(a,b),g=[a[0],a[1]],k=[b[0],b[1]];switch(d){case h.LEFT:g[0]+=-.25*f;break;case h.RIGHT:g[0]+=.25*f;break;case h.UP:g[1]+=-.25*f;break;case h.DOWN:g[1]+=.25*f}switch(e){case h.LEFT:k[0]+=-.25*f;break;case h.RIGHT:k[0]+=.25*f;break;case h.UP:k[1]+=-.25*f;break;case h.DOWN:k[1]+=.25*f}d=(1-c)*(1-c)*(1-c);e=3*(1-c)*(1-c)*c;f=3*(1-c)*c*c;c*=c*c;return[d*a[0]+ -e*g[0]+f*k[0]+c*b[0],d*a[1]+e*g[1]+f*k[1]+c*b[1]]};q.prototype.drawExecutionOrder=function(a){a.shadowColor="transparent";a.globalAlpha=.25;a.textAlign="center";a.strokeStyle="white";a.globalAlpha=.75;for(var b=this.visible_nodes,c=0;cB&&(B=0);1z&&(z=0),1f||f>t-12||gr.last_y+u||void 0===r.last_y)){d=r.value;switch(r.type){case "button":c.type===h.pointerevents_method+"down"&&(r.callback&&setTimeout(function(){r.callback(r,l,a,b,c)},20),this.dirty_canvas=r.clicked=!0);break;case "slider":d= -r.value;n=ca((f-15)/(t-30),0,1);if(r.options.read_only)break;r.value=r.options.min+(r.options.max-r.options.min)*n;d!=r.value&&setTimeout(function(){e(r,r.value)},20);this.dirty_canvas=!0;break;case "number":case "combo":d=r.value;if(c.type==h.pointerevents_method+"move"&&"number"==r.type)m&&(r.value+=.1*m*(r.options.step||1)),null!=r.options.min&&r.valuer.options.max&&(r.value=r.options.max);else if(c.type==h.pointerevents_method+ -"down"){var x=r.options.values;x&&x.constructor===Function&&(x=r.options.values(r,a));var z=null;"number"!=r.type&&(z=x.constructor===Array?x:Object.keys(x));f=40>f?-1:f>t-40?1:0;if("number"==r.type)r.value+=.1*f*(r.options.step||1),null!=r.options.min&&r.valuer.options.max&&(r.value=r.options.max);else if(f)n=-1,this.last_mouseclick=0,n=x.constructor===Object?z.indexOf(String(r.value))+f:z.indexOf(r.value)+f,n>=z.length&&(n=z.length- -1),0>n&&(n=0),r.value=x.constructor===Array?x[n]:n;else{var B=x!=z?Object.values(x):x;new h.ContextMenu(B,{scale:Math.max(1,this.ds.scale),event:c,className:"dark",callback:E.bind(r)},n);function E(F,da,M){x!=z&&(F=B.indexOf(F));this.value=F;e(this,F);l.dirty_canvas=!0;return!1}}}else c.type==h.pointerevents_method+"up"&&"number"==r.type&&(f=40>f?-1:f>t-40?1:0,200>c.click_time&&0==f&&this.prompt("Value",r.value,function(E){if(/^[0-9+\-*/()\s]+|\d+\.\d+$/.test(E))try{E=eval(E)}catch(F){}this.value= -Number(E);e(this,this.value)}.bind(r),c));d!=r.value&&setTimeout(function(){e(this,this.value)}.bind(r),20);this.dirty_canvas=!0;break;case "toggle":c.type==h.pointerevents_method+"down"&&(r.value=!r.value,setTimeout(function(){e(r,r.value)},20));break;case "string":case "text":c.type==h.pointerevents_method+"down"&&this.prompt("Value",r.value,function(E){e(this,E)}.bind(r),c,r.options?r.options.multiline:!1);break;default:r.mouse&&(this.dirty_canvas=r.mouse(c,[f,g],a))}if(d!=r.value){if(a.onWidgetChanged)a.onWidgetChanged(r.name, -r.value,d,r);a.graph._version++}return r}}}return null};q.prototype.drawGroups=function(a,b){if(this.graph){a=this.graph._groups;b.save();b.globalAlpha=.5*this.editor_alpha;for(var c=0;cc&&.01>b.editor_alpha&&(clearInterval(d),1>c&&(b.live_mode=!0));1c.pos[0]+c.size[0])c=g;if(null===d||m+n>d.pos[1]+d.size[1])d=g;if(null===e||k"+(m.label?m.label:k)+""+a+"",value:k})}if(g.length)return new h.ContextMenu(g,{event:c,callback:function(l,n,p,r){e&&(n=this.getBoundingClientRect(),f.showEditPropertyValue(e,l.value,{position:[n.left,n.top]}))},parentMenu:d,allow_html:!0, -node:e},b),!1}};q.decodeHTML=function(a){var b=document.createElement("div");b.innerText=a;return b.innerHTML};q.onMenuResizeNode=function(a,b,c,d,e){if(e){a=function(g){g.size=g.computeSize();if(g.onResize)g.onResize(g.size)};b=q.active_canvas;if(!b.selected_nodes||1>=Object.keys(b.selected_nodes).length)a(e);else for(var f in b.selected_nodes)a(b.selected_nodes[f]);e.setDirtyCanvas(!0,!0)}};q.prototype.showLinkMenu=function(a,b){var c=this,d=c.graph.getNodeById(a.origin_id),e=c.graph.getNodeById(a.target_id), -f=!1;d&&d.outputs&&d.outputs[a.origin_slot]&&(f=d.outputs[a.origin_slot].type);var g=!1;e&&e.outputs&&e.outputs[a.target_slot]&&(g=e.inputs[a.target_slot].type);var k=new h.ContextMenu(["Add Node",null,"Delete",null],{event:b,title:null!=a.data?a.data.constructor.name:null,callback:function(m,l,n){switch(m){case "Add Node":q.onMenuAdd(null,null,n,k,function(p){p.inputs&&p.inputs.length&&p.outputs&&p.outputs.length&&d.connectByType(a.origin_slot,p,f)&&(p.connectByType(a.target_slot,e,g),p.pos[0]-= -.5*p.size[0])});break;case "Delete":c.graph.removeLink(a.id)}}});return!1};q.prototype.createDefaultNodeForSlot=function(a){a=a||{};a=Object.assign({nodeFrom:null,slotFrom:null,nodeTo:null,slotTo:null,position:[],nodeType:null,posAdd:[0,0],posSizeFix:[0,0]},a);var b=a.nodeFrom&&null!==a.slotFrom,c=!b&&a.nodeTo&&null!==a.slotTo;if(!b&&!c)return console.warn("No data passed to createDefaultNodeForSlot "+a.nodeFrom+" "+a.slotFrom+" "+a.nodeTo+" "+a.slotTo),!1;if(!a.nodeType)return console.warn("No type to createDefaultNodeForSlot"), -!1;var d=b?a.nodeFrom:a.nodeTo,e=b?a.slotFrom:a.slotTo;switch(typeof e){case "string":c=b?d.findOutputSlot(e,!1):d.findInputSlot(e,!1);e=b?d.outputs[e]:d.inputs[e];break;case "object":c=b?d.findOutputSlot(e.name):d.findInputSlot(e.name);break;case "number":c=e;e=b?d.outputs[e]:d.inputs[e];break;default:return console.warn("Cant get slot information "+e),!1}!1!==e&&!1!==c||console.warn("createDefaultNodeForSlot bad slotX "+e+" "+c);d=e.type==h.EVENT?"_event_":e.type;if((e=b?h.slot_types_default_out: -h.slot_types_default_in)&&e[d]){nodeNewType=!1;if("object"==typeof e[d]||"array"==typeof e[d])for(var f in e[d]){if(a.nodeType==e[d][f]||"AUTO"==a.nodeType){nodeNewType=e[d][f];break}}else if(a.nodeType==e[d]||"AUTO"==a.nodeType)nodeNewType=e[d];if(nodeNewType){f=!1;"object"==typeof nodeNewType&&nodeNewType.node&&(f=nodeNewType,nodeNewType=nodeNewType.node);if(e=h.createNode(nodeNewType)){if(f){if(f.properties)for(var g in f.properties)e.addProperty(g,f.properties[g]);if(f.inputs)for(g in e.inputs= -[],f.inputs)e.addOutput(f.inputs[g][0],f.inputs[g][1]);if(f.outputs)for(g in e.outputs=[],f.outputs)e.addOutput(f.outputs[g][0],f.outputs[g][1]);f.title&&(e.title=f.title);f.json&&e.configure(f.json)}this.graph.add(e);e.pos=[a.position[0]+a.posAdd[0]+(a.posSizeFix[0]?a.posSizeFix[0]*e.size[0]:0),a.position[1]+a.posAdd[1]+(a.posSizeFix[1]?a.posSizeFix[1]*e.size[1]:0)];b?a.nodeFrom.connectByType(c,e,d):a.nodeTo.connectByTypeOutput(c,e,d);return!0}console.log("failed creating "+nodeNewType)}}return!1}; -q.prototype.showConnectionMenu=function(a){a=a||{};var b=Object.assign({nodeFrom:null,slotFrom:null,nodeTo:null,slotTo:null,e:null},a),c=this,d=b.nodeFrom&&b.slotFrom;a=!d&&b.nodeTo&&b.slotTo;if(!d&&!a)return console.warn("No data passed to showConnectionMenu"),!1;a=d?b.nodeFrom:b.nodeTo;var e=d?b.slotFrom:b.slotTo,f=!1;switch(typeof e){case "string":f=d?a.findOutputSlot(e,!1):a.findInputSlot(e,!1);e=d?a.outputs[e]:a.inputs[e];break;case "object":f=d?a.findOutputSlot(e.name):a.findInputSlot(e.name); -break;case "number":f=e;e=d?a.outputs[e]:a.inputs[e];break;default:return console.warn("Cant get slot information "+e),!1}a=["Add Node",null];c.allow_searchbox&&(a.push("Search"),a.push(null));var g=e.type==h.EVENT?"_event_":e.type,k=d?h.slot_types_default_out:h.slot_types_default_in;if(k&&k[g])if("object"==typeof k[g]||"array"==typeof k[g])for(var m in k[g])a.push(k[g][m]);else a.push(k[g]);var l=new h.ContextMenu(a,{event:b.e,title:(e&&""!=e.name?e.name+(g?" | ":""):"")+(e&&g?g:""),callback:function(n, -p,r){switch(n){case "Add Node":q.onMenuAdd(null,null,r,l,function(u){d?b.nodeFrom.connectByType(f,u,g):b.nodeTo.connectByTypeOutput(f,u,g)});break;case "Search":d?c.showSearchBox(r,{node_from:b.nodeFrom,slot_from:e,type_filter_in:g}):c.showSearchBox(r,{node_to:b.nodeTo,slot_from:e,type_filter_out:g});break;default:c.createDefaultNodeForSlot(Object.assign(b,{position:[b.e.canvasX,b.e.canvasY],nodeType:n}))}}});return!1};q.onShowPropertyEditor=function(a,b,c,d,e){function f(){if(m){var p=m.value;"Number"== -a.type?p=Number(p):"Boolean"==a.type&&(p=!!p);e[g]=p;k.parentNode&&k.parentNode.removeChild(k);e.setDirtyCanvas(!0,!0)}}var g=a.property||"title";b=e[g];var k=document.createElement("div");k.is_modified=!1;k.className="graphdialog";k.innerHTML="";k.close=function(){k.parentNode&&k.parentNode.removeChild(k)};k.querySelector(".name").innerText=g;var m=k.querySelector(".value");m&&(m.value=b,m.addEventListener("blur", -function(p){this.focus()}),m.addEventListener("keydown",function(p){k.is_modified=!0;if(27==p.keyCode)k.close();else if(13==p.keyCode)f();else if(13!=p.keyCode&&"textarea"!=p.target.localName)return;p.preventDefault();p.stopPropagation()}));b=q.active_canvas.canvas;c=b.getBoundingClientRect();var l=d=-20;c&&(d-=c.left,l-=c.top);event?(k.style.left=event.clientX+d+"px",k.style.top=event.clientY+l+"px"):(k.style.left=.5*b.width+d+"px",k.style.top=.5*b.height+l+"px");k.querySelector("button").addEventListener("click", -f);b.parentNode.appendChild(k);m&&m.focus();var n=null;k.addEventListener("mouseleave",function(p){h.dialog_close_on_mouse_leave&&!k.is_modified&&h.dialog_close_on_mouse_leave&&(n=setTimeout(k.close,h.dialog_close_on_mouse_leave_delay))});k.addEventListener("mouseenter",function(p){h.dialog_close_on_mouse_leave&&n&&clearTimeout(n)})};q.prototype.prompt=function(a,b,c,d,e){var f=this;a=a||"";var g=document.createElement("div");g.is_modified=!1;g.className="graphdialog rounded";g.innerHTML=e?" ": -" ";g.close=function(){f.prompt_box=null;g.parentNode&&g.parentNode.removeChild(g)};e=q.active_canvas.canvas;e.parentNode.appendChild(g);1q.search_limit))break}}O=null;if(Array.prototype.filter)O=Object.keys(h.registered_node_types).filter(R);else for(D in O=[],h.registered_node_types)R(D)&&O.push(D);for(D=0;Dq.search_limit);D++);if(b.show_general_after_typefiltered&&(V.value||W.value)){filtered_extra=[];for(D in h.registered_node_types)R(D,{inTypeOverride:V&&V.value?"*":!1,outTypeOverride:W&&W.value? -"*":!1})&&filtered_extra.push(D);for(D=0;Dq.search_limit);D++);}if((V.value||W.value)&&0==t.childNodes.length&&b.show_general_if_none_on_typefilter){filtered_extra=[];for(D in h.registered_node_types)R(D,{skipFilter:!0})&&filtered_extra.push(D);for(D=0;Dq.search_limit);D++);}function R(U,J){J=J||{};J=Object.assign({skipFilter:!1, -inTypeOverride:!1,outTypeOverride:!1},J);var G=h.registered_node_types[U];if(ea&&G.filter!=ea||(!b.show_all_if_empty||y)&&-1===U.toLowerCase().indexOf(y))return!1;if(b.do_type_filter&&!J.skipFilter){G=V.value;!1!==J.inTypeOverride&&(G=J.inTypeOverride);if(V&&G&&h.registered_slot_in_types[G]&&h.registered_slot_in_types[G].nodes&&(G=h.registered_slot_in_types[G].nodes.includes(U),!1===G))return!1;G=W.value;!1!==J.outTypeOverride&&(G=J.outTypeOverride);if(W&&G&&h.registered_slot_out_types[G]&&h.registered_slot_out_types[G].nodes&& -(G=h.registered_slot_out_types[G].nodes.includes(U),!1===G))return!1}return!0}}}b=Object.assign({slot_from:null,node_from:null,node_to:null,do_type_filter:h.search_filter_enabled,type_filter_in:!1,type_filter_out:!1,show_general_if_none_on_typefilter:!0,show_general_after_typefiltered:!0,hide_on_mouse_leave:h.search_hide_on_mouse_leave,show_all_if_empty:!0,show_all_on_open:h.search_show_all_on_open},b||{});var f=this,g=q.active_canvas,k=g.canvas,m=k.ownerDocument||document,l=document.createElement("div"); -l.className="litegraph litesearchbox graphdialog rounded";l.innerHTML="Search ";b.do_type_filter&&(l.innerHTML+="",l.innerHTML+="");l.innerHTML+="
";m.fullscreenElement?m.fullscreenElement.appendChild(l):(m.body.appendChild(l),m.body.style.overflow="hidden"); -if(b.do_type_filter)var n=l.querySelector(".slot_in_type_filter"),p=l.querySelector(".slot_out_type_filter");l.close=function(){f.search_box=null;this.blur();k.focus();m.body.style.overflow="";setTimeout(function(){f.canvas.focus()},20);l.parentNode&&l.parentNode.removeChild(l)};1n.height-200&&(t.style.maxHeight=n.height-a.layerY-20+"px");E.focus();b.show_all_on_open&&e();return l};q.prototype.showEditPropertyValue=function(a,b,c){function d(){e(p.value)}function e(r){f&&f.values&&f.values.constructor===Object&&void 0!=f.values[r]&&(r=f.values[r]); -"number"==typeof a.properties[b]&&(r=Number(r));if("array"==g||"object"==g)r=JSON.parse(r);a.properties[b]=r;a.graph&&a.graph._version++;if(a.onPropertyChanged)a.onPropertyChanged(b,r);if(c.onclose)c.onclose();n.close();a.setDirtyCanvas(!0,!0)}if(a&&void 0!==a.properties[b]){c=c||{};var f=a.getPropertyInfo(b),g=f.type,k="";if("string"==g||"number"==g||"array"==g||"object"==g)k="";else if("enum"!=g&&"combo"!=g||!f.values)if("boolean"==g||"toggle"==g)k="";else{console.warn("unknown type: "+g);return}else{k=""}var n=this.createDialog(""+(f.label?f.label:b)+""+k+"",c),p=!1;if("enum"!=g&&"combo"!=g||!f.values)if("boolean"==g||"toggle"==g)(p=n.querySelector("input"))&& -p.addEventListener("click",function(r){n.modified();e(!!p.checked)});else{if(p=n.querySelector("input"))p.addEventListener("blur",function(r){this.focus()}),l=void 0!==a.properties[b]?a.properties[b]:"","string"!==g&&(l=JSON.stringify(l)),p.value=l,p.addEventListener("keydown",function(r){if(27==r.keyCode)n.close();else if(13==r.keyCode)d();else if(13!=r.keyCode){n.modified();return}r.preventDefault();r.stopPropagation()})}else p=n.querySelector("select"),p.addEventListener("change",function(r){n.modified(); -e(r.target.value)});p&&p.focus();n.querySelector("button").addEventListener("click",d);return n}};q.prototype.createDialog=function(a,b){b=Object.assign({checkForInput:!1,closeOnLeave:!0,closeOnLeave_checkModified:!0},b||{});var c=document.createElement("div");c.className="graphdialog";c.innerHTML=a;c.is_modified=!1;a=this.canvas.getBoundingClientRect();var d=-20,e=-20;a&&(d-=a.left,e-=a.top);b.position?(d+=b.position[0],e+=b.position[1]):b.event?(d+=b.event.clientX,e+=b.event.clientY):(d+=.5*this.canvas.width, -e+=.5*this.canvas.height);c.style.left=d+"px";c.style.top=e+"px";this.canvas.parentNode.appendChild(c);b.checkForInput&&(a=[],(a=c.querySelectorAll("input"))&&a.forEach(function(k){k.addEventListener("keydown",function(m){c.modified();if(27==m.keyCode)c.close();else if(13!=m.keyCode)return;m.preventDefault();m.stopPropagation()});k.focus()}));c.modified=function(){c.is_modified=!0};c.close=function(){c.parentNode&&c.parentNode.removeChild(c)};var f=null,g=!1;c.addEventListener("mouseleave",function(k){g|| -(b.closeOnLeave||h.dialog_close_on_mouse_leave)&&!c.is_modified&&h.dialog_close_on_mouse_leave&&(f=setTimeout(c.close,h.dialog_close_on_mouse_leave_delay))});c.addEventListener("mouseenter",function(k){(b.closeOnLeave||h.dialog_close_on_mouse_leave)&&f&&clearTimeout(f)});(a=c.querySelectorAll("select"))&&a.forEach(function(k){k.addEventListener("click",function(m){g++});k.addEventListener("blur",function(m){g=0});k.addEventListener("change",function(m){g=-1})});return c};q.prototype.createPanel=function(a, -b){b=b||{};var c=b.window||window,d=document.createElement("div");d.className="litegraph dialog";d.innerHTML="
";d.header=d.querySelector(".dialog-header");b.width&&(d.style.width=b.width+(b.width.constructor===Number?"px":""));b.height&&(d.style.height=b.height+(b.height.constructor===Number?"px":""));b.closable&& -(b=document.createElement("span"),b.innerHTML="✕",b.classList.add("close"),b.addEventListener("click",function(){d.close()}),d.header.appendChild(b));d.title_element=d.querySelector(".dialog-title");d.title_element.innerText=a;d.content=d.querySelector(".dialog-content");d.alt_content=d.querySelector(".dialog-alt-content");d.footer=d.querySelector(".dialog-footer");d.close=function(){if(d.onClose&&"function"==typeof d.onClose)d.onClose();d.parentNode&&d.parentNode.removeChild(d);this.parentNode&& -this.parentNode.removeChild(this)};d.toggleAltContent=function(e){if("undefined"!=typeof e){var f=e?"block":"none";e=e?"none":"block"}else f="block"!=d.alt_content.style.display?"block":"none",e="block"!=d.alt_content.style.display?"none":"block";d.alt_content.style.display=f;d.content.style.display=e};d.toggleFooterVisibility=function(e){d.footer.style.display="undefined"!=typeof e?e?"block":"none":"block"!=d.footer.style.display?"block":"none"};d.clear=function(){this.content.innerHTML=""};d.addHTML= -function(e,f,g){var k=document.createElement("div");f&&(k.className=f);k.innerHTML=e;g?d.footer.appendChild(k):d.content.appendChild(k);return k};d.addButton=function(e,f,g){var k=document.createElement("button");k.innerText=e;k.options=g;k.classList.add("btn");k.addEventListener("click",f);d.footer.appendChild(k);return k};d.addSeparator=function(){var e=document.createElement("div");e.className="separator";d.content.appendChild(e)};d.addWidget=function(e,f,g,k,m){function l(u,t){k.callback&&k.callback(u, -t,k);m&&m(u,t,k)}k=k||{};var n=String(g);e=e.toLowerCase();"number"==e&&(n=g.toFixed(3));var p=document.createElement("div");p.className="property";p.innerHTML="";p.querySelector(".property_name").innerText=k.label||f;var r=p.querySelector(".property_value");r.innerText=n;p.dataset.property=f;p.dataset.type=k.type||e;p.options=k;p.value=g;if("code"==e)p.addEventListener("click",function(u){d.inner_showCodePad(this.dataset.property)}); -else if("boolean"==e)p.classList.add("boolean"),g&&p.classList.add("bool-on"),p.addEventListener("click",function(){var u=this.dataset.property;this.value=!this.value;this.classList.toggle("bool-on");this.querySelector(".property_value").innerText=this.value?"true":"false";l(u,this.value)});else if("string"==e||"number"==e)r.setAttribute("contenteditable",!0),r.addEventListener("keydown",function(u){"Enter"!=u.code||"string"==e&&u.shiftKey||(u.preventDefault(),this.blur())}),r.addEventListener("blur", -function(){var u=this.innerText,t=this.parentNode.dataset.property;"number"==this.parentNode.dataset.type&&(u=Number(u));l(t,u)});else if("enum"==e||"combo"==e)n=q.getPropertyPrintableValue(g,k.values),r.innerText=n,r.addEventListener("click",function(u){var t=this.parentNode.dataset.property,x=this;new h.ContextMenu(k.values||[],{event:u,className:"dark",callback:function(z,B,E){x.innerText=z;l(t,z);return!1}},c)});d.content.appendChild(p);return p};if(d.onOpen&&"function"==typeof d.onOpen)d.onOpen(); -return d};q.getPropertyPrintableValue=function(a,b){if(!b||b.constructor===Array)return String(a);if(b.constructor===Object){var c="",d;for(d in b)if(b[d]==a){c=d;break}return String(a)+" ("+c+")"}};q.prototype.closePanels=function(){var a=document.querySelector("#node-panel");a&&a.close();(a=document.querySelector("#option-panel"))&&a.close()};q.prototype.showShowGraphOptionsPanel=function(a,b,c,d){if(this.constructor&&"HTMLDivElement"==this.constructor.name){if(!(b&&b.event&&b.event.target&&b.event.target.lgraphcanvas)){console.warn("Canvas not found"); -return}var e=b.event.target.lgraphcanvas}else e=this;e.closePanels();a=e.getCanvasWindow();panel=e.createPanel("Options",{closable:!0,window:a,onOpen:function(){e.OPTIONPANEL_IS_OPEN=!0},onClose:function(){e.OPTIONPANEL_IS_OPEN=!1;e.options_panel=null}});e.options_panel=panel;panel.id="option-panel";panel.classList.add("settings");(function(){panel.content.innerHTML="";var f=function(l,n,p){p&&p.key&&(l=p.key);p.values&&(n=Object.values(p.values).indexOf(n));e[l]=n},g=h.availableCanvasOptions;g.sort(); -for(var k in g){var m=g[k];panel.addWidget("boolean",m,e[m],{key:m,on:"True",off:"False"},f)}panel.addWidget("combo","Render mode",h.LINK_RENDER_MODES[e.links_render_mode],{key:"links_render_mode",values:h.LINK_RENDER_MODES},f);panel.addSeparator();panel.footer.innerHTML=""})();e.canvas.parentNode.appendChild(panel)};q.prototype.showShowNodePanel=function(a){function b(){e.content.innerHTML="";e.addHTML(""+a.type+""+(a.constructor.desc||"")+""); -e.addHTML("

Properties

");var f=function(l,n){d.graph.beforeChange(a);switch(l){case "Title":a.title=n;break;case "Mode":l=Object.values(h.NODE_MODES).indexOf(n);0<=l&&h.NODE_MODES[l]?a.changeMode(l):console.warn("unexpected mode: "+n);break;case "Color":q.node_colors[n]?(a.color=q.node_colors[n].color,a.bgcolor=q.node_colors[n].bgcolor):console.warn("unexpected color: "+n);break;default:a.setProperty(l,n)}d.graph.afterChange();d.dirty_canvas=!0};e.addWidget("string","Title",a.title,{},f); -e.addWidget("combo","Mode",h.NODE_MODES[a.mode],{values:h.NODE_MODES},f);var g="";void 0!==a.color&&(g=Object.keys(q.node_colors).filter(function(l){return q.node_colors[l].color==a.color}));e.addWidget("combo","Color",g,{values:Object.keys(q.node_colors)},f);for(var k in a.properties){g=a.properties[k];var m=a.getPropertyInfo(k);a.onAddPropertyToPanel&&a.onAddPropertyToPanel(k,e)||e.addWidget(m.widget||m.type,k,g,m,f)}e.addSeparator();if(a.onShowCustomPanelInfo)a.onShowCustomPanelInfo(e);e.footer.innerHTML= -"";e.addButton("Delete",function(){a.block_delete||(a.graph.remove(a),e.close())}).classList.add("delete")}this.SELECTED_NODE=a;this.closePanels();var c=this.getCanvasWindow(),d=this,e=this.createPanel(a.title||"",{closable:!0,window:c,onOpen:function(){d.NODEPANEL_IS_OPEN=!0},onClose:function(){d.NODEPANEL_IS_OPEN=!1;d.node_panel=null}});d.node_panel=e;e.id="node-panel";e.node=a;e.classList.add("settings");e.inner_showCodePad=function(f){e.classList.remove("settings");e.classList.add("centered"); -e.alt_content.innerHTML="";var g=e.alt_content.querySelector("textarea"),k=function(){e.toggleAltContent(!1);e.toggleFooterVisibility(!0);g.parentNode.removeChild(g);e.classList.add("settings");e.classList.remove("centered");b()};g.value=a.properties[f];g.addEventListener("keydown",function(l){"Enter"==l.code&&l.ctrlKey&&(a.setProperty(f,g.value),k())});e.toggleAltContent(!0);e.toggleFooterVisibility(!1);g.style.height="calc(100% - 40px)";var m=e.addButton("Assign", -function(){a.setProperty(f,g.value);k()});e.alt_content.appendChild(m);m=e.addButton("Close",k);m.style.float="right";e.alt_content.appendChild(m)};b();this.canvas.parentNode.appendChild(e)};q.prototype.showSubgraphPropertiesDialog=function(a){function b(){d.clear();if(a.inputs)for(var e=0;e","subgraph_property"); -g.dataset.name=f.name;g.dataset.slot=e;g.querySelector(".name").innerText=f.name;g.querySelector(".type").innerText=f.type;g.querySelector("button").addEventListener("click",function(k){a.removeInput(Number(this.parentNode.dataset.slot));b()})}}}console.log("showing subgraph properties dialog");var c=this.canvas.parentNode.querySelector(".subgraph_dialog");c&&c.close();var d=this.createPanel("Subgraph Inputs",{closable:!0,width:500});d.node=a;d.classList.add("subgraph_dialog");d.addHTML(" + NameType", -"subgraph_property extra",!0).querySelector("button").addEventListener("click",function(e){e=this.parentNode;var f=e.querySelector(".name").value,g=e.querySelector(".type").value;f&&-1==a.findInputSlot(f)&&(a.addInput(f,g),e.querySelector(".name").value="",e.querySelector(".type").value="",b())});b();this.canvas.parentNode.appendChild(d);return d};q.prototype.showSubgraphPropertiesDialogRight=function(a){function b(){e.clear();if(a.outputs)for(var f=0;f","subgraph_property");k.dataset.name=g.name;k.dataset.slot=f;k.querySelector(".name").innerText=g.name;k.querySelector(".type").innerText=g.type;k.querySelector("button").addEventListener("click",function(m){a.removeOutput(Number(this.parentNode.dataset.slot));b()})}}}function c(){var f=this.parentNode,g=f.querySelector(".name").value,k=f.querySelector(".type").value;g&&-1==a.findOutputSlot(g)&& -(a.addOutput(g,k),f.querySelector(".name").value="",f.querySelector(".type").value="",b())}var d=this.canvas.parentNode.querySelector(".subgraph_dialog");d&&d.close();var e=this.createPanel("Subgraph Outputs",{closable:!0,width:500});e.node=a;e.classList.add("subgraph_dialog");d=e.addHTML(" + NameType","subgraph_property extra",!0);d.querySelector(".name").addEventListener("keydown", -function(f){13==f.keyCode&&c.apply(this)});d.querySelector("button").addEventListener("click",function(f){c.apply(this)});b();this.canvas.parentNode.appendChild(e);return e};q.prototype.checkPanels=function(){if(this.canvas)for(var a=this.canvas.parentNode.querySelectorAll(".litegraph.dialog"),b=0;b=Object.keys(a.selected_nodes).length)e.collapse(); -else for(var f in a.selected_nodes)a.selected_nodes[f].collapse();e.graph.afterChange()};q.onMenuNodePin=function(a,b,c,d,e){e.pin()};q.onMenuNodeMode=function(a,b,c,d,e){new h.ContextMenu(h.NODE_MODES,{event:c,callback:function(f){if(e){var g=Object.values(h.NODE_MODES).indexOf(f),k=function(n){0<=g&&h.NODE_MODES[g]?n.changeMode(g):(console.warn("unexpected mode: "+f),n.changeMode(h.ALWAYS))},m=q.active_canvas;if(!m.selected_nodes||1>=Object.keys(m.selected_nodes).length)k(e);else for(var l in m.selected_nodes)k(m.selected_nodes[l])}}, -parentMenu:d,node:e});return!1};q.onMenuNodeColors=function(a,b,c,d,e){if(!e)throw"no node for color";b=[];b.push({value:null,content:"No color"});for(var f in q.node_colors)a=q.node_colors[f],a={value:f,content:""+f+""},b.push(a);new h.ContextMenu(b,{event:c,callback:function(g){if(e){var k=g.value?q.node_colors[g.value]: -null;g=function(n){k?n.constructor===h.LGraphGroup?n.color=k.groupcolor:(n.color=k.color,n.bgcolor=k.bgcolor):(delete n.color,delete n.bgcolor)};var m=q.active_canvas;if(!m.selected_nodes||1>=Object.keys(m.selected_nodes).length)g(e);else for(var l in m.selected_nodes)g(m.selected_nodes[l]);e.setDirtyCanvas(!0,!0)}},parentMenu:d,node:e});return!1};q.onMenuNodeShapes=function(a,b,c,d,e){if(!e)throw"no node passed";new h.ContextMenu(h.VALID_SHAPES,{event:c,callback:function(f){if(e){e.graph.beforeChange(); -var g=q.active_canvas;if(!g.selected_nodes||1>=Object.keys(g.selected_nodes).length)e.shape=f;else for(var k in g.selected_nodes)g.selected_nodes[k].shape=f;e.graph.afterChange();e.setDirtyCanvas(!0)}},parentMenu:d,node:e});return!1};q.onMenuNodeRemove=function(a,b,c,d,e){if(!e)throw"no node passed";a=e.graph;a.beforeChange();b=q.active_canvas;if(!b.selected_nodes||1>=Object.keys(b.selected_nodes).length)!1!==e.removable&&a.remove(e);else for(var f in b.selected_nodes)c=b.selected_nodes[f],!1!==c.removable&& -a.remove(c);a.afterChange();e.setDirtyCanvas(!0,!0)};q.onMenuNodeToSubgraph=function(a,b,c,d,e){a=e.graph;if(b=q.active_canvas)c=Object.values(b.selected_nodes||{}),c.length||(c=[e]),d=h.createNode("graph/subgraph"),d.pos=e.pos.concat(),a.add(d),d.buildFromNodes(c),b.deselectAllNodes(),e.setDirtyCanvas(!0,!0)};q.onMenuNodeClone=function(a,b,c,d,e){e.graph.beforeChange();var f={};a=function(k){if(!1!==k.clonable){var m=k.clone();m&&(m.pos=[k.pos[0]+5,k.pos[1]+5],k.graph.add(m),f[m.id]=m)}};b=q.active_canvas; -if(!b.selected_nodes||1>=Object.keys(b.selected_nodes).length)a(e);else for(var g in b.selected_nodes)a(b.selected_nodes[g]);Object.keys(f).length&&b.selectNodes(f);e.graph.afterChange();e.setDirtyCanvas(!0,!0)};q.node_colors={red:{color:"#322",bgcolor:"#533",groupcolor:"#A88"},brown:{color:"#332922",bgcolor:"#593930",groupcolor:"#b06634"},green:{color:"#232",bgcolor:"#353",groupcolor:"#8A8"},blue:{color:"#223",bgcolor:"#335",groupcolor:"#88A"},pale_blue:{color:"#2a363b",bgcolor:"#3f5159",groupcolor:"#3f789e"}, -cyan:{color:"#233",bgcolor:"#355",groupcolor:"#8AA"},purple:{color:"#323",bgcolor:"#535",groupcolor:"#a1309b"},yellow:{color:"#432",bgcolor:"#653",groupcolor:"#b58b2a"},black:{color:"#222",bgcolor:"#000",groupcolor:"#444"}};q.prototype.getCanvasMenuOptions=function(){if(this.getMenuOptions)var a=this.getMenuOptions();else a=[{content:"Add Node",has_submenu:!0,callback:q.onMenuAdd},{content:"Add Group",callback:q.onGroupAdd}],1Name", -m),r=p.querySelector("input");r&&n&&(r.value=n.label||"");var u=function(){a.graph.beforeChange();r.value&&(n&&(n.label=r.value),c.setDirty(!0));p.close();a.graph.afterChange()};p.querySelector("button").addEventListener("click",u);r.addEventListener("keydown",function(t){p.is_modified=!0;if(27==t.keyCode)p.close();else if(13==t.keyCode)u();else if(13!=t.keyCode&&"textarea"!=t.target.localName)return;t.preventDefault();t.stopPropagation()});r.focus()}},extra:a};a&&(f.title=a.type);var g=null;a&&(g= -a.getSlotInPosition(b.canvasX,b.canvasY),q.active_node=a);g?(e=[],a.getSlotMenuOptions?e=a.getSlotMenuOptions(g):(g&&g.output&&g.output.links&&g.output.links.length&&e.push({content:"Disconnect Links",slot:g}),b=g.input||g.output,b.removable&&e.push(b.locked?"Cannot remove":{content:"Remove Slot",slot:g}),b.nameLocked||e.push({content:"Rename Slot",slot:g})),f.title=(g.input?g.input.type:g.output.type)||"*",g.input&&g.input.type==h.ACTION&&(f.title="Action"),g.output&&g.output.type==h.EVENT&&(f.title= -"Event")):a?e=this.getNodeMenuOptions(a):(e=this.getCanvasMenuOptions(),(g=this.graph.getGroupOnPos(b.canvasX,b.canvasY))&&e.push(null,{content:"Edit Group",has_submenu:!0,submenu:{title:"Group",extra:g,options:this.getGroupMenuOptions(g)}}));e&&new h.ContextMenu(e,f,d)};h.compareObjects=function(a,b){for(var c in a)if(a[c]!=b[c])return!1;return!0};h.distance=ba;h.colorToString=function(a){return"rgba("+Math.round(255*a[0]).toFixed()+","+Math.round(255*a[1]).toFixed()+","+Math.round(255*a[2]).toFixed()+ -","+(4==a.length?a[3].toFixed(2):"1.0")+")"};h.isInsideRectangle=K;h.growBounding=function(a,b,c){ba[2]&&(a[2]=b);ca[3]&&(a[3]=c)};h.isInsideBounding=function(a,b){return a[0]b[1][0]||a[1]>b[1][1]?!1:!0};h.overlapBounding=Z;h.hex2num=function(a){"#"==a.charAt(0)&&(a=a.slice(1));a=a.toUpperCase();for(var b=Array(3),c=0,d,e,f=0;6>f;f+=2)d="0123456789ABCDEF".indexOf(a.charAt(f)),e="0123456789ABCDEF".indexOf(a.charAt(f+1)),b[c]=16*d+e,c++;return b}; -h.num2hex=function(a){for(var b="#",c,d,e=0;3>e;e++)c=a[e]/16,d=a[e]%16,b+="0123456789ABCDEF".charAt(c)+"0123456789ABCDEF".charAt(d);return b};Q.prototype.addItem=function(a,b,c){function d(m){var l=this.value;l&&l.has_submenu&&e.call(this,m)}function e(m){var l=this.value,n=!0;f.current_submenu&&f.current_submenu.close(m);if(c.callback){var p=c.callback.call(this,l,c,m,f,c.node);!0===p&&(n=!1)}if(l&&(l.callback&&!c.ignore_item_callbacks&&!0!==l.disabled&&(p=l.callback.call(this,l,c,m,f,c.extra), -!0===p&&(n=!1)),l.submenu)){if(!l.submenu.options)throw"ContextMenu submenu needs options";new f.constructor(l.submenu.options,{callback:l.submenu.callback,event:m,parentMenu:f,ignore_item_callbacks:l.submenu.ignore_item_callbacks,title:l.submenu.title,extra:l.submenu.extra,autoopen:c.autoopen});n=!1}n&&!f.lock&&f.close()}var f=this;c=c||{};var g=document.createElement("div");g.className="litemenu-entry submenu";var k=!1;if(null===b)g.classList.add("separator");else{g.innerHTML=b&&b.title?b.title: -a;if(g.value=b)b.disabled&&(k=!0,g.classList.add("disabled")),(b.submenu||b.has_submenu)&&g.classList.add("has_submenu");"function"==typeof b?(g.dataset.value=a,g.onclick_callback=b):g.dataset.value=b;b.className&&(g.className+=" "+b.className)}this.root.appendChild(g);k||g.addEventListener("click",e);!k&&c.autoopen&&h.pointerListenerAdd(g,"enter",d);return g};Q.prototype.close=function(a,b){this.root.parentNode&&this.root.parentNode.removeChild(this.root);this.parentMenu&&!b&&(this.parentMenu.lock= -!1,this.parentMenu.current_submenu=null,void 0===a?this.parentMenu.close():a&&!Q.isCursorOverElement(a,this.parentMenu.root)&&Q.trigger(this.parentMenu.root,h.pointerevents_method+"leave",a));this.current_submenu&&this.current_submenu.close(a,!0);this.root.closing_timer&&clearTimeout(this.root.closing_timer)};Q.trigger=function(a,b,c,d){var e=document.createEvent("CustomEvent");e.initCustomEvent(b,!0,!0,c);e.srcElement=d;a.dispatchEvent?a.dispatchEvent(e):a.__events&&a.__events.dispatchEvent(e);return e}; -Q.prototype.getTopMenu=function(){return this.options.parentMenu?this.options.parentMenu.getTopMenu():this};Q.prototype.getFirstEvent=function(){return this.options.parentMenu?this.options.parentMenu.getFirstEvent():this.options.event};Q.isCursorOverElement=function(a,b){var c=a.clientX;a=a.clientY;return(b=b.getBoundingClientRect())?a>b.top&&ab.left&&cMath.abs(b))return d[1];a=(a-d[0])/b;return d[1]*(1-a)+e[1]*a}}return 0}};T.prototype.draw=function(a,b,c,d,e,f){if(c=this.points){this.size=b;var g=b[0]-2*this.margin;b=b[1]-2*this.margin;e=e||"#666";a.save();a.translate(this.margin,this.margin);d&&(a.fillStyle="#111",a.fillRect(0,0,g,b),a.fillStyle="#222",a.fillRect(.5*g,0,1,b),a.strokeStyle="#333", -a.strokeRect(0,0,g,b));a.strokeStyle=e;f&&(a.globalAlpha=.5);a.beginPath();for(d=0;da[1])){var d=this.size[0]-2*this.margin,e=this.size[1]-2*this.margin,f=a[0]-this.margin;a=a[1]-this.margin; -this.selected=this.getCloserPoint([f,a],30/b.ds.scale);-1==this.selected&&(b=[f/d,1-a/e],c.push(b),c.sort(function(g,k){return g[0]-k[0]}),this.selected=c.indexOf(b),this.must_update=!0);if(-1!=this.selected)return!0}};T.prototype.onMouseMove=function(a,b){var c=this.points;if(c){var d=this.selected;if(!(0>d)){var e=(a[0]-this.margin)/(this.size[0]-2*this.margin),f=(a[1]-this.margin)/(this.size[1]-2*this.margin);this._nearest=this.getCloserPoint([a[0]-this.margin,a[1]-this.margin],30/b.ds.scale); -if(b=c[d]){var g=0==d||d==c.length-1;!g&&(-10>a[0]||a[0]>this.size[0]+10||-10>a[1]||a[1]>this.size[1]+10)?(c.splice(d,1),this.selected=-1):(b[0]=g?0==d?0:1:ca(e,0,1),b[1]=1-ca(f,0,1),c.sort(function(k,m){return k[0]-m[0]}),this.selected=c.indexOf(b),this.must_update=!0)}}}};T.prototype.onMouseUp=function(a,b){this.selected=-1;return!1};T.prototype.getCloserPoint=function(a,b){var c=this.points;if(!c)return-1;b=b||30;for(var d=this.size[0]-2*this.margin,e=this.size[1]-2*this.margin,f=c.length,g=[0, -0],k=1E6,m=-1,l=0;lk||n>b||(m=l,k=n)}return m};h.CurveEditor=T;h.getParameterNames=function(a){return(a+"").replace(/[/][/].*$/gm,"").replace(/\s+/g,"").replace(/[/][*][^/*]*[*][/]/g,"").split("){",1)[0].replace(/^[^(]*[(]/,"").replace(/=[^,]+/g,"").split(",").filter(Boolean)};h.pointerListenerAdd=function(a,b,c,d=!1){if(a&&a.addEventListener&&b&&"function"===typeof c){var e=h.pointerevents_method;if("pointer"==e&&!window.PointerEvent)switch(console.warn("sMethod=='pointer' && !window.PointerEvent"), -console.log("Converting pointer["+b+"] : down move up cancel enter TO touchstart touchmove touchend, etc .."),b){case "down":e="touch";b="start";break;case "move":e="touch";break;case "up":e="touch";b="end";break;case "cancel":e="touch";break;case "enter":console.log("debug: Should I send a move event?");break;default:console.warn("PointerEvent not available in this browser ? The event "+b+" would not be called")}switch(b){case "down":case "up":case "move":case "over":case "out":case "enter":a.addEventListener(e+ -b,c,d);case "leave":case "cancel":case "gotpointercapture":case "lostpointercapture":if("mouse"!=e)return a.addEventListener(e+b,c,d);default:return a.addEventListener(b,c,d)}}};h.pointerListenerRemove=function(a,b,c,d=!1){if(a&&a.removeEventListener&&b&&"function"===typeof c)switch(b){case "down":case "up":case "move":case "over":case "out":case "enter":"pointer"!=h.pointerevents_method&&"mouse"!=h.pointerevents_method||a.removeEventListener(h.pointerevents_method+b,c,d);case "leave":case "cancel":case "gotpointercapture":case "lostpointercapture":if("pointer"== -h.pointerevents_method)return a.removeEventListener(h.pointerevents_method+b,c,d);default:return a.removeEventListener(b,c,d)}};X.clamp=ca;"undefined"==typeof window||window.requestAnimationFrame||(window.requestAnimationFrame=window.webkitRequestAnimationFrame||window.mozRequestAnimationFrame||function(a){window.setTimeout(a,1E3/60)})})(this); -"undefined"!=typeof exports&&(exports.LiteGraph=this.LiteGraph,exports.LGraph=this.LGraph,exports.LLink=this.LLink,exports.LGraphNode=this.LGraphNode,exports.LGraphGroup=this.LGraphGroup,exports.DragAndScale=this.DragAndScale,exports.LGraphCanvas=this.LGraphCanvas,exports.ContextMenu=this.ContextMenu); diff --git a/build/litegraph.core.pack.js b/build/litegraph.core.pack.js new file mode 100644 index 000000000..22cbf60d2 --- /dev/null +++ b/build/litegraph.core.pack.js @@ -0,0 +1,2 @@ +/*packed*/!function(global){var LiteGraph=global.LiteGraph={VERSION:.4,CANVAS_GRID_SIZE:10,NODE_TITLE_HEIGHT:30,NODE_TITLE_TEXT_Y:20,NODE_SLOT_HEIGHT:20,NODE_WIDGET_HEIGHT:20,NODE_WIDTH:140,NODE_MIN_WIDTH:50,NODE_COLLAPSED_RADIUS:10,NODE_COLLAPSED_WIDTH:80,NODE_TITLE_COLOR:"#999",NODE_SELECTED_TITLE_COLOR:"#FFF",NODE_TEXT_SIZE:14,NODE_TEXT_COLOR:"#AAA",NODE_SUBTEXT_SIZE:12,NODE_DEFAULT_COLOR:"#333",NODE_DEFAULT_BGCOLOR:"#353535",NODE_DEFAULT_BOXCOLOR:"#666",NODE_DEFAULT_SHAPE:"box",NODE_BOX_OUTLINE_COLOR:"#FFF",DEFAULT_SHADOW_COLOR:"rgba(0,0,0,0.5)",DEFAULT_GROUP_FONT:24,WIDGET_BGCOLOR:"#222",WIDGET_OUTLINE_COLOR:"#666",WIDGET_TEXT_COLOR:"#DDD",WIDGET_SECONDARY_TEXT_COLOR:"#999",LINK_COLOR:"#9A9",EVENT_LINK_COLOR:"#A86",CONNECTING_LINK_COLOR:"#AFA",MAX_NUMBER_OF_NODES:1e3,DEFAULT_POSITION:[100,100],VALID_SHAPES:["default","box","round","card"],BOX_SHAPE:1,ROUND_SHAPE:2,CIRCLE_SHAPE:3,CARD_SHAPE:4,ARROW_SHAPE:5,GRID_SHAPE:6,INPUT:1,OUTPUT:2,EVENT:-1,ACTION:-1,NODE_MODES:["Always","On Event","Never","On Trigger"],NODE_MODES_COLORS:["#666","#422","#333","#224","#626"],ALWAYS:0,ON_EVENT:1,NEVER:2,ON_TRIGGER:3,UP:1,DOWN:2,LEFT:3,RIGHT:4,CENTER:5,LINK_RENDER_MODES:["Straight","Linear","Spline"],STRAIGHT_LINK:0,LINEAR_LINK:1,SPLINE_LINK:2,NORMAL_TITLE:0,NO_TITLE:1,TRANSPARENT_TITLE:2,AUTOHIDE_TITLE:3,VERTICAL_LAYOUT:"vertical",proxy:null,node_images_path:"",debug:!1,catch_exceptions:!0,throw_errors:!0,allow_scripts:!1,use_deferred_actions:!0,registered_node_types:{},node_types_by_file_extension:{},Nodes:{},Globals:{},searchbox_extras:{},auto_sort_node_types:!1,node_box_coloured_when_on:!1,node_box_coloured_by_mode:!1,dialog_close_on_mouse_leave:!0,dialog_close_on_mouse_leave_delay:500,shift_click_do_break_link_from:!1,click_do_break_link_to:!1,search_hide_on_mouse_leave:!0,search_filter_enabled:!1,search_show_all_on_open:!0,auto_load_slot_types:!1,registered_slot_in_types:{},registered_slot_out_types:{},slot_types_in:[],slot_types_out:[],slot_types_default_in:[],slot_types_default_out:[],alt_drag_do_clone_nodes:!1,do_add_triggers_slots:!1,allow_multi_output_for_events:!0,middle_click_slot_add_default_node:!1,release_link_on_empty_shows_menu:!1,pointerevents_method:"mouse",ctrl_shift_v_paste_connect_unselected_outputs:!1,use_uuids:!1,registerNodeType:function(type,base_class){if(!base_class.prototype)throw"Cannot register a simple object, it must be a class with a prototype";base_class.type=type,LiteGraph.debug&&console.log("Node registered: "+type);var i,classname=base_class.name,pos=type.lastIndexOf("/");for(i in base_class.category=type.substring(0,pos),base_class.title||(base_class.title=classname),LGraphNode.prototype)base_class.prototype[i]||(base_class.prototype[i]=LGraphNode.prototype[i]);pos=this.registered_node_types[type];if(pos&&console.log("replacing node type: "+type),!Object.prototype.hasOwnProperty.call(base_class.prototype,"shape")&&(Object.defineProperty(base_class.prototype,"shape",{set:function(v){switch(v){case"default":delete this._shape;break;case"box":this._shape=LiteGraph.BOX_SHAPE;break;case"round":this._shape=LiteGraph.ROUND_SHAPE;break;case"circle":this._shape=LiteGraph.CIRCLE_SHAPE;break;case"card":this._shape=LiteGraph.CARD_SHAPE;break;default:this._shape=v}},get:function(){return this._shape},enumerable:!0,configurable:!0}),base_class.supported_extensions))for(let i in base_class.supported_extensions){var ext=base_class.supported_extensions[i];ext&&ext.constructor===String&&(this.node_types_by_file_extension[ext.toLowerCase()]=base_class)}(this.registered_node_types[type]=base_class).constructor.name&&(this.Nodes[classname]=base_class),LiteGraph.onNodeTypeRegistered&&LiteGraph.onNodeTypeRegistered(type,base_class),pos&&LiteGraph.onNodeTypeReplaced&&LiteGraph.onNodeTypeReplaced(type,base_class,pos),base_class.prototype.onPropertyChange&&console.warn("LiteGraph node class "+type+" has onPropertyChange method, it must be called onPropertyChanged with d at the end"),this.auto_load_slot_types&&new base_class(base_class.title||"tmpnode")},unregisterNodeType:function(type){var base_class=type.constructor===String?this.registered_node_types[type]:type;if(!base_class)throw"node type not found: "+type;delete this.registered_node_types[base_class.type],base_class.constructor.name&&delete this.Nodes[base_class.constructor.name]},registerNodeAndSlotType:function(type,slot_type,out){out=out||!1;var class_type=(type.constructor===String&&"anonymous"!==this.registered_node_types[type]?this.registered_node_types[type]:type).constructor.type;let allTypes=[];allTypes="string"==typeof slot_type?slot_type.split(","):slot_type==this.EVENT||slot_type==this.ACTION?["_event_"]:["*"];for(let i=0;i(a^16*Math.random()>>a/4).toString(16))},isValidConnection:function(type_a,type_b){if(""!=type_b&&"*"!==type_b||(type_b=0),!(type_a=""!=type_a&&"*"!==type_a?type_a:0)||!type_b||type_a==type_b||type_a==LiteGraph.EVENT&&type_b==LiteGraph.ACTION)return!0;if(type_a=String(type_a),type_b=String(type_b),type_a=type_a.toLowerCase(),type_b=type_b.toLowerCase(),-1==type_a.indexOf(",")&&-1==type_b.indexOf(","))return type_a==type_b;for(var supported_types_a=type_a.split(","),supported_types_b=type_b.split(","),i=0;imax_size&&(max_size=node.size[max_size_index]),layout==LiteGraph.VERTICAL_LAYOUT?0:1);y+=node.size[max_size_index]+margin+LiteGraph.NODE_TITLE_HEIGHT}x+=max_size+margin}}this.setDirtyCanvas(!0,!0)},LGraph.prototype.getTime=function(){return this.globaltime},LGraph.prototype.getFixedTime=function(){return this.fixedtime},LGraph.prototype.getElapsedTime=function(){return this.elapsed_time},LGraph.prototype.sendEventToAllNodes=function(eventname,params,mode){mode=mode||LiteGraph.ALWAYS;var nodes=this._nodes_in_order||this._nodes;if(nodes)for(var j=0,l=nodes.length;j=LiteGraph.MAX_NUMBER_OF_NODES)throw"LiteGraph: max number of nodes in a graph reached";return LiteGraph.use_uuids?null!=node.id&&-1!=node.id||(node.id=LiteGraph.uuidv4()):null==node.id||-1==node.id?node.id=++this.last_node_id:this.last_node_id=this.outputs.length)){var output_info=this.outputs[slot];if(output_info&&(output_info._data=data,this.outputs[slot].links))for(var i=0;i=this.outputs.length)){var output_info=this.outputs[slot];if(output_info&&(output_info.type=type,this.outputs[slot].links))for(var i=0;i=this.inputs.length||null==this.inputs[slot].link))return slot=this.inputs[slot].link,(slot=this.graph.links[slot])?(force_update&&(force_update=this.graph.getNodeById(slot.origin_id))&&(force_update.updateOutputData?force_update.updateOutputData(slot.origin_slot):force_update.onExecute&&force_update.onExecute()),slot.data):null},LGraphNode.prototype.getInputDataType=function(slot){var node;return this.inputs&&!(slot>=this.inputs.length||null==this.inputs[slot].link)&&(slot=this.inputs[slot].link,slot=this.graph.links[slot])?(node=this.graph.getNodeById(slot.origin_id))?(node=node.outputs[slot.origin_slot])?node.type:null:slot.type:null},LGraphNode.prototype.getInputDataByName=function(slot_name,force_update){slot_name=this.findInputSlot(slot_name);return-1==slot_name?null:this.getInputData(slot_name,force_update)},LGraphNode.prototype.isInputConnected=function(slot){return!!this.inputs&&slot=this.inputs.length)&&(slot=this.inputs[slot])&&null!==slot.link&&(slot=this.graph.links[slot.link])?this.graph.getNodeById(slot.origin_id):null},LGraphNode.prototype.getInputOrProperty=function(name){if(!this.inputs||!this.inputs.length)return this.properties?this.properties[name]:null;for(var i=0,l=this.inputs.length;i=this.outputs.length?null:this.outputs[slot]._data},LGraphNode.prototype.getOutputInfo=function(slot){return this.outputs&&slot=this.outputs.length)return null;var output=this.outputs[slot];if(!output.links||0==output.links.length)return null;for(var r=[],i=0;ix&&this.pos[1]-margin_top-marginy)return!0;return!1},LGraphNode.prototype.getSlotInPosition=function(x,y){var link_pos=new Float32Array(2);if(this.inputs)for(var i=0,l=this.inputs.length;i=this.outputs.length)return LiteGraph.debug&&console.log("Connect: Error, slot number not found"),null;if(!(target_node=target_node&&target_node.constructor===Number?this.graph.getNodeById(target_node):target_node))throw"target node is null";if(target_node==this)return null;if(target_slot.constructor===String){if(-1==(target_slot=target_node.findInputSlot(target_slot)))return LiteGraph.debug&&console.log("Connect: Error, no slot of name "+target_slot),null}else if(target_slot===LiteGraph.EVENT){if(!LiteGraph.do_add_triggers_slots)return null;target_node.changeMode(LiteGraph.ON_TRIGGER),target_slot=target_node.findInputSlot("onTrigger")}else if(!target_node.inputs||target_slot>=target_node.inputs.length)return LiteGraph.debug&&console.log("Connect: Error, slot number not found"),null;var link_info,changed=!1,input=target_node.inputs[target_slot],output=this.outputs[slot];return this.outputs[slot]?!1!==(target_slot=target_node.onBeforeConnectInput?target_node.onBeforeConnectInput(target_slot):target_slot)&&null!==target_slot&&LiteGraph.isValidConnection(output.type,input.type)?target_node.onConnectInput&&!1===target_node.onConnectInput(target_slot,output.type,output,this,slot)||this.onConnectOutput&&!1===this.onConnectOutput(slot,input.type,input,target_node,target_slot)?null:(target_node.inputs[target_slot]&&null!=target_node.inputs[target_slot].link&&(this.graph.beforeChange(),target_node.disconnectInput(target_slot,{doProcessChange:!1}),changed=!0),null!==output.links&&output.links.length&&output.type===LiteGraph.EVENT&&!LiteGraph.allow_multi_output_for_events&&(this.graph.beforeChange(),this.disconnectOutput(slot,!1,{doProcessChange:!1}),changed=!0),link_info=new LLink(LiteGraph.use_uuids?LiteGraph.uuidv4():++this.graph.last_link_id,input.type||output.type,this.id,slot,target_node.id,target_slot),this.graph.links[link_info.id]=link_info,null==output.links&&(output.links=[]),output.links.push(link_info.id),target_node.inputs[target_slot].link=link_info.id,this.graph&&this.graph._version++,this.onConnectionsChange&&this.onConnectionsChange(LiteGraph.OUTPUT,slot,!0,link_info,output),target_node.onConnectionsChange&&target_node.onConnectionsChange(LiteGraph.INPUT,target_slot,!0,link_info,input),this.graph&&this.graph.onNodeConnectionChange&&(this.graph.onNodeConnectionChange(LiteGraph.INPUT,target_node,target_slot,this,slot),this.graph.onNodeConnectionChange(LiteGraph.OUTPUT,this,slot,target_node,target_slot)),this.setDirtyCanvas(!1,!0),this.graph.afterChange(),this.graph.connectionChange(this,link_info),link_info):(this.setDirtyCanvas(!1,!0),changed&&this.graph.connectionChange(this,null),null):null},LGraphNode.prototype.disconnectOutput=function(slot,target_node){if(slot.constructor===String){if(-1==(slot=this.findOutputSlot(slot)))return LiteGraph.debug&&console.log("Connect: Error, no slot of name "+slot),!1}else if(!this.outputs||slot>=this.outputs.length)return LiteGraph.debug&&console.log("Connect: Error, slot number not found"),!1;var output=this.outputs[slot];if(!output||!output.links||0==output.links.length)return!1;if(target_node){if(!(target_node=target_node.constructor===Number?this.graph.getNodeById(target_node):target_node))throw"Target Node not found";for(var i=0,l=output.links.length;i=this.inputs.length)return LiteGraph.debug&&console.log("Connect: Error, slot number not found"),!1;var input=this.inputs[slot];if(!input)return!1;var link_id=this.inputs[slot].link;if(null!=link_id){this.inputs[slot].link=null;var link_info=this.graph.links[link_id];if(link_info){var target_node=this.graph.getNodeById(link_info.origin_id);if(!target_node)return!1;var output=target_node.outputs[link_info.origin_slot];if(!output||!output.links||0==output.links.length)return!1;for(var i=0,l=output.links.length;iLGraphNode.MAX_CONSOLE&&this.console.shift(),this.graph.onNodeTrace&&this.graph.onNodeTrace(this,msg)},LGraphNode.prototype.setDirtyCanvas=function(dirty_foreground,dirty_background){this.graph&&this.graph.sendActionToCanvas("setDirty",[dirty_foreground,dirty_background])},LGraphNode.prototype.loadImage=function(url){var img=new Image,that=(img.src=LiteGraph.node_images_path+url,img.ready=!1,this);return img.onload=function(){this.ready=!0,that.setDirtyCanvas(!0)},img},LGraphNode.prototype.captureInput=function(v){if(this.graph&&this.graph.list_of_graphcanvas)for(var list=this.graph.list_of_graphcanvas,i=0;i=this.viewport[0]&&x=this.viewport[1]&&rectthis.max_scale&&(value=this.max_scale),value!=this.scale&&this.element&&(rect=this.element.getBoundingClientRect())&&(zooming_center=zooming_center||[.5*rect.width,.5*rect.height],rect=this.convertCanvasToOffset(zooming_center),this.scale=value,Math.abs(this.scale-1)<.01&&(this.scale=1),zooming_center=[(value=this.convertCanvasToOffset(zooming_center))[0]-rect[0],value[1]-rect[1]],this.offset[0]+=zooming_center[0],this.offset[1]+=zooming_center[1],this.onredraw)&&this.onredraw(this)},DragAndScale.prototype.changeDeltaScale=function(value,zooming_center){this.changeScale(this.scale*value,zooming_center)},DragAndScale.prototype.reset=function(){this.scale=1,this.offset[0]=0,this.offset[1]=0},global.LGraphCanvas=LiteGraph.LGraphCanvas=LGraphCanvas,LGraphCanvas.DEFAULT_BACKGROUND_IMAGE="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAIAAAD/gAIDAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAQBJREFUeNrs1rEKwjAUhlETUkj3vP9rdmr1Ysammk2w5wdxuLgcMHyptfawuZX4pJSWZTnfnu/lnIe/jNNxHHGNn//HNbbv+4dr6V+11uF527arU7+u63qfa/bnmh8sWLBgwYJlqRf8MEptXPBXJXa37BSl3ixYsGDBMliwFLyCV/DeLIMFCxYsWLBMwSt4Be/NggXLYMGCBUvBK3iNruC9WbBgwYJlsGApeAWv4L1ZBgsWLFiwYJmCV/AK3psFC5bBggULloJX8BpdwXuzYMGCBctgwVLwCl7Be7MMFixYsGDBsu8FH1FaSmExVfAxBa/gvVmwYMGCZbBg/W4vAQYA5tRF9QYlv/QAAAAASUVORK5CYII=",LGraphCanvas.link_type_colors={"-1":LiteGraph.EVENT_LINK_COLOR,number:"#AAA",node:"#DCA"},LGraphCanvas.gradients={},LGraphCanvas.prototype.clear=function(){this.frame=0,this.last_draw_time=0,this.render_time=0,this.fps=0,this.dragging_rectangle=null,this.selected_nodes={},this.selected_group=null,this.visible_nodes=[],this.node_dragged=null,this.node_over=null,this.node_capturing_input=null,this.connecting_node=null,this.highlighted_links={},this.dragging_canvas=!1,this.dirty_canvas=!0,this.dirty_bgcanvas=!0,this.dirty_area=null,this.node_in_panel=null,this.node_widget=null,this.last_mouse=[0,0],this.last_mouseclick=0,this.pointer_is_down=!1,this.pointer_is_double=!1,this.visible_area.set([0,0,0,0]),this.onClear&&this.onClear()},LGraphCanvas.prototype.setGraph=function(graph,skip_clear){this.graph!=graph&&(skip_clear||this.clear(),!graph&&this.graph?this.graph.detachCanvas(this):(graph.attachCanvas(this),this._graph_stack&&(this._graph_stack=null),this.setDirty(!0,!0)))},LGraphCanvas.prototype.getTopGraph=function(){return this._graph_stack.length?this._graph_stack[0]:this.graph},LGraphCanvas.prototype.openSubgraph=function(graph){if(!graph)throw"graph cannot be null";if(this.graph==graph)throw"graph cannot be the same";this.clear(),this.graph&&(this._graph_stack||(this._graph_stack=[]),this._graph_stack.push(this.graph)),graph.attachCanvas(this),this.checkPanels(),this.setDirty(!0,!0)},LGraphCanvas.prototype.closeSubgraph=function(){var subgraph_node,graph;this._graph_stack&&0!=this._graph_stack.length&&(subgraph_node=this.graph._subgraph_node,graph=this._graph_stack.pop(),this.selected_nodes={},this.highlighted_links={},graph.attachCanvas(this),this.setDirty(!0,!0),subgraph_node&&(this.centerOnNode(subgraph_node),this.selectNodes([subgraph_node])),this.ds.offset=[0,0],this.ds.scale=1)},LGraphCanvas.prototype.getCurrentGraph=function(){return this.graph},LGraphCanvas.prototype.setCanvas=function(canvas,skip_events){if(canvas&&canvas.constructor===String&&!(canvas=document.getElementById(canvas)))throw"Error creating LiteGraph canvas: Canvas not found";if(canvas!==this.canvas&&(canvas||!this.canvas||skip_events||this.unbindEvents(),this.canvas=canvas,this.ds.element=canvas)){if(canvas.className+=" lgraphcanvas",canvas.data=this,canvas.tabindex="1",this.bgcanvas=null,this.bgcanvas||(this.bgcanvas=document.createElement("canvas"),this.bgcanvas.width=this.canvas.width,this.bgcanvas.height=this.canvas.height),null==canvas.getContext){if("canvas"!=canvas.localName)throw"Element supplied for LGraphCanvas must be a element, you passed a "+canvas.localName;throw"This browser doesn't support Canvas"}null==(this.ctx=canvas.getContext("2d"))&&(canvas.webgl_enabled||console.warn("This canvas seems to be WebGL, enabling WebGL renderer"),this.enableWebGL()),skip_events||this.bindEvents()}},LGraphCanvas.prototype._doNothing=function(e){return e.preventDefault(),!1},LGraphCanvas.prototype._doReturnTrue=function(e){return e.preventDefault(),!0},LGraphCanvas.prototype.bindEvents=function(){var canvas,document;this._events_binded?console.warn("LGraphCanvas: events already binded"):(canvas=this.canvas,document=this.getCanvasWindow().document,this._mousedown_callback=this.processMouseDown.bind(this),this._mousewheel_callback=this.processMouseWheel.bind(this),this._mousemove_callback=this.processMouseMove.bind(this),this._mouseup_callback=this.processMouseUp.bind(this),LiteGraph.pointerListenerAdd(canvas,"down",this._mousedown_callback,!0),canvas.addEventListener("mousewheel",this._mousewheel_callback,!1),LiteGraph.pointerListenerAdd(canvas,"up",this._mouseup_callback,!0),LiteGraph.pointerListenerAdd(canvas,"move",this._mousemove_callback),canvas.addEventListener("contextmenu",this._doNothing),canvas.addEventListener("DOMMouseScroll",this._mousewheel_callback,!1),this._key_callback=this.processKey.bind(this),canvas.setAttribute("tabindex",1),canvas.addEventListener("keydown",this._key_callback,!0),document.addEventListener("keyup",this._key_callback,!0),this._ondrop_callback=this.processDrop.bind(this),canvas.addEventListener("dragover",this._doNothing,!1),canvas.addEventListener("dragend",this._doNothing,!1),canvas.addEventListener("drop",this._ondrop_callback,!1),canvas.addEventListener("dragenter",this._doReturnTrue,!1),this._events_binded=!0)},LGraphCanvas.prototype.unbindEvents=function(){var document;this._events_binded?(document=this.getCanvasWindow().document,LiteGraph.pointerListenerRemove(this.canvas,"move",this._mousedown_callback),LiteGraph.pointerListenerRemove(this.canvas,"up",this._mousedown_callback),LiteGraph.pointerListenerRemove(this.canvas,"down",this._mousedown_callback),this.canvas.removeEventListener("mousewheel",this._mousewheel_callback),this.canvas.removeEventListener("DOMMouseScroll",this._mousewheel_callback),this.canvas.removeEventListener("keydown",this._key_callback),document.removeEventListener("keyup",this._key_callback),this.canvas.removeEventListener("contextmenu",this._doNothing),this.canvas.removeEventListener("drop",this._ondrop_callback),this.canvas.removeEventListener("dragenter",this._doReturnTrue),this._mousedown_callback=null,this._mousewheel_callback=null,this._key_callback=null,this._ondrop_callback=null,this._events_binded=!1):console.warn("LGraphCanvas: no events binded")},LGraphCanvas.getFileExtension=function(url){var question=url.indexOf("?"),question=(url=-1!=question?url.substr(0,question):url).lastIndexOf(".");return-1==question?"":url.substr(question+1).toLowerCase()},LGraphCanvas.prototype.enableWebGL=function(){if("undefined"==typeof GL)throw"litegl.js must be included to use a WebGL canvas";if("undefined"==typeof enableWebGLCanvas)throw"webglCanvas.js must be included to use this feature";this.gl=this.ctx=enableWebGLCanvas(this.canvas),this.ctx.webgl=!0,this.bgcanvas=this.canvas,this.bgctx=this.gl,this.canvas.webgl_enabled=!0},LGraphCanvas.prototype.setDirty=function(fgcanvas,bgcanvas){fgcanvas&&(this.dirty_canvas=!0),bgcanvas&&(this.dirty_bgcanvas=!0)},LGraphCanvas.prototype.getCanvasWindow=function(){var doc;return this.canvas?(doc=this.canvas.ownerDocument).defaultView||doc.parentWindow:window},LGraphCanvas.prototype.startRendering=function(){function renderFrame(){this.pause_rendering||this.draw();var window=this.getCanvasWindow();this.is_rendering&&window.requestAnimationFrame(renderFrame.bind(this))}this.is_rendering||(this.is_rendering=!0,renderFrame.call(this))},LGraphCanvas.prototype.stopRendering=function(){this.is_rendering=!1},LGraphCanvas.prototype.blockClick=function(){this.block_click=!0,this.last_mouseclick=0},LGraphCanvas.prototype.processMouseDown=function(e){if(this.set_canvas_dirty_on_mouse_event&&(this.dirty_canvas=!0),this.graph){this.adjustMouseEvent(e);var ref_window=this.getCanvasWindow(),that=(ref_window.document,LGraphCanvas.active_canvas=this),x=e.clientX,y=e.clientY,x=(this.ds.viewport=this.viewport,!this.viewport||this.viewport&&x>=this.viewport[0]&&x=this.viewport[1]&&ycenter[0]+4||e.canvasYcenter[1]+4)){this.showLinkMenu(link,e),this.over_link_center=null;break}}this.selected_group=this.graph.getGroupOnPos(e.canvasX,e.canvasY),this.selected_group_resizing=!1,this.selected_group&&!this.read_only&&(e.ctrlKey&&(this.dragging_rectangle=null),distance([e.canvasX,e.canvasY],[this.selected_group.pos[0]+this.selected_group.size[0],this.selected_group.pos[1]+this.selected_group.size[1]])*this.ds.scale<10?this.selected_group_resizing=!0:this.selected_group.recomputeInsideNodes()),is_double_click&&!this.read_only&&this.allow_searchbox&&(this.showSearchBox(e),e.preventDefault(),e.stopPropagation()),x=!0}}else{if(this.live_mode||node.flags.pinned||this.bringToFront(node),this.allow_interaction&&!this.connecting_node&&!node.flags.collapsed&&!this.live_mode)if(!skip_action&&!1!==node.resizable&&isInsideRectangle(e.canvasX,e.canvasY,node.pos[0]+node.size[0]-5,node.pos[1]+node.size[1]-5,10,10))this.graph.beforeChange(),this.resizing_node=node,this.canvas.style.cursor="se-resize",skip_action=!0;else{if(node.outputs)for(var i=0,l=node.outputs.length;inode.size[0]-LiteGraph.NODE_TITLE_HEIGHT&&y[1]<0&&(that=this,setTimeout(function(){that.openSubgraph(node.subgraph)},10)),this.live_mode&&(block_drag_node=x=!0)),block_drag_node?node.is_selected||this.processNodeSelected(node,e):(this.allow_dragnodes&&(this.graph.beforeChange(),this.node_dragged=node),this.processNodeSelected(node,e)),this.dirty_canvas=!0)}!skip_action&&x&&this.allow_dragcanvas&&(this.dragging_canvas=!0)}return this.last_mouse[0]=e.clientX,this.last_mouse[1]=e.clientY,this.last_mouseclick=LiteGraph.getTime(),this.last_mouse_dragging=!0,this.graph.change(),ref_window.document.activeElement&&("input"==ref_window.document.activeElement.nodeName.toLowerCase()||"textarea"==ref_window.document.activeElement.nodeName.toLowerCase())||e.preventDefault(),e.stopPropagation(),this.onMouseDown&&this.onMouseDown(e),!1}}}},LGraphCanvas.prototype.processMouseMove=function(e){if(this.autoresize&&this.resize(),this.set_canvas_dirty_on_mouse_event&&(this.dirty_canvas=!0),this.graph){(LGraphCanvas.active_canvas=this).adjustMouseEvent(e);var mouse=[e.clientX,e.clientY],delta=(this.mouse[0]=mouse[0],this.mouse[1]=mouse[1],[mouse[0]-this.last_mouse[0],mouse[1]-this.last_mouse[1]]);if(this.last_mouse=mouse,this.graph_mouse[0]=e.canvasX,this.graph_mouse[1]=e.canvasY,!this.block_click){e.dragging=this.last_mouse_dragging,this.node_widget&&(this.processNodeWidgets(this.node_widget[0],this.graph_mouse,e,this.node_widget[1]),this.dirty_canvas=!0);var node=this.graph.getNodeOnPos(e.canvasX,e.canvasY,this.visible_nodes);if(this.dragging_rectangle)this.dragging_rectangle[2]=e.canvasX-this.dragging_rectangle[0],this.dragging_rectangle[3]=e.canvasY-this.dragging_rectangle[1],this.dirty_canvas=!0;else if(this.selected_group&&!this.read_only)this.selected_group_resizing?this.selected_group.size=[e.canvasX-this.selected_group.pos[0],e.canvasY-this.selected_group.pos[1]]:(mouse=delta[0]/this.ds.scale,deltay=delta[1]/this.ds.scale,this.selected_group.move(mouse,deltay,e.ctrlKey),this.selected_group._nodes.length&&(this.dirty_canvas=!0)),this.dirty_bgcanvas=!0;else if(this.dragging_canvas)this.ds.offset[0]+=delta[0]/this.ds.scale,this.ds.offset[1]+=delta[1]/this.ds.scale,this.dirty_canvas=!0,this.dirty_bgcanvas=!0;else if((this.allow_interaction||node&&node.flags.allow_interaction)&&!this.read_only){this.connecting_node&&(this.dirty_canvas=!0);for(var pos,slot,slot_type,deltay,i=0,l=this.graph._nodes.length;icenter[0]+4||e.canvasYcenter[1]+4)){over_link=link;break}}over_link!=this.over_link_center&&(this.over_link_center=over_link,this.dirty_canvas=!0),this.canvas&&(this.canvas.style.cursor="")}if(this.node_capturing_input&&this.node_capturing_input!=node&&this.node_capturing_input.onMouseMove&&this.node_capturing_input.onMouseMove(e,[e.canvasX-this.node_capturing_input.pos[0],e.canvasY-this.node_capturing_input.pos[1]],this),this.node_dragged&&!this.live_mode){for(var i in this.selected_nodes){var n=this.selected_nodes[i];n.pos[0]+=delta[0]/this.ds.scale,n.pos[1]+=delta[1]/this.ds.scale,n.is_selected||this.processNodeSelected(n,e)}this.dirty_canvas=!0,this.dirty_bgcanvas=!0}this.resizing_node&&!this.live_mode&&(mouse=[e.canvasX-this.resizing_node.pos[0],e.canvasY-this.resizing_node.pos[1]],deltay=this.resizing_node.computeSize(),mouse[0]=Math.max(deltay[0],mouse[0]),mouse[1]=Math.max(deltay[1],mouse[1]),this.resizing_node.setSize(mouse),this.canvas.style.cursor="se-resize",this.dirty_canvas=!0,this.dirty_bgcanvas=!0)}}return e.preventDefault(),!1}},LGraphCanvas.prototype.processMouseUp=function(e){var is_primary=void 0===e.isPrimary||e.isPrimary;if(!is_primary)return!1;if(this.set_canvas_dirty_on_mouse_event&&(this.dirty_canvas=!0),this.graph){var document=this.getCanvasWindow().document,document=((LGraphCanvas.active_canvas=this).options.skip_events||(LiteGraph.pointerListenerRemove(document,"move",this._mousemove_callback,!0),LiteGraph.pointerListenerAdd(this.canvas,"move",this._mousemove_callback,!0),LiteGraph.pointerListenerRemove(document,"up",this._mouseup_callback,!0)),this.adjustMouseEvent(e),LiteGraph.getTime());if(e.click_time=document-this.last_mouseclick,this.last_mouse_dragging=!1,this.last_click_position=null,this.block_click&&(this.block_click=!1),1==e.which){this.node_widget&&this.processNodeWidgets(this.node_widget[0],this.graph_mouse,e),this.node_widget=null,this.selected_group&&(document=this.selected_group.pos[0]-Math.round(this.selected_group.pos[0]),diffy=this.selected_group.pos[1]-Math.round(this.selected_group.pos[1]),this.selected_group.move(document,diffy,e.ctrlKey),this.selected_group.pos[0]=Math.round(this.selected_group.pos[0]),this.selected_group.pos[1]=Math.round(this.selected_group.pos[1]),this.selected_group._nodes.length&&(this.dirty_canvas=!0),this.selected_group=null),this.selected_group_resizing=!1;var slot,document=this.graph.getNodeOnPos(e.canvasX,e.canvasY,this.visible_nodes);if(this.dragging_rectangle){if(this.graph){var nodes=this.graph._nodes,node_bounding=new Float32Array(4),diffy=Math.abs(this.dragging_rectangle[2]),h=Math.abs(this.dragging_rectangle[3]),startx=this.dragging_rectangle[2]<0?this.dragging_rectangle[0]-diffy:this.dragging_rectangle[0],starty=this.dragging_rectangle[3]<0?this.dragging_rectangle[1]-h:this.dragging_rectangle[1];if(this.dragging_rectangle[0]=startx,this.dragging_rectangle[1]=starty,this.dragging_rectangle[2]=diffy,this.dragging_rectangle[3]=h,!document||10=this.viewport[0]&&x=this.viewport[1]&&yclipboard_info.nodes[i].pos[0]&&(posMin[0]=clipboard_info.nodes[i].pos[0],posMinIndexes[0]=i),posMin[1]>clipboard_info.nodes[i].pos[1]&&(posMin[1]=clipboard_info.nodes[i].pos[1],posMinIndexes[1]=i)):(posMin=[clipboard_info.nodes[i].pos[0],clipboard_info.nodes[i].pos[1]],posMinIndexes=[i,i]);for(var nodes=[],i=0;i=this.viewport[0]&&x=this.viewport[1]&&y> ";ctx.fillText(title+viewport.getTitle(),.5*canvas.width,40),ctx.restore()}var that,viewport=!1;this.onRenderBackground&&(viewport=this.onRenderBackground(canvas,ctx)),this.viewport||(ctx.restore(),ctx.setTransform(1,0,0,1,0,0)),this.visible_links.length=0,this.graph&&(ctx.save(),this.ds.toCanvasContext(ctx),this.ds.scale<1.5&&!viewport&&this.clear_background_color&&(ctx.fillStyle=this.clear_background_color,ctx.fillRect(this.visible_area[0],this.visible_area[1],this.visible_area[2],this.visible_area[3])),this.background_image&&.5bounding[2]&&(bounding[2]=x),ybounding[3]&&(bounding[3]=y)}function isInsideBounding(p,bb){return!(p[0]bb[1][0]||p[1]>bb[1][1])}function overlapBounding(a,b){var A_end_x=a[0]+a[2],A_end_y=a[1]+a[3],B_end_x=b[0]+b[2],B_end_y=b[1]+b[3];return!(a[0]>B_end_x||a[1]>B_end_y||A_end_xrect.width-root_rect.width-10&&(eventClass=rect.width-root_rect.width-10),rect.height)&&top>rect.height-root_rect.height-10&&(top=rect.height-root_rect.height-10),root.style.left=eventClass+"px",root.style.top=top+"px",options.scale&&(root.style.transform="scale("+options.scale+")")}function CurveEditor(points){this.points=points,this.selected=-1,this.nearest=-1,this.size=null,this.must_update=!0,this.margin=5}function clamp(v,a,b){return va[1]?0:Math.PI,ctx.save(),ctx.translate(skip_border[0],skip_border[1]),ctx.rotate(angleA),ctx.beginPath(),ctx.moveTo(-5,-3),ctx.lineTo(0,7),ctx.lineTo(5,-3),ctx.fill(),ctx.restore(),ctx.save(),ctx.translate(posC[0],posC[1]),ctx.rotate(angleB),ctx.beginPath(),ctx.moveTo(-5,-3),ctx.lineTo(0,7),ctx.lineTo(5,-3),ctx.fill(),ctx.restore()),ctx.beginPath(),ctx.arc(pos[0],pos[1],5,0,2*Math.PI),ctx.fill()),flow){ctx.fillStyle=color;for(i=0;i<5;++i){var f=(.001*LiteGraph.getTime()+.2*i)%1,pos=this.computeConnectionPoint(a,b,f,start_dir,end_dir);ctx.beginPath(),ctx.arc(pos[0],pos[1],5,0,2*Math.PI),ctx.fill()}}},LGraphCanvas.prototype.computeConnectionPoint=function(a,b,t,start_dir,end_dir){start_dir=start_dir||LiteGraph.RIGHT,end_dir=end_dir||LiteGraph.LEFT;var dist=distance(a,b),p0=a,p1=[a[0],a[1]],p2=[b[0],b[1]],a=b;switch(start_dir){case LiteGraph.LEFT:p1[0]+=-.25*dist;break;case LiteGraph.RIGHT:p1[0]+=.25*dist;break;case LiteGraph.UP:p1[1]+=-.25*dist;break;case LiteGraph.DOWN:p1[1]+=.25*dist}switch(end_dir){case LiteGraph.LEFT:p2[0]+=-.25*dist;break;case LiteGraph.RIGHT:p2[0]+=.25*dist;break;case LiteGraph.UP:p2[1]+=-.25*dist;break;case LiteGraph.DOWN:p2[1]+=.25*dist}b=(1-t)*(1-t)*(1-t),start_dir=(1-t)*(1-t)*3*t,end_dir=3*(1-t)*(t*t),t*=t*t;return[b*p0[0]+start_dir*p1[0]+end_dir*p2[0]+t*a[0],b*p0[1]+start_dir*p1[1]+end_dir*p2[1]+t*a[1]]},LGraphCanvas.prototype.drawExecutionOrder=function(ctx){ctx.shadowColor="transparent",ctx.globalAlpha=.25,ctx.textAlign="center",ctx.strokeStyle="white",ctx.globalAlpha=.75;for(var visible_nodes=this.visible_nodes,i=0;iw.last_y+widget_height||void 0===w.last_y)){var old_value=w.value;switch(w.type){case"button":event.type===LiteGraph.pointerevents_method+"down"&&(w.callback&&setTimeout(function(){w.callback(w,that,node,pos,event)},20),w.clicked=!0,this.dirty_canvas=!0);break;case"slider":var old_value=w.value,nvalue=clamp((x-15)/(widget_width-30),0,1);w.options.read_only||(w.value=w.options.min+(w.options.max-w.options.min)*nvalue,old_value!=w.value&&setTimeout(function(){inner_value_change(w,w.value)},20),this.dirty_canvas=!0);break;case"number":case"combo":var old_value=w.value,values,values_list,delta,index,text_values,menu,delta;function inner_clicked(v,option,event){return values!=values_list&&(v=text_values.indexOf(v)),this.value=v,inner_value_change(this,v),!(that.dirty_canvas=!0)}event.type==LiteGraph.pointerevents_method+"move"&&"number"==w.type?(deltaX&&(w.value+=.1*deltaX*(w.options.step||1)),null!=w.options.min&&w.valuew.options.max&&(w.value=w.options.max)):event.type==LiteGraph.pointerevents_method+"down"?(values=w.options.values,values&&values.constructor===Function&&(values=w.options.values(w,node)),values_list=null,"number"!=w.type&&(values_list=values.constructor===Array?values:Object.keys(values)),delta=x<40?-1:widget_width-40w.options.max&&(w.value=w.options.max)):delta?(index=-1,this.last_mouseclick=0,index=values.constructor===Object?values_list.indexOf(String(w.value))+delta:values_list.indexOf(w.value)+delta,index>=values_list.length&&(index=values_list.length-1),index<0&&(index=0),values.constructor===Array?w.value=values[index]:w.value=index):(text_values=values!=values_list?Object.values(values):values,menu=new LiteGraph.ContextMenu(text_values,{scale:Math.max(1,this.ds.scale),event:event,className:"dark",callback:inner_clicked.bind(w)},ref_window))):event.type==LiteGraph.pointerevents_method+"up"&&"number"==w.type&&(delta=x<40?-1:widget_width-40right.pos[0]+right.size[0])&&(right=node),(null===bottom||y+height>bottom.pos[1]+bottom.size[1])&&(bottom=node),(null===left||x"+(info.label||i)+""+value+"",value:i})}if(entries.length)return new LiteGraph.ContextMenu(entries,{event:e,callback:function(v,options,e,prev){var rect;node&&(rect=this.getBoundingClientRect(),canvas.showEditPropertyValue(node,v.value,{position:[rect.left,rect.top]}))},parentMenu:prev_menu,allow_html:!0,node:node},ref_window),!1}},LGraphCanvas.decodeHTML=function(str){var e=document.createElement("div");return e.innerText=str,e.innerHTML},LGraphCanvas.onMenuResizeNode=function(value,options,e,menu,node){if(node){function fApplyMultiNode(node){node.size=node.computeSize(),node.onResize&&node.onResize(node.size)}var graphcanvas=LGraphCanvas.active_canvas;if(!graphcanvas.selected_nodes||Object.keys(graphcanvas.selected_nodes).length<=1)fApplyMultiNode(node);else for(var i in graphcanvas.selected_nodes)fApplyMultiNode(graphcanvas.selected_nodes[i]);node.setDirtyCanvas(!0,!0)}},LGraphCanvas.prototype.showLinkMenu=function(link,e){var that=this,node_left=that.graph.getNodeById(link.origin_id),node_right=that.graph.getNodeById(link.target_id),fromType=!1,destType=(node_left&&node_left.outputs&&node_left.outputs[link.origin_slot]&&(fromType=node_left.outputs[link.origin_slot].type),!1),menu=(node_right&&node_right.outputs&&node_right.outputs[link.target_slot]&&(destType=node_right.inputs[link.target_slot].type),new LiteGraph.ContextMenu(["Add Node",null,"Delete",null],{event:e,title:null!=link.data?link.data.constructor.name:null,callback:function(v,options,e){switch(v){case"Add Node":LGraphCanvas.onMenuAdd(null,null,e,menu,function(node){node.inputs&&node.inputs.length&&node.outputs&&node.outputs.length&&node_left.connectByType(link.origin_slot,node,fromType)&&(node.connectByType(link.target_slot,node_right,destType),node.pos[0]-=.5*node.size[0])});break;case"Delete":that.graph.removeLink(link.id)}}}));return!1},LGraphCanvas.prototype.createDefaultNodeForSlot=function(optPass){var optPass=optPass||{},opts=Object.assign({nodeFrom:null,slotFrom:null,nodeTo:null,slotTo:null,position:[],nodeType:null,posAdd:[0,0],posSizeFix:[0,0]},optPass),isFrom=opts.nodeFrom&&null!==opts.slotFrom,optPass=!isFrom&&opts.nodeTo&&null!==opts.slotTo;if(isFrom||optPass)if(opts.nodeType){var nodeX=isFrom?opts.nodeFrom:opts.nodeTo,slotX=isFrom?opts.slotFrom:opts.slotTo,iSlotConn=!1;switch(typeof slotX){case"string":iSlotConn=isFrom?nodeX.findOutputSlot(slotX,!1):nodeX.findInputSlot(slotX,!1),slotX=(isFrom?nodeX.outputs:nodeX.inputs)[slotX];break;case"object":iSlotConn=isFrom?nodeX.findOutputSlot(slotX.name):nodeX.findInputSlot(slotX.name);break;case"number":iSlotConn=slotX,slotX=(isFrom?nodeX.outputs:nodeX.inputs)[slotX];break;default:return console.warn("Cant get slot information "+slotX),!1}!1!==slotX&&!1!==iSlotConn||console.warn("createDefaultNodeForSlot bad slotX "+slotX+" "+iSlotConn);var fromSlotType=slotX.type==LiteGraph.EVENT?"_event_":slotX.type,slotTypesDefault=isFrom?LiteGraph.slot_types_default_out:LiteGraph.slot_types_default_in;if(slotTypesDefault&&slotTypesDefault[fromSlotType]){if(slotX.link,nodeNewType=!1,Array.isArray(slotTypesDefault[fromSlotType])||"object"==typeof slotTypesDefault[fromSlotType]){for(var typeX in slotTypesDefault[fromSlotType])if(opts.nodeType==slotTypesDefault[fromSlotType][typeX]||"AUTO"==opts.nodeType){nodeNewType=slotTypesDefault[fromSlotType][typeX];break}}else opts.nodeType!=slotTypesDefault[fromSlotType]&&"AUTO"!=opts.nodeType||(nodeNewType=slotTypesDefault[fromSlotType]);if(nodeNewType){var nodeNewOpts=!1,newNode=("object"==typeof nodeNewType&&nodeNewType.node&&(nodeNewType=(nodeNewOpts=nodeNewType).node),LiteGraph.createNode(nodeNewType));if(newNode){if(nodeNewOpts){if(nodeNewOpts.properties)for(var i in nodeNewOpts.properties)newNode.addProperty(i,nodeNewOpts.properties[i]);if(nodeNewOpts.inputs)for(var i in newNode.inputs=[],nodeNewOpts.inputs)newNode.addOutput(nodeNewOpts.inputs[i][0],nodeNewOpts.inputs[i][1]);if(nodeNewOpts.outputs)for(var i in newNode.outputs=[],nodeNewOpts.outputs)newNode.addOutput(nodeNewOpts.outputs[i][0],nodeNewOpts.outputs[i][1]);nodeNewOpts.title&&(newNode.title=nodeNewOpts.title),nodeNewOpts.json&&newNode.configure(nodeNewOpts.json)}return this.graph.add(newNode),newNode.pos=[opts.position[0]+opts.posAdd[0]+(opts.posSizeFix[0]?opts.posSizeFix[0]*newNode.size[0]:0),opts.position[1]+opts.posAdd[1]+(opts.posSizeFix[1]?opts.posSizeFix[1]*newNode.size[1]:0)],isFrom?opts.nodeFrom.connectByType(iSlotConn,newNode,fromSlotType):opts.nodeTo.connectByTypeOutput(iSlotConn,newNode,fromSlotType),!0}console.log("failed creating "+nodeNewType)}}}else console.warn("No type to createDefaultNodeForSlot");else console.warn("No data passed to createDefaultNodeForSlot "+opts.nodeFrom+" "+opts.slotFrom+" "+opts.nodeTo+" "+opts.slotTo);return!1},LGraphCanvas.prototype.showConnectionMenu=function(optPass){var optPass=optPass||{},opts=Object.assign({nodeFrom:null,slotFrom:null,nodeTo:null,slotTo:null,e:null},optPass),that=this,isFrom=opts.nodeFrom&&opts.slotFrom,optPass=!isFrom&&opts.nodeTo&&opts.slotTo;if(isFrom||optPass){var nodeX=isFrom?opts.nodeFrom:opts.nodeTo,slotX=isFrom?opts.slotFrom:opts.slotTo,iSlotConn=!1;switch(typeof slotX){case"string":iSlotConn=isFrom?nodeX.findOutputSlot(slotX,!1):nodeX.findInputSlot(slotX,!1),slotX=(isFrom?nodeX.outputs:nodeX.inputs)[slotX];break;case"object":iSlotConn=isFrom?nodeX.findOutputSlot(slotX.name):nodeX.findInputSlot(slotX.name);break;case"number":iSlotConn=slotX,slotX=(isFrom?nodeX.outputs:nodeX.inputs)[slotX];break;default:return console.warn("Cant get slot information "+slotX),!1}var options=["Add Node",null],fromSlotType=(that.allow_searchbox&&(options.push("Search"),options.push(null)),slotX.type==LiteGraph.EVENT?"_event_":slotX.type),slotTypesDefault=isFrom?LiteGraph.slot_types_default_out:LiteGraph.slot_types_default_in;if(slotTypesDefault&&slotTypesDefault[fromSlotType])if(Array.isArray(slotTypesDefault[fromSlotType])||"object"==typeof slotTypesDefault[fromSlotType])for(var typeX in slotTypesDefault[fromSlotType])options.push(slotTypesDefault[fromSlotType][typeX]);else options.push(slotTypesDefault[fromSlotType]);var menu=new LiteGraph.ContextMenu(options,{event:opts.e,title:(slotX&&""!=slotX.name?slotX.name+(fromSlotType?" | ":""):"")+(slotX&&fromSlotType?fromSlotType:""),callback:function(v,options,e){switch(v){case"Add Node":LGraphCanvas.onMenuAdd(null,null,e,menu,function(node){isFrom?opts.nodeFrom.connectByType(iSlotConn,node,fromSlotType):opts.nodeTo.connectByTypeOutput(iSlotConn,node,fromSlotType)});break;case"Search":isFrom?that.showSearchBox(e,{node_from:opts.nodeFrom,slot_from:slotX,type_filter_in:fromSlotType}):that.showSearchBox(e,{node_to:opts.nodeTo,slot_from:slotX,type_filter_out:fromSlotType});break;default:that.createDefaultNodeForSlot(Object.assign(opts,{position:[opts.e.canvasX,opts.e.canvasY],nodeType:v}))}}})}else console.warn("No data passed to showConnectionMenu");return!1},LGraphCanvas.onShowPropertyEditor=function(item,options,e,menu,node){var property=item.property||"title",value=node[property],dialog=document.createElement("div");dialog.is_modified=!1,dialog.className="graphdialog",dialog.innerHTML="",dialog.close=function(){dialog.parentNode&&dialog.parentNode.removeChild(dialog)};dialog.querySelector(".name").innerText=property;var input=dialog.querySelector(".value");input&&(input.value=value,input.addEventListener("blur",function(e){this.focus()}),input.addEventListener("keydown",function(e){if(dialog.is_modified=!0,27==e.keyCode)dialog.close();else if(13==e.keyCode)inner();else if(13!=e.keyCode&&"textarea"!=e.target.localName)return;e.preventDefault(),e.stopPropagation()}));var value=LGraphCanvas.active_canvas.canvas,rect=value.getBoundingClientRect(),offsetx=-20,offsety=-20;rect&&(offsetx-=rect.left,offsety-=rect.top),event?(dialog.style.left=event.clientX+offsetx+"px",dialog.style.top=event.clientY+offsety+"px"):(dialog.style.left=.5*value.width+offsetx+"px",dialog.style.top=.5*value.height+offsety+"px");dialog.querySelector("button").addEventListener("click",inner),value.parentNode.appendChild(dialog),input&&input.focus();var dialogCloseTimer=null;function inner(){var value;input&&(value=input.value,"Number"==item.type?value=Number(value):"Boolean"==item.type&&(value=Boolean(value)),node[property]=value,dialog.parentNode&&dialog.parentNode.removeChild(dialog),node.setDirtyCanvas(!0,!0))}dialog.addEventListener("mouseleave",function(e){LiteGraph.dialog_close_on_mouse_leave&&!dialog.is_modified&&LiteGraph.dialog_close_on_mouse_leave&&(dialogCloseTimer=setTimeout(dialog.close,LiteGraph.dialog_close_on_mouse_leave_delay))}),dialog.addEventListener("mouseenter",function(e){LiteGraph.dialog_close_on_mouse_leave&&dialogCloseTimer&&clearTimeout(dialogCloseTimer)})},LGraphCanvas.prototype.prompt=function(title,value,callback,event,multiline){var that=this,dialog=(title=title||"",document.createElement("div"));dialog.is_modified=!1,dialog.className="graphdialog rounded",dialog.innerHTML=multiline?" ":" ",dialog.close=function(){that.prompt_box=null,dialog.parentNode&&dialog.parentNode.removeChild(dialog)};var multiline=LGraphCanvas.active_canvas.canvas,dialogCloseTimer=(multiline.parentNode.appendChild(dialog),1LGraphCanvas.search_limit)break}}var filtered=null;if(Array.prototype.filter)filtered=Object.keys(LiteGraph.registered_node_types).filter(inner_test_filter);else for(var i in filtered=[],LiteGraph.registered_node_types)inner_test_filter(i)&&filtered.push(i);for(i=0;iLGraphCanvas.search_limit));i++);if(options.show_general_after_typefiltered&&(sIn.value||sOut.value)){for(var i in filtered_extra=[],LiteGraph.registered_node_types)inner_test_filter(i,{inTypeOverride:!(!sIn||!sIn.value)&&"*",outTypeOverride:!(!sOut||!sOut.value)&&"*"})&&filtered_extra.push(i);for(i=0;iLGraphCanvas.search_limit));i++);}if((sIn.value||sOut.value)&&0==helper.childNodes.length&&options.show_general_if_none_on_typefilter){for(var i in filtered_extra=[],LiteGraph.registered_node_types)inner_test_filter(i,{skipFilter:!0})&&filtered_extra.push(i);for(i=0;iLGraphCanvas.search_limit));i++);}function inner_test_filter(type,optsIn){var optsIn=optsIn||{},optsIn=Object.assign({skipFilter:!1,inTypeOverride:!1,outTypeOverride:!1},optsIn),ctor=LiteGraph.registered_node_types[type];if(filter&&ctor.filter!=filter)return!1;if((!options.show_all_if_empty||str)&&-1===type.toLowerCase().indexOf(str))return!1;if(options.do_type_filter&&!optsIn.skipFilter){ctor=type,type=sIn.value;if(!1!==optsIn.inTypeOverride&&(type=optsIn.inTypeOverride),sIn&&type&&LiteGraph.registered_slot_in_types[type]&&LiteGraph.registered_slot_in_types[type].nodes){var doesInc=LiteGraph.registered_slot_in_types[type].nodes.includes(ctor);if(!1===doesInc)return!1}type=sOut.value;if(!1!==optsIn.outTypeOverride&&(type=optsIn.outTypeOverride),sOut&&type&&LiteGraph.registered_slot_out_types[type]&&LiteGraph.registered_slot_out_types[type].nodes){doesInc=LiteGraph.registered_slot_out_types[type].nodes.includes(ctor);if(!1===doesInc)return!1}}return!0}}function addResult(type,className){var help=document.createElement("div");first=first||type,help.innerText=type,help.dataset.type=escape(type),help.className="litegraph lite-search-item",className&&(help.className+=" "+className),help.addEventListener("click",function(e){select(unescape(this.dataset.type))}),helper.appendChild(help)}}return dialog.style.left=left+"px",dialog.style.top=top+"px",event.layerY>def_options.height-200&&(helper.style.maxHeight=def_options.height-event.layerY-20+"px"),input.focus(),options.show_all_on_open&&refreshHelper(),dialog},LGraphCanvas.prototype.showEditPropertyValue=function(node,property,options){if(node&&void 0!==node.properties[property]){options=options||{};var info=node.getPropertyInfo(property),type=info.type,input_html="";if("string"==type||"number"==type||"array"==type||"object"==type)input_html="";else if("enum"!=type&&"combo"!=type||!info.values){if("boolean"!=type&&"toggle"!=type)return void console.warn("unknown type: "+type);input_html=""}else{for(var i in input_html=""}var dialog=this.createDialog(""+(info.label||property)+""+input_html+"",options),input=!1;return"enum"!=type&&"combo"!=type||!info.values?"boolean"==type||"toggle"==type?(input=dialog.querySelector("input"))&&input.addEventListener("click",function(e){dialog.modified(),setValue(!!input.checked)}):(input=dialog.querySelector("input"))&&(input.addEventListener("blur",function(e){this.focus()}),v=void 0!==node.properties[property]?node.properties[property]:"","string"!==type&&(v=JSON.stringify(v)),input.value=v,input.addEventListener("keydown",function(e){if(27==e.keyCode)dialog.close();else if(13==e.keyCode)inner();else if(13!=e.keyCode)return void dialog.modified();e.preventDefault(),e.stopPropagation()})):(input=dialog.querySelector("select")).addEventListener("change",function(e){dialog.modified(),setValue(e.target.value)}),input&&input.focus(),dialog.querySelector("button").addEventListener("click",inner),dialog}function inner(){setValue(input.value)}function setValue(value){info&&info.values&&info.values.constructor===Object&&null!=info.values[value]&&(value=info.values[value]),"number"==typeof node.properties[property]&&(value=Number(value)),"array"!=type&&"object"!=type||(value=JSON.parse(value)),node.properties[property]=value,node.graph&&node.graph._version++,node.onPropertyChanged&&node.onPropertyChanged(property,value),options.onclose&&options.onclose(),dialog.close(),node.setDirtyCanvas(!0,!0)}},LGraphCanvas.prototype.createDialog=function(html,options){options=Object.assign({checkForInput:!1,closeOnLeave:!0,closeOnLeave_checkModified:!0},options||{});var dialog=document.createElement("div"),html=(dialog.className="graphdialog",dialog.innerHTML=html,dialog.is_modified=!1,this.canvas.getBoundingClientRect()),offsetx=-20,offsety=-20,dialogCloseTimer=(html&&(offsetx-=html.left,offsety-=html.top),options.position?(offsetx+=options.position[0],offsety+=options.position[1]):options.event?(offsetx+=options.event.clientX,offsety+=options.event.clientY):(offsetx+=.5*this.canvas.width,offsety+=.5*this.canvas.height),dialog.style.left=offsetx+"px",dialog.style.top=offsety+"px",this.canvas.parentNode.appendChild(dialog),options.checkForInput&&(html=[],html=dialog.querySelectorAll("input"))&&html.forEach(function(iX){iX.addEventListener("keydown",function(e){if(dialog.modified(),27==e.keyCode)dialog.close();else if(13!=e.keyCode)return;e.preventDefault(),e.stopPropagation()}),iX.focus()}),dialog.modified=function(){dialog.is_modified=!0},dialog.close=function(){dialog.parentNode&&dialog.parentNode.removeChild(dialog)},null),prevent_timeout=!1,offsetx=(dialog.addEventListener("mouseleave",function(e){prevent_timeout||(options.closeOnLeave||LiteGraph.dialog_close_on_mouse_leave)&&!dialog.is_modified&&LiteGraph.dialog_close_on_mouse_leave&&(dialogCloseTimer=setTimeout(dialog.close,LiteGraph.dialog_close_on_mouse_leave_delay))}),dialog.addEventListener("mouseenter",function(e){(options.closeOnLeave||LiteGraph.dialog_close_on_mouse_leave)&&dialogCloseTimer&&clearTimeout(dialogCloseTimer)}),dialog.querySelectorAll("select"));return offsetx&&offsetx.forEach(function(selIn){selIn.addEventListener("click",function(e){prevent_timeout++}),selIn.addEventListener("blur",function(e){prevent_timeout=0}),selIn.addEventListener("change",function(e){prevent_timeout=-1})}),dialog},LGraphCanvas.prototype.createPanel=function(title,options){var ref_window=(options=options||{}).window||window,root=document.createElement("div");return root.className="litegraph dialog",root.innerHTML="
",root.header=root.querySelector(".dialog-header"),options.width&&(root.style.width=options.width+(options.width.constructor===Number?"px":"")),options.height&&(root.style.height=options.height+(options.height.constructor===Number?"px":"")),options.closable&&((options=document.createElement("span")).innerHTML="✕",options.classList.add("close"),options.addEventListener("click",function(){root.close()}),root.header.appendChild(options)),root.title_element=root.querySelector(".dialog-title"),root.title_element.innerText=title,root.content=root.querySelector(".dialog-content"),root.alt_content=root.querySelector(".dialog-alt-content"),root.footer=root.querySelector(".dialog-footer"),root.close=function(){root.onClose&&"function"==typeof root.onClose&&root.onClose(),root.parentNode&&root.parentNode.removeChild(root),this.parentNode&&this.parentNode.removeChild(this)},root.toggleAltContent=function(force){var vTo;force=void 0!==force?(vTo=force?"block":"none",force?"none":"block"):(vTo="block"!=root.alt_content.style.display?"block":"none","block"!=root.alt_content.style.display?"none":"block"),root.alt_content.style.display=vTo,root.content.style.display=force},root.toggleFooterVisibility=function(force){force=void 0!==force?force?"block":"none":"block"!=root.footer.style.display?"block":"none",root.footer.style.display=force},root.clear=function(){this.content.innerHTML=""},root.addHTML=function(code,classname,on_footer){var elem=document.createElement("div");return classname&&(elem.className=classname),elem.innerHTML=code,(on_footer?root.footer:root.content).appendChild(elem),elem},root.addButton=function(name,callback,options){var elem=document.createElement("button");return elem.innerText=name,elem.options=options,elem.classList.add("btn"),elem.addEventListener("click",callback),root.footer.appendChild(elem),elem},root.addSeparator=function(){var elem=document.createElement("div");elem.className="separator",root.content.appendChild(elem)},root.addWidget=function(type,name,value,options,callback){options=options||{};var str_value=String(value),elem=("number"==(type=type.toLowerCase())&&(str_value=value.toFixed(3)),document.createElement("div")),value_element=(elem.className="property",elem.innerHTML="",elem.querySelector(".property_name").innerText=options.label||name,elem.querySelector(".property_value"));function innerChange(name,value){options.callback&&options.callback(name,value,options),callback&&callback(name,value,options)}return value_element.innerText=str_value,elem.dataset.property=name,elem.dataset.type=options.type||type,elem.options=options,elem.value=value,"code"==type?elem.addEventListener("click",function(e){root.inner_showCodePad(this.dataset.property)}):"boolean"==type?(elem.classList.add("boolean"),value&&elem.classList.add("bool-on"),elem.addEventListener("click",function(){var propname=this.dataset.property;this.value=!this.value,this.classList.toggle("bool-on"),this.querySelector(".property_value").innerText=this.value?"true":"false",innerChange(propname,this.value)})):"string"==type||"number"==type?(value_element.setAttribute("contenteditable",!0),value_element.addEventListener("keydown",function(e){"Enter"!=e.code||"string"==type&&e.shiftKey||(e.preventDefault(),this.blur())}),value_element.addEventListener("blur",function(){var v=this.innerText;innerChange(this.parentNode.dataset.property,v="number"==this.parentNode.dataset.type?Number(v):v)})):"enum"!=type&&"combo"!=type||(str_value=LGraphCanvas.getPropertyPrintableValue(value,options.values),value_element.innerText=str_value,value_element.addEventListener("click",function(event){var values=options.values||[],propname=this.parentNode.dataset.property,elem_that=this;new LiteGraph.ContextMenu(values,{event:event,className:"dark",callback:function(v,option,event){return elem_that.innerText=v,innerChange(propname,v),!1}},ref_window)})),root.content.appendChild(elem),elem},root.onOpen&&"function"==typeof root.onOpen&&root.onOpen(),root},LGraphCanvas.getPropertyPrintableValue=function(value,values){if(!values)return String(value);if(values.constructor===Array)return String(value);if(values.constructor===Object){var k,desc_value="";for(k in values)if(values[k]==value){desc_value=k;break}return String(value)+" ("+desc_value+")"}},LGraphCanvas.prototype.closePanels=function(){var panel=document.querySelector("#node-panel");panel&&panel.close(),(panel=document.querySelector("#option-panel"))&&panel.close()},LGraphCanvas.prototype.showShowGraphOptionsPanel=function(refOpts,obEv,refMenu,refMenu2){if(this.constructor&&"HTMLDivElement"==this.constructor.name){if(!(obEv&&obEv.event&&obEv.event.target&&obEv.event.target.lgraphcanvas))return void console.warn("Canvas not found");var graphcanvas=obEv.event.target.lgraphcanvas}else graphcanvas=this;graphcanvas.closePanels();obEv=graphcanvas.getCanvasWindow();function fUpdate(name,value,options){options&&options.key&&(name=options.key),options.values&&(value=Object.values(options.values).indexOf(value)),graphcanvas[name]=value}panel=graphcanvas.createPanel("Options",{closable:!0,window:obEv,onOpen:function(){graphcanvas.OPTIONPANEL_IS_OPEN=!0},onClose:function(){graphcanvas.OPTIONPANEL_IS_OPEN=!1,graphcanvas.options_panel=null}}),(graphcanvas.options_panel=panel).id="option-panel",panel.classList.add("settings"),panel.content.innerHTML="";var pI,aProps=LiteGraph.availableCanvasOptions;for(pI in aProps.sort(),aProps){var pX=aProps[pI];panel.addWidget("boolean",pX,graphcanvas[pX],{key:pX,on:"True",off:"False"},fUpdate)}graphcanvas.links_render_mode,panel.addWidget("combo","Render mode",LiteGraph.LINK_RENDER_MODES[graphcanvas.links_render_mode],{key:"links_render_mode",values:LiteGraph.LINK_RENDER_MODES},fUpdate),panel.addSeparator(),panel.footer.innerHTML="",graphcanvas.canvas.parentNode.appendChild(panel)},LGraphCanvas.prototype.showShowNodePanel=function(node){this.SELECTED_NODE=node,this.closePanels();var ref_window=this.getCanvasWindow(),graphcanvas=this,panel=this.createPanel(node.title||"",{closable:!0,window:ref_window,onOpen:function(){graphcanvas.NODEPANEL_IS_OPEN=!0},onClose:function(){graphcanvas.NODEPANEL_IS_OPEN=!1,graphcanvas.node_panel=null}});function inner_refresh(){panel.content.innerHTML="",panel.addHTML(""+node.type+""+(node.constructor.desc||"")+""),panel.addHTML("

Properties

");function fUpdate(name,value){switch(graphcanvas.graph.beforeChange(node),name){case"Title":node.title=value;break;case"Mode":var kV=Object.values(LiteGraph.NODE_MODES).indexOf(value);0<=kV&&LiteGraph.NODE_MODES[kV]?node.changeMode(kV):console.warn("unexpected mode: "+value);break;case"Color":LGraphCanvas.node_colors[value]?(node.color=LGraphCanvas.node_colors[value].color,node.bgcolor=LGraphCanvas.node_colors[value].bgcolor):console.warn("unexpected color: "+value);break;default:node.setProperty(name,value)}graphcanvas.graph.afterChange(),graphcanvas.dirty_canvas=!0}panel.addWidget("string","Title",node.title,{},fUpdate),panel.addWidget("combo","Mode",LiteGraph.NODE_MODES[node.mode],{values:LiteGraph.NODE_MODES},fUpdate);var pName,nodeCol="";for(pName in void 0!==node.color&&(nodeCol=Object.keys(LGraphCanvas.node_colors).filter(function(nK){return LGraphCanvas.node_colors[nK].color==node.color})),panel.addWidget("combo","Color",nodeCol,{values:Object.keys(LGraphCanvas.node_colors)},fUpdate),node.properties){var value=node.properties[pName],info=node.getPropertyInfo(pName);info.type;node.onAddPropertyToPanel&&node.onAddPropertyToPanel(pName,panel)||panel.addWidget(info.widget||info.type,pName,value,info,fUpdate)}panel.addSeparator(),node.onShowCustomPanelInfo&&node.onShowCustomPanelInfo(panel),panel.footer.innerHTML="",panel.addButton("Delete",function(){node.block_delete||(node.graph.remove(node),panel.close())}).classList.add("delete")}(graphcanvas.node_panel=panel).id="node-panel",panel.node=node,panel.classList.add("settings"),panel.inner_showCodePad=function(propname){panel.classList.remove("settings"),panel.classList.add("centered"),panel.alt_content.innerHTML="";function fDoneWith(){panel.toggleAltContent(!1),panel.toggleFooterVisibility(!0),textarea.parentNode.removeChild(textarea),panel.classList.add("settings"),panel.classList.remove("centered"),inner_refresh()}var textarea=panel.alt_content.querySelector("textarea"),assign=(textarea.value=node.properties[propname],textarea.addEventListener("keydown",function(e){"Enter"==e.code&&e.ctrlKey&&(node.setProperty(propname,textarea.value),fDoneWith())}),panel.toggleAltContent(!0),panel.toggleFooterVisibility(!1),textarea.style.height="calc(100% - 40px)",panel.addButton("Assign",function(){node.setProperty(propname,textarea.value),fDoneWith()})),assign=(panel.alt_content.appendChild(assign),panel.addButton("Close",fDoneWith));assign.style.float="right",panel.alt_content.appendChild(assign)},inner_refresh(),this.canvas.parentNode.appendChild(panel)},LGraphCanvas.prototype.showSubgraphPropertiesDialog=function(node){console.log("showing subgraph properties dialog");var old_panel=this.canvas.parentNode.querySelector(".subgraph_dialog"),panel=(old_panel&&old_panel.close(),this.createPanel("Subgraph Inputs",{closable:!0,width:500}));function inner_refresh(){if(panel.clear(),node.inputs)for(var i=0;i","subgraph_property")).dataset.name=input.name,elem.dataset.slot=i,elem.querySelector(".name").innerText=input.name,elem.querySelector(".type").innerText=input.type,elem.querySelector("button").addEventListener("click",function(e){node.removeInput(Number(this.parentNode.dataset.slot)),inner_refresh()}))}}panel.node=node,panel.classList.add("subgraph_dialog");return panel.addHTML(" + NameType","subgraph_property extra",!0).querySelector("button").addEventListener("click",function(e){var elem=this.parentNode,name=elem.querySelector(".name").value,type=elem.querySelector(".type").value;name&&-1==node.findInputSlot(name)&&(node.addInput(name,type),elem.querySelector(".name").value="",elem.querySelector(".type").value="",inner_refresh())}),inner_refresh(),this.canvas.parentNode.appendChild(panel),panel},LGraphCanvas.prototype.showSubgraphPropertiesDialogRight=function(node){var old_panel=this.canvas.parentNode.querySelector(".subgraph_dialog"),panel=(old_panel&&old_panel.close(),this.createPanel("Subgraph Outputs",{closable:!0,width:500}));function inner_refresh(){if(panel.clear(),node.outputs)for(var i=0;i","subgraph_property")).dataset.name=input.name,elem.dataset.slot=i,elem.querySelector(".name").innerText=input.name,elem.querySelector(".type").innerText=input.type,elem.querySelector("button").addEventListener("click",function(e){node.removeOutput(Number(this.parentNode.dataset.slot)),inner_refresh()}))}}panel.node=node,panel.classList.add("subgraph_dialog");old_panel=panel.addHTML(" + NameType","subgraph_property extra",!0);function addOutput(){var elem=this.parentNode,name=elem.querySelector(".name").value,type=elem.querySelector(".type").value;name&&-1==node.findOutputSlot(name)&&(node.addOutput(name,type),elem.querySelector(".name").value="",elem.querySelector(".type").value="",inner_refresh())}return old_panel.querySelector(".name").addEventListener("keydown",function(e){13==e.keyCode&&addOutput.apply(this)}),old_panel.querySelector("button").addEventListener("click",function(e){addOutput.apply(this)}),inner_refresh(),this.canvas.parentNode.appendChild(panel),panel},LGraphCanvas.prototype.checkPanels=function(){if(this.canvas)for(var panels=this.canvas.parentNode.querySelectorAll(".litegraph.dialog"),i=0;iNo color"}),LGraphCanvas.node_colors){var color=LGraphCanvas.node_colors[i],value={value:i,content:""+i+""};values.push(value)}return new LiteGraph.ContextMenu(values,{event:e,callback:function(v){if(node){function fApplyColor(node){color?node.constructor===LiteGraph.LGraphGroup?node.color=color.groupcolor:(node.color=color.color,node.bgcolor=color.bgcolor):(delete node.color,delete node.bgcolor)}var color=v.value?LGraphCanvas.node_colors[v.value]:null,graphcanvas=LGraphCanvas.active_canvas;if(!graphcanvas.selected_nodes||Object.keys(graphcanvas.selected_nodes).length<=1)fApplyColor(node);else for(var i in graphcanvas.selected_nodes)fApplyColor(graphcanvas.selected_nodes[i]);node.setDirtyCanvas(!0,!0)}},parentMenu:menu,node:node}),!1},LGraphCanvas.onMenuNodeShapes=function(value,options,e,menu,node){if(node)return new LiteGraph.ContextMenu(LiteGraph.VALID_SHAPES,{event:e,callback:function(v){if(node){node.graph.beforeChange();function fApplyMultiNode(node){node.shape=v}var graphcanvas=LGraphCanvas.active_canvas;if(!graphcanvas.selected_nodes||Object.keys(graphcanvas.selected_nodes).length<=1)fApplyMultiNode(node);else for(var i in graphcanvas.selected_nodes)fApplyMultiNode(graphcanvas.selected_nodes[i]);node.graph.afterChange(),node.setDirtyCanvas(!0)}},parentMenu:menu,node:node}),!1;throw"no node passed"},LGraphCanvas.onMenuNodeRemove=function(value,options,e,menu,node){if(!node)throw"no node passed";function fApplyMultiNode(node){!1!==node.removable&&graph.remove(node)}var graph=node.graph,graphcanvas=(graph.beforeChange(),LGraphCanvas.active_canvas);if(!graphcanvas.selected_nodes||Object.keys(graphcanvas.selected_nodes).length<=1)fApplyMultiNode(node);else for(var i in graphcanvas.selected_nodes)fApplyMultiNode(graphcanvas.selected_nodes[i]);graph.afterChange(),node.setDirtyCanvas(!0,!0)},LGraphCanvas.onMenuNodeToSubgraph=function(value,options,e,menu,node){var nodes_list,subgraph_node,graph=node.graph,graphcanvas=LGraphCanvas.active_canvas;graphcanvas&&((nodes_list=Object.values(graphcanvas.selected_nodes||{})).length||(nodes_list=[node]),(subgraph_node=LiteGraph.createNode("graph/subgraph")).pos=node.pos.concat(),graph.add(subgraph_node),subgraph_node.buildFromNodes(nodes_list),graphcanvas.deselectAllNodes(),node.setDirtyCanvas(!0,!0))},LGraphCanvas.onMenuNodeClone=function(value,options,e,menu,node){node.graph.beforeChange();function fApplyMultiNode(node){var newnode;!1!==node.clonable&&(newnode=node.clone())&&(newnode.pos=[node.pos[0]+5,node.pos[1]+5],node.graph.add(newnode),newSelected[newnode.id]=newnode)}var newSelected={},graphcanvas=LGraphCanvas.active_canvas;if(!graphcanvas.selected_nodes||Object.keys(graphcanvas.selected_nodes).length<=1)fApplyMultiNode(node);else for(var i in graphcanvas.selected_nodes)fApplyMultiNode(graphcanvas.selected_nodes[i]);Object.keys(newSelected).length&&graphcanvas.selectNodes(newSelected),node.graph.afterChange(),node.setDirtyCanvas(!0,!0)},LGraphCanvas.node_colors={red:{color:"#322",bgcolor:"#533",groupcolor:"#A88"},brown:{color:"#332922",bgcolor:"#593930",groupcolor:"#b06634"},green:{color:"#232",bgcolor:"#353",groupcolor:"#8A8"},blue:{color:"#223",bgcolor:"#335",groupcolor:"#88A"},pale_blue:{color:"#2a363b",bgcolor:"#3f5159",groupcolor:"#3f789e"},cyan:{color:"#233",bgcolor:"#355",groupcolor:"#8AA"},purple:{color:"#323",bgcolor:"#535",groupcolor:"#a1309b"},yellow:{color:"#432",bgcolor:"#653",groupcolor:"#b58b2a"},black:{color:"#222",bgcolor:"#000",groupcolor:"#444"}},LGraphCanvas.prototype.getCanvasMenuOptions=function(){var extra,options=null;return this.getMenuOptions?options=this.getMenuOptions():(options=[{content:"Add Node",has_submenu:!0,callback:LGraphCanvas.onMenuAdd},{content:"Add Group",callback:LGraphCanvas.onGroupAdd}],1Name",options),(input=dialog.querySelector("input"))&&slot_info&&(input.value=slot_info.label||""),inner=function(){node.graph.beforeChange(),input.value&&(slot_info&&(slot_info.label=input.value),that.setDirty(!0)),dialog.close(),node.graph.afterChange()},dialog.querySelector("button").addEventListener("click",inner),input.addEventListener("keydown",function(e){if(dialog.is_modified=!0,27==e.keyCode)dialog.close();else if(13==e.keyCode)inner();else if(13!=e.keyCode&&"textarea"!=e.target.localName)return;e.preventDefault(),e.stopPropagation()}),input.focus()))},extra:node},slot=(node&&(options.title=node.type),null);node&&(slot=node.getSlotInPosition(event.canvasX,event.canvasY),LGraphCanvas.active_node=node),slot?(menu_info=[],node.getSlotMenuOptions?menu_info=node.getSlotMenuOptions(slot):(slot&&slot.output&&slot.output.links&&slot.output.links.length&&menu_info.push({content:"Disconnect Links",slot:slot}),(_slot=slot.input||slot.output).removable&&menu_info.push(_slot.locked?"Cannot remove":{content:"Remove Slot",slot:slot}),_slot.nameLocked||menu_info.push({content:"Rename Slot",slot:slot})),options.title=(slot.input||slot.output).type||"*",slot.input&&slot.input.type==LiteGraph.ACTION&&(options.title="Action"),slot.output&&slot.output.type==LiteGraph.EVENT&&(options.title="Event")):node?menu_info=this.getNodeMenuOptions(node):(menu_info=this.getCanvasMenuOptions(),(_slot=this.graph.getGroupOnPos(event.canvasX,event.canvasY))&&menu_info.push(null,{content:"Edit Group",has_submenu:!0,submenu:{title:"Group",extra:_slot,options:this.getGroupMenuOptions(_slot)}})),menu_info&&new LiteGraph.ContextMenu(menu_info,options,ref_window)},LiteGraph.compareObjects=compareObjects,LiteGraph.distance=distance,LiteGraph.colorToString=colorToString,LiteGraph.isInsideRectangle=isInsideRectangle,LiteGraph.growBounding=growBounding,LiteGraph.isInsideBounding=isInsideBounding,LiteGraph.overlapBounding=overlapBounding,LiteGraph.hex2num=hex2num,LiteGraph.num2hex=num2hex,ContextMenu.prototype.addItem=function(name,value,options){var that=this,element=(options=options||{},document.createElement("div")),disabled=!(element.className="litemenu-entry submenu");function inner_onclick(e){var value=this.value,close_parent=!0;if((that.current_submenu&&that.current_submenu.close(e),options.callback&&!0===options.callback.call(this,value,options,e,that,options.node)&&(close_parent=!1),value)&&(value.callback&&!options.ignore_item_callbacks&&!0!==value.disabled&&!0===value.callback.call(this,value,options,e,that,options.extra)&&(close_parent=!1),value.submenu)){if(!value.submenu.options)throw"ContextMenu submenu needs options";new that.constructor(value.submenu.options,{callback:value.submenu.callback,event:e,parentMenu:that,ignore_item_callbacks:value.submenu.ignore_item_callbacks,title:value.submenu.title,extra:value.submenu.extra,autoopen:options.autoopen});close_parent=!1}close_parent&&!that.lock&&that.close()}return null===value?element.classList.add("separator"):(element.innerHTML=value&&value.title?value.title:name,(element.value=value)&&(value.disabled&&(disabled=!0,element.classList.add("disabled")),value.submenu||value.has_submenu)&&element.classList.add("has_submenu"),"function"==typeof value?(element.dataset.value=name,element.onclick_callback=value):element.dataset.value=value,value.className&&(element.className+=" "+value.className)),this.root.appendChild(element),disabled||element.addEventListener("click",inner_onclick),!disabled&&options.autoopen&&LiteGraph.pointerListenerAdd(element,"enter",function(e){var value=this.value;value&&value.has_submenu&&inner_onclick.call(this,e)}),element},ContextMenu.prototype.close=function(e,ignore_parent_menu){this.root.parentNode&&this.root.parentNode.removeChild(this.root),this.parentMenu&&!ignore_parent_menu&&(this.parentMenu.lock=!1,this.parentMenu.current_submenu=null,void 0===e?this.parentMenu.close():e&&!ContextMenu.isCursorOverElement(e,this.parentMenu.root)&&ContextMenu.trigger(this.parentMenu.root,LiteGraph.pointerevents_method+"leave",e)),this.current_submenu&&this.current_submenu.close(e,!0),this.root.closing_timer&&clearTimeout(this.root.closing_timer)},ContextMenu.trigger=function(element,event_name,params,origin){var evt=document.createEvent("CustomEvent");return evt.initCustomEvent(event_name,!0,!0,params),evt.srcElement=origin,element.dispatchEvent?element.dispatchEvent(evt):element.__events&&element.__events.dispatchEvent(evt),evt},ContextMenu.prototype.getTopMenu=function(){return this.options.parentMenu?this.options.parentMenu.getTopMenu():this},ContextMenu.prototype.getFirstEvent=function(){return this.options.parentMenu?this.options.parentMenu.getFirstEvent():this.options.event},ContextMenu.isCursorOverElement=function(event,element){var left=event.clientX,event=event.clientY,element=element.getBoundingClientRect();return!!element&&event>element.top&&eventelement.left&&leftthis.size[0]+10||localpos[1]<-10||localpos[1]>this.size[1]+10)?(points.splice(s,1),this.selected=-1):(curvepos[0]=graphcanvas?0==s?0:1:clamp(x,0,1),curvepos[1]=1-clamp(y,0,1),points.sort(function(a,b){return a[0]-b[0]}),this.selected=points.indexOf(curvepos),this.must_update=!0)))},CurveEditor.prototype.onMouseUp=function(localpos,graphcanvas){return!(this.selected=-1)},CurveEditor.prototype.getCloserPoint=function(pos,max_dist){var points=this.points;if(!points)return-1;max_dist=max_dist||30;for(var w=this.size[0]-2*this.margin,h=this.size[1]-2*this.margin,num=points.length,p2=[0,0],min_dist=1e6,closest=-1,i=0;i(a^Math.random()*16>>a/4).toString(16)); + return ([1e7]+-1e3+-4e3+-8e3+-1e11).replace(/[018]/g,(a) => (a^Math.random()*16>>a/4).toString(16)); }, /** @@ -684,12 +674,12 @@ * @return {Boolean} true if they can be connected */ isValidConnection: function(type_a, type_b) { - if (type_a=="" || type_a==="*") type_a = 0; - if (type_b=="" || type_b==="*") type_b = 0; + if (type_a=="" || type_a==="*") type_a = 0; + if (type_b=="" || type_b==="*") type_b = 0; if ( - !type_a //generic output + !type_a // generic output || !type_b // generic input - || type_a == type_b //same type (is valid for triggers) + || type_a == type_b // same type (is valid for triggers) || (type_a == LiteGraph.EVENT && type_b == LiteGraph.ACTION) ) { return true; @@ -711,8 +701,8 @@ var supported_types_b = type_b.split(","); for (var i = 0; i < supported_types_a.length; ++i) { for (var j = 0; j < supported_types_b.length; ++j) { - if(this.isValidConnection(supported_types_a[i],supported_types_b[j])){ - //if (supported_types_a[i] == supported_types_b[j]) { + if(this.isValidConnection(supported_types_a[i],supported_types_b[j])) { + // if (supported_types_a[i] == supported_types_b[j]) { return true; } } @@ -733,7 +723,7 @@ this.searchbox_extras[description.toLowerCase()] = { type: node_type, desc: description, - data: data + data: data, }; }, @@ -744,65 +734,61 @@ * @param {String} type an string to know how to fetch it: "text","arraybuffer","json","blob" * @param {Function} on_complete callback(data) * @param {Function} on_error in case of an error - * @return {FileReader|Promise} returns the object used to + * @return {FileReader|Promise} returns the object used to */ - fetchFile: function( url, type, on_complete, on_error ) { - var that = this; - if(!url) - return null; + fetchFile: function( url, type, on_complete, on_error ) { + var that = this; + if(!url) + return null; - type = type || "text"; - if( url.constructor === String ) - { - if (url.substr(0, 4) == "http" && LiteGraph.proxy) { - url = LiteGraph.proxy + url.substr(url.indexOf(":") + 3); - } - return fetch(url) - .then(function(response) { - if(!response.ok) - throw new Error("File not found"); //it will be catch below - if(type == "arraybuffer") - return response.arrayBuffer(); - else if(type == "text" || type == "string") - return response.text(); - else if(type == "json") - return response.json(); - else if(type == "blob") - return response.blob(); - }) - .then(function(data) { - if(on_complete) - on_complete(data); - }) - .catch(function(error) { - console.error("error fetching file:",url); - if(on_error) - on_error(error); - }); - } - else if( url.constructor === File || url.constructor === Blob) - { - var reader = new FileReader(); - reader.onload = function(e) - { - var v = e.target.result; - if( type == "json" ) - v = JSON.parse(v); - if(on_complete) - on_complete(v); - } - if(type == "arraybuffer") - return reader.readAsArrayBuffer(url); - else if(type == "text" || type == "json") - return reader.readAsText(url); - else if(type == "blob") - return reader.readAsBinaryString(url); - } - return null; - } + type = type || "text"; + if( url.constructor === String ) { + if (url.substr(0, 4) == "http" && LiteGraph.proxy) { + url = LiteGraph.proxy + url.substr(url.indexOf(":") + 3); + } + return fetch(url) + .then(function(response) { + if(!response.ok) + throw new Error("File not found"); // it will be catch below + if(type == "arraybuffer") + return response.arrayBuffer(); + else if(type == "text" || type == "string") + return response.text(); + else if(type == "json") + return response.json(); + else if(type == "blob") + return response.blob(); + }) + .then(function(data) { + if(on_complete) + on_complete(data); + }) + .catch(function(error) { + console.error("error fetching file:",url); + if(on_error) + on_error(error); + }); + } else if( url.constructor === File || url.constructor === Blob) { + var reader = new FileReader(); + reader.onload = function(e) { + var v = e.target.result; + if( type == "json" ) + v = JSON.parse(v); + if(on_complete) + on_complete(v); + } + if(type == "arraybuffer") + return reader.readAsArrayBuffer(url); + else if(type == "text" || type == "json") + return reader.readAsText(url); + else if(type == "blob") + return reader.readAsBinaryString(url); + } + return null; + }, }); - //timer that works everywhere + // timer that works everywhere if (typeof performance != "undefined") { LiteGraph.getTime = performance.now.bind(performance); } else if (typeof Date != "undefined" && Date.now) { @@ -818,9 +804,9 @@ }; } - //********************************************************************************* + //* ******************************************************************************** // LGraph CLASS - //********************************************************************************* + //* ******************************************************************************** /** * LGraph is the class that contain a full graph. We instantiate one and add nodes to it, and then we can run the execution loop. @@ -848,10 +834,10 @@ global.LGraph = LiteGraph.LGraph = LGraph; - //default supported types + // 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) + // 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; }; @@ -871,9 +857,9 @@ this.last_node_id = 0; this.last_link_id = 0; - this._version = -1; //used to detect changes + this._version = -1; // used to detect changes - //safe clear + // safe clear if (this._nodes) { for (var i = 0; i < this._nodes.length; ++i) { var node = this._nodes[i]; @@ -883,27 +869,27 @@ } } - //nodes + // nodes this._nodes = []; this._nodes_by_id = {}; - this._nodes_in_order = []; //nodes sorted in execution order - this._nodes_executable = null; //nodes that contain onExecute sorted in execution order + this._nodes_in_order = []; // nodes sorted in execution order + this._nodes_executable = null; // nodes that contain onExecute sorted in execution order - //other scene stuff + // other scene stuff this._groups = []; - //links - this.links = {}; //container with all the links + // links + this.links = {}; // container with all the links - //iterations + // iterations this.iteration = 0; - //custom data + // custom data this.config = {}; - this.vars = {}; - this.extra = {}; //to store custom data + this.vars = {}; + this.extra = {}; // to store custom data - //timing + // timing this.globaltime = 0; this.runningtime = 0; this.fixedtime = 0; @@ -917,12 +903,12 @@ this.nodes_executing = []; this.nodes_actioning = []; this.nodes_executedAction = []; - - //subgraph_data + + // subgraph_data this.inputs = {}; this.outputs = {}; - //notify canvas to redraw + // notify canvas to redraw this.change(); this.sendActionToCanvas("clear"); @@ -986,35 +972,35 @@ this.sendEventToAllNodes("onStart"); - //launch + // launch this.starttime = LiteGraph.getTime(); this.last_update_time = this.starttime; interval = interval || 0; var that = this; - //execute once per frame + // execute once per frame if ( interval == 0 && typeof window != "undefined" && window.requestAnimationFrame ) { function on_frame() { if (that.execution_timer_id != -1) { return; } window.requestAnimationFrame(on_frame); - if(that.onBeforeStep) - that.onBeforeStep(); + if(that.onBeforeStep) + that.onBeforeStep(); that.runStep(1, !that.catch_errors); - if(that.onAfterStep) - that.onAfterStep(); + if(that.onAfterStep) + that.onAfterStep(); } this.execution_timer_id = -1; on_frame(); - } else { //execute every 'interval' ms + } else { // execute every 'interval' ms this.execution_timer_id = setInterval(function() { - //execute - if(that.onBeforeStep) - that.onBeforeStep(); + // execute + if(that.onBeforeStep) + that.onBeforeStep(); that.runStep(1, !that.catch_errors); - if(that.onAfterStep) - that.onAfterStep(); + if(that.onAfterStep) + that.onAfterStep(); }, interval); } }; @@ -1049,7 +1035,7 @@ * Run N steps (cycles) of the graph * @method runStep * @param {number} num number of steps to run, default is 1 - * @param {Boolean} do_not_catch_errors [optional] if you want to try/catch errors + * @param {Boolean} do_not_catch_errors [optional] if you want to try/catch errors * @param {number} limit max number of nodes to execute (used to execute from start to a node) */ @@ -1059,10 +1045,10 @@ var start = LiteGraph.getTime(); this.globaltime = 0.001 * (start - this.starttime); - //not optimal: executes possible pending actions in node, problem is it is not optimized - //it is done here as if it was done in the later loop it wont be called in the node missed the onExecute - - //from now on it will iterate only on executable nodes which is faster + // not optimal: executes possible pending actions in node, problem is it is not optimized + // it is done here as if it was done in the later loop it wont be called in the node missed the onExecute + + // from now on it will iterate only on executable nodes which is faster var nodes = this._nodes_executable ? this._nodes_executable : this._nodes; @@ -1070,18 +1056,18 @@ return; } - limit = limit || nodes.length; + limit = limit || nodes.length; if (do_not_catch_errors) { - //iterations + // iterations for (var i = 0; i < num; i++) { for (var j = 0; j < limit; ++j) { var node = nodes[j]; if(LiteGraph.use_deferred_actions && node._waiting_actions && node._waiting_actions.length) node.executePendingActions(); if (node.mode == LiteGraph.ALWAYS && node.onExecute) { - //wrap node.onExecute(); - node.doExecute(); + // wrap node.onExecute(); + node.doExecute(); } } @@ -1094,9 +1080,9 @@ if (this.onAfterExecute) { this.onAfterExecute(); } - } else { //catch errors + } else { // catch errors try { - //iterations + // iterations for (var i = 0; i < num; i++) { for (var j = 0; j < limit; ++j) { var node = nodes[j]; @@ -1159,27 +1145,27 @@ } }; - //This is more internal, it computes the executable nodes in order and returns it + // This is more internal, it computes the executable nodes in order and returns it LGraph.prototype.computeExecutionOrder = function( only_onExecute, - set_level + set_level, ) { var L = []; var S = []; var M = {}; - var visited_links = {}; //to avoid repeating links - var remaining_links = {}; //to a + var visited_links = {}; // to avoid repeating links + var remaining_links = {}; // to a - //search for the nodes without inputs (starting nodes) + // search for the nodes without inputs (starting nodes) for (var i = 0, l = this._nodes.length; i < l; ++i) { var node = this._nodes[i]; if (only_onExecute && !node.onExecute) { continue; } - M[node.id] = node; //add to pending nodes + M[node.id] = node; // add to pending nodes - var num = 0; //num of input connections + var num = 0; // num of input connections if (node.inputs) { for (var j = 0, l2 = node.inputs.length; j < l2; j++) { if (node.inputs[j] && node.inputs[j].link != null) { @@ -1189,13 +1175,12 @@ } if (num == 0) { - //is a starting node + // is a starting node S.push(node); if (set_level) { node._level = 1; } - } //num of input links - else { + } else { // num of input links if (set_level) { node._level = 0; } @@ -1203,24 +1188,21 @@ } } - while (true) { - if (S.length == 0) { - break; - } + while (S.length != 0) { - //get an starting node + // get an starting node var node = S.shift(); - L.push(node); //add to ordered list - delete M[node.id]; //remove from the pending nodes + L.push(node); // add to ordered list + delete M[node.id]; // remove from the pending nodes if (!node.outputs) { continue; } - //for every output + // for every output for (var i = 0; i < node.outputs.length; i++) { var output = node.outputs[i]; - //not connected + // not connected if ( output == null || output.links == null || @@ -1229,7 +1211,7 @@ continue; } - //for every connection + // for every connection for (var j = 0; j < output.links.length; j++) { var link_id = output.links[j]; var link = this.links[link_id]; @@ -1237,7 +1219,7 @@ continue; } - //already visited link (ignore it) + // already visited link (ignore it) if (visited_links[link.id]) { continue; } @@ -1256,16 +1238,16 @@ target_node._level = node._level + 1; } - visited_links[link.id] = true; //mark as visited - remaining_links[target_node.id] -= 1; //reduce the number of links remaining + visited_links[link.id] = true; // mark as visited + remaining_links[target_node.id] -= 1; // reduce the number of links remaining if (remaining_links[target_node.id] == 0) { S.push(target_node); - } //if no more links, then add to starters array + } // if no more links, then add to starters array } } } - //the remaining ones (loops) + // the remaining ones (loops) for (var i in M) { L.push(M[i]); } @@ -1276,23 +1258,23 @@ var l = L.length; - //save order number in the node + // save order number in the node for (var i = 0; i < l; ++i) { L[i].order = i; } - //sort now by priority + // sort now by priority L = L.sort(function(A, B) { var Ap = A.constructor.priority || A.priority || 0; var Bp = B.constructor.priority || B.priority || 0; if (Ap == Bp) { - //if same priority, sort by order + // if same priority, sort by order return A.order - B.order; } - return Ap - Bp; //sort by priority + return Ap - Bp; // sort by priority }); - //save order number in the node, again... + // save order number in the node, again... for (var i = 0; i < l; ++i) { L[i].order = i; } @@ -1473,7 +1455,7 @@ return; } - //groups + // groups if (node.constructor === LGraphGroup) { this._groups.push(node); this.setDirtyCanvas(true); @@ -1483,15 +1465,12 @@ return; } - //nodes + // nodes if (node.id != -1 && this._nodes_by_id[node.id] != null) { - console.warn( - "LiteGraph: there is already a node with this ID, changing it" - ); + console.warn("LiteGraph: there is already a node with this ID, changing it"); if (LiteGraph.use_uuids) { node.id = LiteGraph.uuidv4(); - } - else { + } else { node.id = ++this.last_node_id; } } @@ -1500,12 +1479,11 @@ throw "LiteGraph: max number of nodes in a graph reached"; } - //give him an id + // give him an id if (LiteGraph.use_uuids) { if (node.id == null || node.id == -1) node.id = LiteGraph.uuidv4(); - } - else { + } else { if (node.id == null || node.id == -1) { node.id = ++this.last_node_id; } else if (this.last_node_id < node.id) { @@ -1538,7 +1516,7 @@ this.setDirtyCanvas(true); this.change(); - return node; //to chain actions + return node; // to chain actions }; /** @@ -1562,15 +1540,15 @@ if (this._nodes_by_id[node.id] == null) { return; - } //not found + } // not found if (node.ignore_remove) { return; - } //cannot be removed + } // cannot be removed - this.beforeChange(); //sure? - almost sure is wrong + this.beforeChange(); // sure? - almost sure is wrong - //disconnect inputs + // disconnect inputs if (node.inputs) { for (var i = 0; i < node.inputs.length; i++) { var slot = node.inputs[i]; @@ -1580,7 +1558,7 @@ } } - //disconnect outputs + // disconnect outputs if (node.outputs) { for (var i = 0; i < node.outputs.length; i++) { var slot = node.outputs[i]; @@ -1590,9 +1568,9 @@ } } - //node.id = -1; //why? + // node.id = -1; //why? - //callback + // callback if (node.onRemoved) { node.onRemoved(); } @@ -1600,7 +1578,7 @@ node.graph = null; this._version++; - //remove from canvas render + // remove from canvas render if (this.list_of_graphcanvas) { for (var i = 0; i < this.list_of_graphcanvas.length; ++i) { var canvas = this.list_of_graphcanvas[i]; @@ -1613,7 +1591,7 @@ } } - //remove from containers + // remove from containers var pos = this._nodes.indexOf(node); if (pos != -1) { this._nodes.splice(pos, 1); @@ -1624,11 +1602,11 @@ this.onNodeRemoved(node); } - //close panels - this.sendActionToCanvas("checkPanels"); + // close panels + this.sendActionToCanvas("checkPanels"); this.setDirtyCanvas(true, true); - this.afterChange(); //sure? - almost sure is wrong + this.afterChange(); // sure? - almost sure is wrong this.change(); this.updateExecutionOrder(); @@ -1723,16 +1701,16 @@ */ LGraph.prototype.getNodeOnPos = function(x, y, nodes_list, margin) { nodes_list = nodes_list || this._nodes; - var nRet = null; + var nRet = null; for (var i = nodes_list.length - 1; i >= 0; i--) { var n = nodes_list[i]; if (n.isPointInside(x, y, margin)) { // check for lesser interest nodes (TODO check for overlapping, use the top) - /*if (typeof n == "LGraphGroup"){ + /* if (typeof n == "LGraphGroup"){ nRet = n; }else{*/ - return n; - /*}*/ + return n; + /* }*/ } } return nRet; @@ -1790,14 +1768,14 @@ LGraph.prototype.onAction = function(action, param, options) { this._input_nodes = this.findNodesByClass( LiteGraph.GraphInput, - this._input_nodes + this._input_nodes, ); for (var i = 0; i < this._input_nodes.length; ++i) { var node = this._input_nodes[i]; if (node.properties.name != action) { continue; } - //wrap node.onAction(action, param); + // wrap node.onAction(action, param); node.actionDo(action, param, options); break; } @@ -1819,14 +1797,14 @@ LGraph.prototype.addInput = function(name, type, value) { var input = this.inputs[name]; if (input) { - //already exist + // already exist return; } - this.beforeChange(); + this.beforeChange(); this.inputs[name] = { name: name, type: type, value: value }; this._version++; - this.afterChange(); + this.afterChange(); if (this.onInputAdded) { this.onInputAdded(name, type); @@ -2087,7 +2065,7 @@ } }; - //used for undo, called before any change is made to the graph + // used for undo, called before any change is made to the graph LGraph.prototype.beforeChange = function(info) { if (this.onBeforeChange) { this.onBeforeChange(this,info); @@ -2095,7 +2073,7 @@ this.sendActionToCanvas("onBeforeChange", this); }; - //used to resend actions, called after any change is made to the graph + // used to resend actions, called after any change is made to the graph LGraph.prototype.afterChange = function(info) { if (this.onAfterChange) { this.onAfterChange(this,info); @@ -2178,7 +2156,7 @@ } }; - //save and recover app state *************************************** + // save and recover app state *************************************** /** * Creates a Object containing all the info about this graph, it can be serialized * @method serialize @@ -2190,18 +2168,16 @@ nodes_info.push(this._nodes[i].serialize()); } - //pack link info into a non-verbose format + // pack link info into a non-verbose format var links = []; for (var i in this.links) { - //links is an OBJECT + // links is an OBJECT var link = this.links[i]; if (!link.serialize) { - //weird bug I havent solved yet - console.warn( - "weird LLink bug, link info is not a LLink but a regular object" - ); + // weird bug I havent solved yet + console.warn("weird LLink bug, link info is not a LLink but a regular object"); var link2 = new LLink(); - for (var j in link) { + for (var j in link) { link2[j] = link[j]; } this.links[i] = link2; @@ -2223,12 +2199,12 @@ links: links, groups: groups_info, config: this.config, - extra: this.extra, - version: LiteGraph.VERSION + extra: this.extra, + version: LiteGraph.VERSION, }; - if(this.onSerialize) - this.onSerialize(data); + if(this.onSerialize) + this.onSerialize(data); return data; }; @@ -2250,16 +2226,15 @@ var nodes = data.nodes; - //decode links info (they are very verbose) + // decode links info (they are very verbose) if (data.links && data.links.constructor === Array) { var links = []; for (var i = 0; i < data.links.length; ++i) { var link_data = data.links[i]; - if(!link_data) //weird bug - { - console.warn("serialized graph link data contains errors, skipping."); - continue; - } + if(!link_data) { // weird bug + console.warn("serialized graph link data contains errors, skipping."); + continue; + } var link = new LLink(); link.configure(link_data); links[link.id] = link; @@ -2267,41 +2242,39 @@ data.links = links; } - //copy all stored fields + // copy all stored fields for (var i in data) { - if(i == "nodes" || i == "groups" ) //links must be accepted - continue; + if(i == "nodes" || i == "groups" ) // links must be accepted + continue; this[i] = data[i]; } var error = false; - //create nodes + // create nodes this._nodes = []; if (nodes) { for (var i = 0, l = nodes.length; i < l; ++i) { - var n_info = nodes[i]; //stored info + var n_info = nodes[i]; // stored info var node = LiteGraph.createNode(n_info.type, n_info.title); if (!node) { if (LiteGraph.debug) { - console.log( - "Node not found or has errors: " + n_info.type - ); + console.log("Node not found or has errors: " + n_info.type); } - //in case of error we create a replacement node to avoid losing info + // in case of error we create a replacement node to avoid losing info node = new LGraphNode(); node.last_serialization = n_info; node.has_errors = true; error = true; - //continue; + // continue; } - node.id = n_info.id; //id it or it will create a new id - this.add(node, true); //add before configure, otherwise configure cannot create links + node.id = n_info.id; // id it or it will create a new id + this.add(node, true); // add before configure, otherwise configure cannot create links } - //configure nodes afterwards so they can reach each other + // configure nodes afterwards so they can reach each other for (var i = 0, l = nodes.length; i < l; ++i) { var n_info = nodes[i]; var node = this.getNodeById(n_info.id); @@ -2311,7 +2284,7 @@ } } - //groups + // groups this._groups.length = 0; if (data.groups) { for (var i = 0; i < data.groups.length; ++i) { @@ -2323,10 +2296,10 @@ this.updateExecutionOrder(); - this.extra = data.extra || {}; + this.extra = data.extra || {}; - if(this.onConfigure) - this.onConfigure(data); + if(this.onConfigure) + this.onConfigure(data); this._version++; this.setDirtyCanvas(true, true); @@ -2336,22 +2309,21 @@ LGraph.prototype.load = function(url, callback) { var that = this; - //from file - if(url.constructor === File || url.constructor === Blob) - { - var reader = new FileReader(); - reader.addEventListener('load', function(event) { - var data = JSON.parse(event.target.result); - that.configure(data); - if(callback) - callback(); - }); - - reader.readAsText(url); - return; - } + // from file + if(url.constructor === File || url.constructor === Blob) { + var reader = new FileReader(); + reader.addEventListener('load', function(event) { + var data = JSON.parse(event.target.result); + that.configure(data); + if(callback) + callback(); + }); + + reader.readAsText(url); + return; + } - //is a string, then an URL + // is a string, then an URL var req = new XMLHttpRequest(); req.open("GET", url, true); req.send(null); @@ -2362,8 +2334,8 @@ } var data = JSON.parse( req.response ); that.configure(data); - if(callback) - callback(); + if(callback) + callback(); }; req.onerror = function(err) { console.error("Error loading graph:", err); @@ -2371,10 +2343,10 @@ }; LGraph.prototype.onNodeTrace = function(node, msg, color) { - //TODO + // TODO }; - //this is the class in charge of storing link information + // this is the class in charge of storing link information function LLink(id, type, origin_id, origin_slot, target_id, target_slot) { this.id = id; this.type = type; @@ -2384,7 +2356,7 @@ this.target_slot = target_slot; this._data = null; - this._pos = new Float32Array(2); //center + this._pos = new Float32Array(2); // center } LLink.prototype.configure = function(o) { @@ -2412,7 +2384,7 @@ this.origin_slot, this.target_id, this.target_slot, - this.type + this.type, ]; }; @@ -2437,7 +2409,7 @@ + resizable: if set to false it wont be resizable with the mouse + horizontal: slots are distributed horizontally + widgets_start_y: widgets start at y distance from the top of the node - + flags object: + collapsed: if it is collapsed @@ -2503,25 +2475,24 @@ get: function() { return this._pos; }, - enumerable: true + enumerable: true, }); if (LiteGraph.use_uuids) { this.id = LiteGraph.uuidv4(); - } - else { - this.id = -1; //not know till not added + } else { + this.id = -1; // not know till not added } this.type = null; - //inputs available: array of inputs + // inputs available: array of inputs this.inputs = []; this.outputs = []; this.connections = []; - //local data - this.properties = {}; //for the values - this.properties_info = []; //for the info + // local data + this.properties = {}; // for the values + this.properties_info = []; // for the info this.flags = {}; }; @@ -2536,7 +2507,7 @@ } for (var j in info) { if (j == "properties") { - //i don't want to clone properties, I want to reuse the old container + // i don't want to clone properties, I want to reuse the old container for (var k in info.properties) { this.properties[k] = info.properties[k]; if (this.onPropertyChanged) { @@ -2549,14 +2520,13 @@ if (info[j] == null) { continue; } else if (typeof info[j] == "object") { - //object + // object if (this[j] && this[j].configure) { this[j].configure(info[j]); } else { this[j] = LiteGraph.cloneObject(info[j], this[j]); } - } //value - else { + } else { // value this[j] = info[j]; } } @@ -2565,54 +2535,52 @@ this.title = this.constructor.title; } - if (this.inputs) { - for (var i = 0; i < this.inputs.length; ++i) { - var input = this.inputs[i]; - var link_info = this.graph ? this.graph.links[input.link] : null; - if (this.onConnectionsChange) - this.onConnectionsChange( LiteGraph.INPUT, i, true, link_info, input ); //link_info has been created now, so its updated + if (this.inputs) { + for (var i = 0; i < this.inputs.length; ++i) { + var input = this.inputs[i]; + var link_info = this.graph ? this.graph.links[input.link] : null; + if (this.onConnectionsChange) + this.onConnectionsChange( LiteGraph.INPUT, i, true, link_info, input ); // link_info has been created now, so its updated - if( this.onInputAdded ) - this.onInputAdded(input); + if( this.onInputAdded ) + this.onInputAdded(input); - } - } + } + } - if (this.outputs) { - for (var i = 0; i < this.outputs.length; ++i) { - var output = this.outputs[i]; - if (!output.links) { - continue; - } - for (var j = 0; j < output.links.length; ++j) { - var link_info = this.graph ? this.graph.links[output.links[j]] : null; - if (this.onConnectionsChange) - this.onConnectionsChange( LiteGraph.OUTPUT, i, true, link_info, output ); //link_info has been created now, so its updated - } + if (this.outputs) { + for (var i = 0; i < this.outputs.length; ++i) { + var output = this.outputs[i]; + if (!output.links) { + continue; + } + for (var j = 0; j < output.links.length; ++j) { + var link_info = this.graph ? this.graph.links[output.links[j]] : null; + if (this.onConnectionsChange) + this.onConnectionsChange( LiteGraph.OUTPUT, i, true, link_info, output ); // link_info has been created now, so its updated + } - if( this.onOutputAdded ) - this.onOutputAdded(output); - } + if( this.onOutputAdded ) + this.onOutputAdded(output); + } } - if( this.widgets ) - { - for (var i = 0; i < this.widgets.length; ++i) - { - var w = this.widgets[i]; - if(!w) - continue; - if(w.options && w.options.property && (this.properties[ w.options.property ] != undefined)) - w.value = JSON.parse( JSON.stringify( this.properties[ w.options.property ] ) ); - } - if (info.widgets_values) { - for (var i = 0; i < info.widgets_values.length; ++i) { - if (this.widgets[i]) { - this.widgets[i].value = info.widgets_values[i]; - } - } - } - } + if( this.widgets ) { + for (var i = 0; i < this.widgets.length; ++i) { + var w = this.widgets[i]; + if(!w) + continue; + if(w.options && w.options.property && (this.properties[w.options.property] != undefined)) + w.value = JSON.parse( JSON.stringify( this.properties[w.options.property] ) ); + } + if (info.widgets_values) { + for (var i = 0; i < info.widgets_values.length; ++i) { + if (this.widgets[i]) { + this.widgets[i].value = info.widgets_values[i]; + } + } + } + } if (this.onConfigure) { this.onConfigure(info); @@ -2625,18 +2593,18 @@ */ LGraphNode.prototype.serialize = function() { - //create serialization object + // create serialization object var o = { id: this.id, type: this.type, pos: this.pos, size: this.size, flags: LiteGraph.cloneObject(this.flags), - order: this.order, - mode: this.mode + order: this.order, + mode: this.mode, }; - //special case for when there were errors + // special case for when there were errors if (this.constructor === LGraphNode && this.last_serialization) { return this.last_serialization; } @@ -2646,7 +2614,7 @@ } if (this.outputs) { - //clear outputs last data (because data in connections is never serialized but stored inside the outputs info) + // clear outputs last data (because data in connections is never serialized but stored inside the outputs info) for (var i = 0; i < this.outputs.length; i++) { delete this.outputs[i]._data; } @@ -2664,10 +2632,10 @@ if (this.widgets && this.serialize_widgets) { o.widgets_values = []; for (var i = 0; i < this.widgets.length; ++i) { - if(this.widgets[i]) - o.widgets_values[i] = this.widgets[i].value; - else - o.widgets_values[i] = null; + if(this.widgets[i]) + o.widgets_values[i] = this.widgets[i].value; + else + o.widgets_values[i] = null; } } @@ -2690,9 +2658,7 @@ if (this.onSerialize) { if (this.onSerialize(o)) { - console.warn( - "node onSerialize shouldnt return anything, data should be stored in the object pass in the first parameter" - ); + console.warn("node onSerialize shouldnt return anything, data should be stored in the object pass in the first parameter"); } } @@ -2706,10 +2672,10 @@ return null; } - //we clone it because serialize returns shared containers + // we clone it because serialize returns shared containers var data = LiteGraph.cloneObject(this.serialize()); - //remove links + // remove links if (data.inputs) { for (var i = 0; i < data.inputs.length; ++i) { data.inputs[i].link = null; @@ -2730,7 +2696,7 @@ data["id"] = LiteGraph.uuidv4() } - //remove links + // remove links node.configure(data); return node; @@ -2744,7 +2710,7 @@ LGraphNode.prototype.toString = function() { return JSON.stringify(this.serialize()); }; - //LGraphNode.prototype.deserialize = function(info) {} //this cannot be done from within, must be done in LiteGraph + // LGraphNode.prototype.deserialize = function(info) {} //this cannot be done from within, must be done in LiteGraph /** * get the title string @@ -2765,26 +2731,24 @@ if (!this.properties) { this.properties = {}; } - if( value === this.properties[name] ) - return; - var prev_value = this.properties[name]; + if( value === this.properties[name] ) + return; + var prev_value = this.properties[name]; this.properties[name] = value; if (this.onPropertyChanged) { - if( this.onPropertyChanged(name, value, prev_value) === false ) //abort change - this.properties[name] = prev_value; + if( this.onPropertyChanged(name, value, prev_value) === false ) // abort change + this.properties[name] = prev_value; } - if(this.widgets) //widgets could be linked to properties - for(var i = 0; i < this.widgets.length; ++i) - { - var w = this.widgets[i]; - if(!w) - continue; - if(w.options.property == name) - { - w.value = value; - break; - } - } + if(this.widgets) // widgets could be linked to properties + for(var i = 0; i < this.widgets.length; ++i) { + var w = this.widgets[i]; + if(!w) + continue; + if(w.options.property == name) { + w.value = value; + break; + } + } }; // Execution ************************* @@ -2799,8 +2763,8 @@ return; } - //this maybe slow and a niche case - //if(slot && slot.constructor === String) + // this maybe slow and a niche case + // if(slot && slot.constructor === String) // slot = this.findOutputSlot(slot); if (slot == -1 || slot >= this.outputs.length) { @@ -2812,16 +2776,16 @@ return; } - //store data in the output itself in case we want to debug + // store data in the output itself in case we want to debug output_info._data = data; - //if there are connections, pass the data to the connections + // if there are connections, pass the data to the connections if (this.outputs[slot].links) { for (var i = 0; i < this.outputs[slot].links.length; i++) { var link_id = this.outputs[slot].links[i]; - var link = this.graph.links[link_id]; - if(link) - link.data = data; + var link = this.graph.links[link_id]; + if(link) + link.data = data; } } }; @@ -2843,10 +2807,10 @@ if (!output_info) { return; } - //store data in the output itself in case we want to debug + // store data in the output itself in case we want to debug output_info.type = type; - //if there are connections, pass the data to the connections + // if there are connections, pass the data to the connections if (this.outputs[slot].links) { for (var i = 0; i < this.outputs[slot].links.length; i++) { var link_id = this.outputs[slot].links[i]; @@ -2865,7 +2829,7 @@ LGraphNode.prototype.getInputData = function(slot, force_update) { if (!this.inputs) { return; - } //undefined; + } // undefined; if (slot >= this.inputs.length || this.inputs[slot].link == null) { return; @@ -2874,7 +2838,7 @@ var link_id = this.inputs[slot].link; var link = this.graph.links[link_id]; if (!link) { - //bug: weird case but it happens sometimes + // bug: weird case but it happens sometimes return null; } @@ -2882,7 +2846,7 @@ return link.data; } - //special case: used to extract data from the incoming connection before the graph has been executed + // special case: used to extract data from the incoming connection before the graph has been executed var node = this.graph.getNodeById(link.origin_id); if (!node) { return link.data; @@ -2906,7 +2870,7 @@ LGraphNode.prototype.getInputDataType = function(slot) { if (!this.inputs) { return null; - } //undefined; + } // undefined; if (slot >= this.inputs.length || this.inputs[slot].link == null) { return null; @@ -2914,7 +2878,7 @@ var link_id = this.inputs[slot].link; var link = this.graph.links[link_id]; if (!link) { - //bug: weird case but it happens sometimes + // bug: weird case but it happens sometimes return null; } var node = this.graph.getNodeById(link.origin_id); @@ -2937,7 +2901,7 @@ */ LGraphNode.prototype.getInputDataByName = function( slot_name, - force_update + force_update, ) { var slot = this.findInputSlot(slot_name); if (slot == -1) { @@ -2987,7 +2951,7 @@ } if (slot < this.inputs.length) { var slot_info = this.inputs[slot]; - return this.graph.links[ slot_info.link ]; + return this.graph.links[slot_info.link]; } return null; }; @@ -3141,59 +3105,58 @@ return r; }; - LGraphNode.prototype.addOnTriggerInput = function(){ + LGraphNode.prototype.addOnTriggerInput = function() { var trigS = this.findInputSlot("onTrigger"); - if (trigS == -1){ //!trigS || + if (trigS == -1) { // !trigS || var input = this.addInput("onTrigger", LiteGraph.EVENT, {optional: true, nameLocked: true}); return this.findInputSlot("onTrigger"); } return trigS; } - - LGraphNode.prototype.addOnExecutedOutput = function(){ + + LGraphNode.prototype.addOnExecutedOutput = function() { var trigS = this.findOutputSlot("onExecuted"); - if (trigS == -1){ //!trigS || + if (trigS == -1) { // !trigS || var output = this.addOutput("onExecuted", LiteGraph.ACTION, {optional: true, nameLocked: true}); return this.findOutputSlot("onExecuted"); } return trigS; } - - LGraphNode.prototype.onAfterExecuteNode = function(param, options){ + + LGraphNode.prototype.onAfterExecuteNode = function(param, options) { var trigS = this.findOutputSlot("onExecuted"); - if (trigS != -1){ - - //console.debug(this.id+":"+this.order+" triggering slot onAfterExecute"); - //console.debug(param); - //console.debug(options); + if (trigS != -1) { + + // console.debug(this.id+":"+this.order+" triggering slot onAfterExecute"); + // console.debug(param); + // console.debug(options); this.triggerSlot(trigS, param, null, options); - + } - } - - LGraphNode.prototype.changeMode = function(modeTo){ - switch(modeTo){ + } + + LGraphNode.prototype.changeMode = function(modeTo) { + switch(modeTo) { case LiteGraph.ON_EVENT: // this.addOnExecutedOutput(); break; - + case LiteGraph.ON_TRIGGER: this.addOnTriggerInput(); this.addOnExecutedOutput(); break; - + case LiteGraph.NEVER: break; - + case LiteGraph.ALWAYS: break; - + case LiteGraph.ON_REQUEST: break; - + default: return false; - break; } this.mode = modeTo; return true; @@ -3202,19 +3165,18 @@ /** * Triggers the execution of actions that were deferred when the action was triggered * @method executePendingActions - */ + */ LGraphNode.prototype.executePendingActions = function() { if(!this._waiting_actions || !this._waiting_actions.length) return; - for(var i = 0; i < this._waiting_actions.length;++i) - { + for(var i = 0; i < this._waiting_actions.length;++i) { var p = this._waiting_actions[i]; this.onAction(p[0],p[1],p[2],p[3],p[4]); - } + } this._waiting_actions.length = 0; } - + /** * Triggers the node code execution, place a boolean/counter to mark the node as being executed * @method doExecute @@ -3223,30 +3185,28 @@ */ LGraphNode.prototype.doExecute = function(param, options) { options = options || {}; - if (this.onExecute){ - + if (this.onExecute) { + // enable this to give the event an ID - if (!options.action_call) options.action_call = this.id+"_exec_"+Math.floor(Math.random()*9999); - - this.graph.nodes_executing[this.id] = true; //.push(this.id); + if (!options.action_call) options.action_call = this.id+"_exec_"+Math.floor(Math.random()*9999); + + this.graph.nodes_executing[this.id] = true; // .push(this.id); this.onExecute(param, options); - - this.graph.nodes_executing[this.id] = false; //.pop(); - + + this.graph.nodes_executing[this.id] = false; // .pop(); + // save execution/action ref this.exec_version = this.graph.iteration; - if(options && options.action_call){ + if(options && options.action_call) { this.action_call = options.action_call; // if (param) this.graph.nodes_executedAction[this.id] = options.action_call; } } - else { - } this.execute_triggered = 2; // the nFrames it will be used (-- each step), means "how old" is the event if(this.onAfterExecuteNode) this.onAfterExecuteNode(param, options); // callback }; - + /** * Triggers an action, wrapped by logics to control execution flow * @method actionDo @@ -3255,19 +3215,19 @@ */ LGraphNode.prototype.actionDo = function(action, param, options, action_slot ) { options = options || {}; - if (this.onAction){ - - // enable this to give the event an ID + if (this.onAction) { + + // enable this to give the event an ID if (!options.action_call) options.action_call = this.id+"_"+(action?action:"action")+"_"+Math.floor(Math.random()*9999); - - this.graph.nodes_actioning[this.id] = (action?action:"actioning"); //.push(this.id); - + + this.graph.nodes_actioning[this.id] = (action?action:"actioning"); // .push(this.id); + this.onAction(action, param, options, action_slot); - - this.graph.nodes_actioning[this.id] = false; //.pop(); - + + this.graph.nodes_actioning[this.id] = false; // .pop(); + // save execution/action ref - if(options && options.action_call){ + if(options && options.action_call) { this.action_call = options.action_call; // if (param) this.graph.nodes_executedAction[this.id] = options.action_call; } @@ -3275,7 +3235,7 @@ this.action_triggered = 2; // the nFrames it will be used (-- each step), means "how old" is the event if(this.onAfterExecuteNode) this.onAfterExecuteNode(param, options); }; - + /** * Triggers an event in this node, this will trigger any output with the same name * @method trigger @@ -3311,14 +3271,13 @@ return; } - if(slot == null) - { - console.error("slot must be a number"); - return; - } + if(slot == null) { + console.error("slot must be a number"); + return; + } - if(slot.constructor !== Number) - console.warn("slot must be a number, use node.trigger('name') if you want to use a string"); + if(slot.constructor !== Number) + console.warn("slot must be a number, use node.trigger('name') if you want to use a string"); var output = this.outputs[slot]; if (!output) { @@ -3334,52 +3293,47 @@ this.graph._last_trigger_time = LiteGraph.getTime(); } - //for every link attached here + // for every link attached here for (var k = 0; k < links.length; ++k) { var id = links[k]; if (link_id != null && link_id != id) { - //to skip links + // to skip links continue; } var link_info = this.graph.links[links[k]]; if (!link_info) { - //not connected + // not connected continue; } link_info._last_time = LiteGraph.getTime(); var node = this.graph.getNodeById(link_info.target_id); if (!node) { - //node not found? + // node not found? continue; } - //used to mark events in graph + // used to mark events in graph var target_connection = node.inputs[link_info.target_slot]; - if (node.mode === LiteGraph.ON_TRIGGER) - { - // generate unique trigger ID if not present - if (!options.action_call) options.action_call = this.id+"_trigg_"+Math.floor(Math.random()*9999); + if (node.mode === LiteGraph.ON_TRIGGER) { + // generate unique trigger ID if not present + if (!options.action_call) options.action_call = this.id+"_trigg_"+Math.floor(Math.random()*9999); if (node.onExecute) { // -- wrapping node.onExecute(param); -- node.doExecute(param, options); } - } - else if (node.onAction) { + } else if (node.onAction) { // generate unique action ID if not present - if (!options.action_call) options.action_call = this.id+"_act_"+Math.floor(Math.random()*9999); - //pass the action name + if (!options.action_call) options.action_call = this.id+"_act_"+Math.floor(Math.random()*9999); + // pass the action name var target_connection = node.inputs[link_info.target_slot]; - //instead of executing them now, it will be executed in the next graph loop, to ensure data flow - if(LiteGraph.use_deferred_actions && node.onExecute) - { + // instead of executing them now, it will be executed in the next graph loop, to ensure data flow + if(LiteGraph.use_deferred_actions && node.onExecute) { if(!node._waiting_actions) node._waiting_actions = []; node._waiting_actions.push([target_connection.name, param, options, link_info.target_slot]); - } - else - { + } else { // wrap node.onAction(target_connection.name, param); node.actionDo( target_connection.name, param, options, link_info.target_slot ); } @@ -3408,16 +3362,16 @@ return; } - //for every link attached here + // for every link attached here for (var k = 0; k < links.length; ++k) { var id = links[k]; if (link_id != null && link_id != id) { - //to skip links + // to skip links continue; } var link_info = this.graph.links[links[k]]; if (!link_info) { - //not connected + // not connected continue; } link_info._last_time = 0; @@ -3429,12 +3383,11 @@ * @method setSize * @param {vec2} size */ - LGraphNode.prototype.setSize = function(size) - { - this.size = size; - if(this.onResize) - this.onResize(this.size); - } + LGraphNode.prototype.setSize = function(size) { + this.size = size; + if(this.onResize) + this.onResize(this.size); + } /** * add a new property to this node @@ -3448,7 +3401,7 @@ name, default_value, type, - extra_info + extra_info, ) { var o = { name: name, type: type, default_value: default_value }; if (extra_info) { @@ -3467,7 +3420,7 @@ return o; }; - //connections + // connections /** * add a new output slot to use in this node @@ -3491,9 +3444,9 @@ if (this.onOutputAdded) { this.onOutputAdded(output); } - + if (LiteGraph.auto_load_slot_types) LiteGraph.registerNodeAndSlotType(this,type,true); - + this.setSize( this.computeSize() ); this.setDirtyCanvas(true, true); return output; @@ -3521,9 +3474,9 @@ if (this.onOutputAdded) { this.onOutputAdded(o); } - + if (LiteGraph.auto_load_slot_types) LiteGraph.registerNodeAndSlotType(this,info[1],true); - + } this.setSize( this.computeSize() ); @@ -3584,8 +3537,8 @@ if (this.onInputAdded) { this.onInputAdded(input); - } - + } + LiteGraph.registerNodeAndSlotType(this,type); this.setDirtyCanvas(true, true); @@ -3614,7 +3567,7 @@ if (this.onInputAdded) { this.onInputAdded(o); } - + LiteGraph.registerNodeAndSlotType(this,info[1]); } @@ -3661,7 +3614,7 @@ type: type, pos: pos, direction: direction, - links: null + links: null, }; this.connections.push(o); return o; @@ -3680,11 +3633,11 @@ var rows = Math.max( this.inputs ? this.inputs.length : 1, - this.outputs ? this.outputs.length : 1 + this.outputs ? this.outputs.length : 1, ); var size = out || new Float32Array([0, 0]); rows = Math.max(rows, 1); - var font_size = LiteGraph.NODE_TEXT_SIZE; //although it should be graphcanvas.inner_text_font size + var font_size = LiteGraph.NODE_TEXT_SIZE; // although it should be graphcanvas.inner_text_font size var title_width = compute_text_size(this.title); var input_width = 0; @@ -3731,7 +3684,7 @@ widgets_height += 8; } - //compute height using widgets height + // compute height using widgets height if( this.widgets_up ) size[1] = Math.max( size[1], widgets_height ); else if( this.widgets_start_y != null ) @@ -3753,7 +3706,7 @@ size[1] = this.constructor.min_height; } - size[1] += 6; //margin + size[1] += 6; // margin return size; }; @@ -3765,13 +3718,12 @@ * @param {String} property name of the property * @return {Object} the object with all the available info */ - LGraphNode.prototype.getPropertyInfo = function( property ) - { + LGraphNode.prototype.getPropertyInfo = function( property ) { var info = null; - //there are several ways to define info about a property - //legacy mode - if (this.properties_info) { + // there are several ways to define info about a property + // legacy mode + if (this.properties_info) { for (var i = 0; i < this.properties_info.length; ++i) { if (this.properties_info[i].name == property) { info = this.properties_info[i]; @@ -3779,27 +3731,27 @@ } } } - //litescene mode using the constructor - if(this.constructor["@" + property]) - info = this.constructor["@" + property]; + // litescene mode using the constructor + if(this.constructor["@" + property]) + info = this.constructor["@" + property]; - if(this.constructor.widgets_info && this.constructor.widgets_info[property]) - info = this.constructor.widgets_info[property]; + if(this.constructor.widgets_info && this.constructor.widgets_info[property]) + info = this.constructor.widgets_info[property]; - //litescene mode using the constructor - if (!info && this.onGetPropertyInfo) { + // litescene mode using the constructor + if (!info && this.onGetPropertyInfo) { info = this.onGetPropertyInfo(property); } if (!info) info = {}; - if(!info.type) - info.type = typeof this.properties[property]; - if(info.widget == "combo") - info.type = "enum"; + if(!info.type) + info.type = typeof this.properties[property]; + if(info.widget == "combo") + info.type = "enum"; - return info; - } + return info; + } /** * Defines a widget inside the node, it will be rendered on top of the node, you can control lots of properties @@ -3809,44 +3761,40 @@ * @param {String} name the text to show on the widget * @param {String} value the default value * @param {Function|String} callback function to call when it changes (optionally, it can be the name of the property to modify) - * @param {Object} options the object that contains special properties of this widget + * @param {Object} options the object that contains special properties of this widget * @return {Object} the created widget object */ - LGraphNode.prototype.addWidget = function( type, name, value, callback, options ) - { + LGraphNode.prototype.addWidget = function( type, name, value, callback, options ) { if (!this.widgets) { this.widgets = []; } - if(!options && callback && callback.constructor === Object) - { - options = callback; - callback = null; - } + if(!options && callback && callback.constructor === Object) { + options = callback; + callback = null; + } - if(options && options.constructor === String) //options can be the property name - options = { property: options }; + if(options && options.constructor === String) // options can be the property name + options = { property: options }; - if(callback && callback.constructor === String) //callback can be the property name - { - if(!options) - options = {}; - options.property = callback; - callback = null; - } + if(callback && callback.constructor === String) { // callback can be the property name + if(!options) + options = {}; + options.property = callback; + callback = null; + } - if(callback && callback.constructor !== Function) - { - console.warn("addWidget: callback must be a function"); - callback = null; - } + if(callback && callback.constructor !== Function) { + console.warn("addWidget: callback must be a function"); + callback = null; + } var w = { type: type.toLowerCase(), name: name, value: value, callback: callback, - options: options || {} + options: options || {}, }; if (w.options.y !== undefined) { @@ -3860,7 +3808,7 @@ throw "LiteGraph addWidget('combo',...) requires to pass values in options: { values:['red','blue'] }"; } this.widgets.push(w); - this.setSize( this.computeSize() ); + this.setSize( this.computeSize() ); return w; }; @@ -3884,13 +3832,13 @@ const nodePos = this.pos; const isCollapsed = this.flags.collapsed; const nodeSize = this.size; - + let left_offset = 0; // 1 offset due to how nodes are rendered - let right_offset = 1 ; + let right_offset = 1 ; let top_offset = 0; let bottom_offset = 0; - + if (compute_outer) { // 4 offset for collapsed node connection points left_offset = 4; @@ -3901,7 +3849,7 @@ // 5 offset for bottom shadow and collapsed node connection points bottom_offset = 5 + top_offset; } - + out[0] = nodePos[0] - left_offset; out[1] = nodePos[1] - LiteGraph.NODE_TITLE_HEIGHT - top_offset; out[2] = isCollapsed ? @@ -3932,7 +3880,7 @@ margin_top = 0; } if (this.flags && this.flags.collapsed) { - //if ( distance([x,y], [this.pos[0] + this.size[0]*0.5, this.pos[1] + this.size[1]*0.5]) < LiteGraph.NODE_COLLAPSED_RADIUS) + // if ( distance([x,y], [this.pos[0] + this.size[0]*0.5, this.pos[1] + this.size[1]*0.5]) < LiteGraph.NODE_COLLAPSED_RADIUS) if ( isInsideRectangle( x, @@ -3941,7 +3889,7 @@ this.pos[1] - LiteGraph.NODE_TITLE_HEIGHT - margin, (this._collapsed_width || LiteGraph.NODE_COLLAPSED_WIDTH) + 2 * margin, - LiteGraph.NODE_TITLE_HEIGHT + 2 * margin + LiteGraph.NODE_TITLE_HEIGHT + 2 * margin, ) ) { return true; @@ -3965,7 +3913,7 @@ * @return {Object} if found the object contains { input|output: slot object, slot: number, link_pos: [x,y] } */ LGraphNode.prototype.getSlotInPosition = function(x, y) { - //search for inputs + // search for inputs var link_pos = new Float32Array(2); if (this.inputs) { for (var i = 0, l = this.inputs.length; i < l; ++i) { @@ -3978,7 +3926,7 @@ link_pos[0] - 10, link_pos[1] - 5, 20, - 10 + 10, ) ) { return { input: input, slot: i, link_pos: link_pos }; @@ -3997,7 +3945,7 @@ link_pos[0] - 10, link_pos[1] - 5, 20, - 10 + 10, ) ) { return { output: output, slot: i, link_pos: link_pos }; @@ -4015,7 +3963,7 @@ * @param {boolean} returnObj if the obj itself wanted * @return {number_or_object} the slot (-1 if not found) */ - LGraphNode.prototype.findInputSlot = function(name, returnObj) { + LGraphNode.prototype.findInputSlot = function(name, returnObj) { if (!this.inputs) { return -1; } @@ -4046,9 +3994,9 @@ } return -1; }; - + // TODO refactor: USE SINGLE findInput/findOutput functions! :: merge options - + /** * returns the first free input slot * @method findInputSlotFree @@ -4057,9 +4005,10 @@ */ LGraphNode.prototype.findInputSlotFree = function(optsIn) { var optsIn = optsIn || {}; - var optsDef = {returnObj: false - ,typesNotAccepted: [] - }; + var optsDef = { + returnObj: false, + typesNotAccepted: [], + }; var opts = Object.assign(optsDef,optsIn); if (!this.inputs) { return -1; @@ -4068,7 +4017,7 @@ if (this.inputs[i].link && this.inputs[i].link != null) { continue; } - if (opts.typesNotAccepted && opts.typesNotAccepted.includes && opts.typesNotAccepted.includes(this.inputs[i].type)){ + if (opts.typesNotAccepted && opts.typesNotAccepted.includes && opts.typesNotAccepted.includes(this.inputs[i].type)) { continue; } return !opts.returnObj ? i : this.inputs[i]; @@ -4084,9 +4033,10 @@ */ LGraphNode.prototype.findOutputSlotFree = function(optsIn) { var optsIn = optsIn || {}; - var optsDef = { returnObj: false - ,typesNotAccepted: [] - }; + var optsDef = { + returnObj: false, + typesNotAccepted: [], + }; var opts = Object.assign(optsDef,optsIn); if (!this.outputs) { return -1; @@ -4095,14 +4045,14 @@ if (this.outputs[i].links && this.outputs[i].links != null) { continue; } - if (opts.typesNotAccepted && opts.typesNotAccepted.includes && opts.typesNotAccepted.includes(this.outputs[i].type)){ + if (opts.typesNotAccepted && opts.typesNotAccepted.includes && opts.typesNotAccepted.includes(this.outputs[i].type)) { continue; } return !opts.returnObj ? i : this.outputs[i]; } return -1; }; - + /** * findSlotByType for INPUTS */ @@ -4116,7 +4066,7 @@ LGraphNode.prototype.findOutputSlotByType = function(type, returnObj, preferFreeSlot, doNotUseOccupied) { return this.findSlotByType(false, type, returnObj, preferFreeSlot, doNotUseOccupied); }; - + /** * returns the output (or input) slot with a given type, -1 if not found * @method findSlotByType @@ -4135,20 +4085,20 @@ if (!aSlots) { return -1; } - // !! empty string type is considered 0, * !! - if (type == "" || type == "*") type = 0; + // !! empty string type is considered 0, * !! + if (type == "" || type == "*") type = 0; for (var i = 0, l = aSlots.length; i < l; ++i) { var tFound = false; var aSource = (type+"").toLowerCase().split(","); var aDest = aSlots[i].type=="0"||aSlots[i].type=="*"?"0":aSlots[i].type; - aDest = (aDest+"").toLowerCase().split(","); - for(var sI=0;sI= 0 && target_slot !== null){ - //console.debug("CONNbyTYPE type "+target_slotType+" for "+target_slot) + if (target_slot >= 0 && target_slot !== null) { + // console.debug("CONNbyTYPE type "+target_slotType+" for "+target_slot) return this.connect(slot, target_node, target_slot); }else{ - //console.log("type "+target_slotType+" not found or not free?") - if (opts.createEventInCase && target_slotType == LiteGraph.EVENT){ + // console.log("type "+target_slotType+" not found or not free?") + if (opts.createEventInCase && target_slotType == LiteGraph.EVENT) { // WILL CREATE THE onTrigger IN SLOT - //console.debug("connect WILL CREATE THE onTrigger "+target_slotType+" to "+target_node); + // console.debug("connect WILL CREATE THE onTrigger "+target_slotType+" to "+target_node); return this.connect(slot, target_node, -1); } - // connect to the first general output slot if not found a specific type and - if (opts.generalTypeInCase){ + // connect to the first general output slot if not found a specific type and + if (opts.generalTypeInCase) { var target_slot = target_node.findInputSlotByType(0, false, true, true); - //console.debug("connect TO a general type (*, 0), if not found the specific type ",target_slotType," to ",target_node,"RES_SLOT:",target_slot); - if (target_slot >= 0){ + // console.debug("connect TO a general type (*, 0), if not found the specific type ",target_slotType," to ",target_node,"RES_SLOT:",target_slot); + if (target_slot >= 0) { return this.connect(slot, target_node, target_slot); } } // connect to the first free input slot if not found a specific type and this output is general - if (opts.firstFreeIfOutputGeneralInCase && (target_slotType == 0 || target_slotType == "*" || target_slotType == "")){ + if (opts.firstFreeIfOutputGeneralInCase && (target_slotType == 0 || target_slotType == "*" || target_slotType == "")) { var target_slot = target_node.findInputSlotFree({typesNotAccepted: [LiteGraph.EVENT] }); - //console.debug("connect TO TheFirstFREE ",target_slotType," to ",target_node,"RES_SLOT:",target_slot); - if (target_slot >= 0){ - return this.connect(slot, target_node, target_slot); + // console.debug("connect TO TheFirstFREE ",target_slotType," to ",target_node,"RES_SLOT:",target_slot); + if (target_slot >= 0) { + return this.connect(slot, target_node, target_slot); } } - - console.debug("no way to connect type: ",target_slotType," to targetNODE ",target_node); - //TODO filter - + + console.debug("no way to connect type: ",target_slotType," to targetNODE ",target_node); + // TODO filter + return null; } } - + /** * connect this node input to the output of another node BY TYPE * @method connectByType @@ -4239,51 +4190,52 @@ */ LGraphNode.prototype.connectByTypeOutput = function(slot, source_node, source_slotType, optsIn) { var optsIn = optsIn || {}; - var optsDef = { createEventInCase: true - ,firstFreeIfInputGeneralInCase: true - ,generalTypeInCase: true - }; + var optsDef = { + createEventInCase: true, + firstFreeIfInputGeneralInCase: true, + generalTypeInCase: true, + }; var opts = Object.assign(optsDef,optsIn); if (source_node && source_node.constructor === Number) { source_node = this.graph.getNodeById(source_node); } var source_slot = source_node.findOutputSlotByType(source_slotType, false, true); - if (source_slot >= 0 && source_slot !== null){ - //console.debug("CONNbyTYPE OUT! type "+source_slotType+" for "+source_slot) + if (source_slot >= 0 && source_slot !== null) { + // console.debug("CONNbyTYPE OUT! type "+source_slotType+" for "+source_slot) return source_node.connect(source_slot, this, slot); }else{ - - // connect to the first general output slot if not found a specific type and - if (opts.generalTypeInCase){ + + // connect to the first general output slot if not found a specific type and + if (opts.generalTypeInCase) { var source_slot = source_node.findOutputSlotByType(0, false, true, true); - if (source_slot >= 0){ + if (source_slot >= 0) { return source_node.connect(source_slot, this, slot); } } - - if (opts.createEventInCase && source_slotType == LiteGraph.EVENT){ + + if (opts.createEventInCase && source_slotType == LiteGraph.EVENT) { // WILL CREATE THE onExecuted OUT SLOT - if (LiteGraph.do_add_triggers_slots){ - var source_slot = source_node.addOnExecutedOutput(); - return source_node.connect(source_slot, this, slot); - } + if (LiteGraph.do_add_triggers_slots) { + var source_slot = source_node.addOnExecutedOutput(); + return source_node.connect(source_slot, this, slot); + } } // connect to the first free output slot if not found a specific type and this input is general - if (opts.firstFreeIfInputGeneralInCase && (source_slotType == 0 || source_slotType == "*" || source_slotType == "")){ + if (opts.firstFreeIfInputGeneralInCase && (source_slotType == 0 || source_slotType == "*" || source_slotType == "")) { var source_slot = source_node.findOutputSlotFree({typesNotAccepted: [LiteGraph.EVENT] }); - if (source_slot >= 0){ + if (source_slot >= 0) { return source_node.connect(source_slot, this, slot); } } - - console.debug("no way to connect byOUT type: ",source_slotType," to sourceNODE ",source_node); - //TODO filter - - //console.log("type OUT! "+source_slotType+" not found or not free?") + + console.debug("no way to connect byOUT type: ",source_slotType," to sourceNODE ",source_node); + // TODO filter + + // console.log("type OUT! "+source_slotType+" not found or not free?") return null; } } - + /** * connect this node output to the input of another node * @method connect @@ -4296,14 +4248,12 @@ target_slot = target_slot || 0; if (!this.graph) { - //could be connected before adding it to a graph - console.log( - "Connect: Error, node doesn't belong to any graph. Nodes must be added first to a graph before connecting them." - ); //due to link ids being associated with graphs + // could be connected before adding it to a graph + console.log("Connect: Error, node doesn't belong to any graph. Nodes must be added first to a graph before connecting them."); // due to link ids being associated with graphs return null; } - //seek for the output slot + // seek for the output slot if (slot.constructor === String) { slot = this.findOutputSlot(slot); if (slot == -1) { @@ -4326,33 +4276,31 @@ throw "target node is null"; } - //avoid loopback + // avoid loopback if (target_node == this) { return null; } - //you can specify the slot by name + // you can specify the slot by name if (target_slot.constructor === String) { target_slot = target_node.findInputSlot(target_slot); if (target_slot == -1) { if (LiteGraph.debug) { - console.log( - "Connect: Error, no slot of name " + target_slot - ); + console.log("Connect: Error, no slot of name " + target_slot); } return null; } } else if (target_slot === LiteGraph.EVENT) { - - if (LiteGraph.do_add_triggers_slots){ - //search for first slot with event? :: NO this is done outside - //console.log("Connect: Creating triggerEvent"); - // force mode - target_node.changeMode(LiteGraph.ON_TRIGGER); - target_slot = target_node.findInputSlot("onTrigger"); - }else{ - return null; // -- break -- - } + + if (LiteGraph.do_add_triggers_slots) { + // search for first slot with event? :: NO this is done outside + // console.log("Connect: Creating triggerEvent"); + // force mode + target_node.changeMode(LiteGraph.ON_TRIGGER); + target_slot = target_node.findInputSlot("onTrigger"); + } else { + return null; // -- break -- + } } else if ( !target_node.inputs || target_slot >= target_node.inputs.length @@ -4363,14 +4311,14 @@ return null; } - var changed = false; + var changed = false; var input = target_node.inputs[target_slot]; var link_info = null; var output = this.outputs[slot]; - - if (!this.outputs[slot]){ - /*console.debug("Invalid slot passed: "+slot); + + if (!this.outputs[slot]) { + /* console.debug("Invalid slot passed: "+slot); console.debug(this.outputs);*/ return null; } @@ -4378,21 +4326,20 @@ // allow target node to change slot if (target_node.onBeforeConnectInput) { // This way node can choose another slot (or make a new one?) - target_slot = target_node.onBeforeConnectInput(target_slot); //callback + target_slot = target_node.onBeforeConnectInput(target_slot); // callback } - //check target_slot and check connection types - if (target_slot===false || target_slot===null || !LiteGraph.isValidConnection(output.type, input.type)) - { - this.setDirtyCanvas(false, true); - if(changed) - this.graph.connectionChange(this, link_info); - return null; - }else{ - //console.debug("valid connection",output.type, input.type); - } + // check target_slot and check connection types + if (target_slot===false || target_slot===null || !LiteGraph.isValidConnection(output.type, input.type)) { + this.setDirtyCanvas(false, true); + if(changed) + this.graph.connectionChange(this, link_info); + return null; + }else{ + // console.debug("valid connection",output.type, input.type); + } - //allows nodes to block connection, callback + // allows nodes to block connection, callback if (target_node.onConnectInput) { if ( target_node.onConnectInput(target_slot, output.type, output, this, slot) === false ) { return null; @@ -4404,23 +4351,23 @@ } } - //if there is something already plugged there, disconnect + // if there is something already plugged there, disconnect if (target_node.inputs[target_slot] && target_node.inputs[target_slot].link != null) { - this.graph.beforeChange(); + this.graph.beforeChange(); target_node.disconnectInput(target_slot, {doProcessChange: false}); - changed = true; + changed = true; } - if (output.links !== null && output.links.length){ - switch(output.type){ + if (output.links !== null && output.links.length) { + switch(output.type) { case LiteGraph.EVENT: - if (!LiteGraph.allow_multi_output_for_events){ + if (!LiteGraph.allow_multi_output_for_events) { this.graph.beforeChange(); this.disconnectOutput(slot, false, {doProcessChange: false}); // Input(target_slot, {doProcessChange: false}); changed = true; } - break; + break; default: - break; + break; } } @@ -4429,68 +4376,68 @@ nextId = LiteGraph.uuidv4(); else nextId = ++this.graph.last_link_id; - - //create link class - link_info = new LLink( - nextId, - input.type || output.type, - this.id, - slot, - target_node.id, - target_slot - ); - - //add to graph links list - this.graph.links[link_info.id] = link_info; - - //connect in output - if (output.links == null) { - output.links = []; - } - output.links.push(link_info.id); - //connect in input - target_node.inputs[target_slot].link = link_info.id; - if (this.graph) { - this.graph._version++; - } - if (this.onConnectionsChange) { - this.onConnectionsChange( - LiteGraph.OUTPUT, - slot, - true, - link_info, - output - ); - } //link_info has been created now, so its updated - if (target_node.onConnectionsChange) { - target_node.onConnectionsChange( - LiteGraph.INPUT, - target_slot, - true, - link_info, - input - ); - } - if (this.graph && this.graph.onNodeConnectionChange) { - this.graph.onNodeConnectionChange( - LiteGraph.INPUT, - target_node, - target_slot, - this, - slot - ); - this.graph.onNodeConnectionChange( - LiteGraph.OUTPUT, - this, - slot, - target_node, - target_slot - ); - } + + // create link class + link_info = new LLink( + nextId, + input.type || output.type, + this.id, + slot, + target_node.id, + target_slot, + ); + + // add to graph links list + this.graph.links[link_info.id] = link_info; + + // connect in output + if (output.links == null) { + output.links = []; + } + output.links.push(link_info.id); + // connect in input + target_node.inputs[target_slot].link = link_info.id; + if (this.graph) { + this.graph._version++; + } + if (this.onConnectionsChange) { + this.onConnectionsChange( + LiteGraph.OUTPUT, + slot, + true, + link_info, + output, + ); + } // link_info has been created now, so its updated + if (target_node.onConnectionsChange) { + target_node.onConnectionsChange( + LiteGraph.INPUT, + target_slot, + true, + link_info, + input, + ); + } + if (this.graph && this.graph.onNodeConnectionChange) { + this.graph.onNodeConnectionChange( + LiteGraph.INPUT, + target_node, + target_slot, + this, + slot, + ); + this.graph.onNodeConnectionChange( + LiteGraph.OUTPUT, + this, + slot, + target_node, + target_slot, + ); + } this.setDirtyCanvas(false, true); - this.graph.afterChange(); - this.graph.connectionChange(this, link_info); + this.graph.afterChange(); + this.graph.connectionChange(this, link_info); return link_info; }; @@ -4518,13 +4465,13 @@ return false; } - //get output slot + // get output slot var output = this.outputs[slot]; if (!output || !output.links || output.links.length == 0) { return false; } - //one of the output links in this slot + // one of the output links in this slot if (target_node) { if (target_node.constructor === Number) { target_node = this.graph.getNodeById(target_node); @@ -4537,12 +4484,12 @@ var link_id = output.links[i]; var link_info = this.graph.links[link_id]; - //is the link we are searching for... + // is the link we are searching for... if (link_info.target_id == target_node.id) { - output.links.splice(i, 1); //remove here + output.links.splice(i, 1); // remove here var input = target_node.inputs[link_info.target_slot]; - input.link = null; //remove there - delete this.graph.links[link_id]; //remove the link from the links pool + input.link = null; // remove there + delete this.graph.links[link_id]; // remove the link from the links pool if (this.graph) { this.graph._version++; } @@ -4552,47 +4499,46 @@ link_info.target_slot, false, link_info, - input + input, ); - } //link_info hasn't been modified so its ok + } // link_info hasn't been modified so its ok if (this.onConnectionsChange) { this.onConnectionsChange( LiteGraph.OUTPUT, slot, false, link_info, - output + output, ); } if (this.graph && this.graph.onNodeConnectionChange) { this.graph.onNodeConnectionChange( LiteGraph.OUTPUT, this, - slot + slot, ); } if (this.graph && this.graph.onNodeConnectionChange) { this.graph.onNodeConnectionChange( LiteGraph.OUTPUT, this, - slot + slot, ); this.graph.onNodeConnectionChange( LiteGraph.INPUT, target_node, - link_info.target_slot + link_info.target_slot, ); } break; } } - } //all the links in this output slot - else { + } else { // all the links in this output slot for (var i = 0, l = output.links.length; i < l; i++) { var link_id = output.links[i]; var link_info = this.graph.links[link_id]; if (!link_info) { - //bug: it happens sometimes + // bug: it happens sometimes continue; } @@ -4603,44 +4549,44 @@ } if (target_node) { input = target_node.inputs[link_info.target_slot]; - input.link = null; //remove other side link + input.link = null; // remove other side link if (target_node.onConnectionsChange) { target_node.onConnectionsChange( LiteGraph.INPUT, link_info.target_slot, false, link_info, - input + input, ); - } //link_info hasn't been modified so its ok + } // link_info hasn't been modified so its ok if (this.graph && this.graph.onNodeConnectionChange) { this.graph.onNodeConnectionChange( LiteGraph.INPUT, target_node, - link_info.target_slot + link_info.target_slot, ); } } - delete this.graph.links[link_id]; //remove the link from the links pool + delete this.graph.links[link_id]; // remove the link from the links pool if (this.onConnectionsChange) { this.onConnectionsChange( LiteGraph.OUTPUT, slot, false, link_info, - output + output, ); } if (this.graph && this.graph.onNodeConnectionChange) { this.graph.onNodeConnectionChange( LiteGraph.OUTPUT, this, - slot + slot, ); this.graph.onNodeConnectionChange( LiteGraph.INPUT, target_node, - link_info.target_slot + link_info.target_slot, ); } } @@ -4659,7 +4605,7 @@ * @return {boolean} if it was disconnected successfully */ LGraphNode.prototype.disconnectInput = function(slot) { - //seek for the output slot + // seek for the output slot if (slot.constructor === String) { slot = this.findInputSlot(slot); if (slot == -1) { @@ -4681,67 +4627,66 @@ } var link_id = this.inputs[slot].link; - if(link_id != null) - { - this.inputs[slot].link = null; - - //remove other side - var link_info = this.graph.links[link_id]; - if (link_info) { - var target_node = this.graph.getNodeById(link_info.origin_id); - if (!target_node) { - return false; - } + if(link_id != null) { + this.inputs[slot].link = null; + + // remove other side + var link_info = this.graph.links[link_id]; + if (link_info) { + var target_node = this.graph.getNodeById(link_info.origin_id); + if (!target_node) { + return false; + } - var output = target_node.outputs[link_info.origin_slot]; - if (!output || !output.links || output.links.length == 0) { - return false; - } + var output = target_node.outputs[link_info.origin_slot]; + if (!output || !output.links || output.links.length == 0) { + return false; + } - //search in the inputs list for this link - for (var i = 0, l = output.links.length; i < l; i++) { - if (output.links[i] == link_id) { - output.links.splice(i, 1); - break; - } - } + // search in the inputs list for this link + for (var i = 0, l = output.links.length; i < l; i++) { + if (output.links[i] == link_id) { + output.links.splice(i, 1); + break; + } + } - delete this.graph.links[link_id]; //remove from the pool - if (this.graph) { - this.graph._version++; - } - if (this.onConnectionsChange) { - this.onConnectionsChange( - LiteGraph.INPUT, - slot, - false, - link_info, - input - ); - } - if (target_node.onConnectionsChange) { - target_node.onConnectionsChange( - LiteGraph.OUTPUT, - i, - false, - link_info, - output - ); - } - if (this.graph && this.graph.onNodeConnectionChange) { - this.graph.onNodeConnectionChange( - LiteGraph.OUTPUT, - target_node, - i - ); - this.graph.onNodeConnectionChange(LiteGraph.INPUT, this, slot); - } - } - } //link != null + delete this.graph.links[link_id]; // remove from the pool + if (this.graph) { + this.graph._version++; + } + if (this.onConnectionsChange) { + this.onConnectionsChange( + LiteGraph.INPUT, + slot, + false, + link_info, + input, + ); + } + if (target_node.onConnectionsChange) { + target_node.onConnectionsChange( + LiteGraph.OUTPUT, + i, + false, + link_info, + output, + ); + } + if (this.graph && this.graph.onNodeConnectionChange) { + this.graph.onNodeConnectionChange( + LiteGraph.OUTPUT, + target_node, + i, + ); + this.graph.onNodeConnectionChange(LiteGraph.INPUT, this, slot); + } + } + } // link != null this.setDirtyCanvas(false, true); - if(this.graph) - this.graph.connectionChange(this); + if(this.graph) + this.graph.connectionChange(this); return true; }; @@ -4756,7 +4701,7 @@ LGraphNode.prototype.getConnectionPos = function( is_input, slot_number, - out + out, ) { out = out || new Float32Array(2); var num_slots = 0; @@ -4789,14 +4734,14 @@ return out; } - //weird feature that never got finished + // weird feature that never got finished if (is_input && slot_number == -1) { out[0] = this.pos[0] + LiteGraph.NODE_TITLE_HEIGHT * 0.5; out[1] = this.pos[1] + LiteGraph.NODE_TITLE_HEIGHT * 0.5; return out; } - //hard-coded pos + // hard-coded pos if ( is_input && num_slots > slot_number && @@ -4815,7 +4760,7 @@ return out; } - //horizontal distributed slots + // horizontal distributed slots if (this.horizontal) { out[0] = this.pos[0] + (slot_number + 0.5) * (this.size[0] / num_slots); @@ -4827,7 +4772,7 @@ return out; } - //default vertical slots + // default vertical slots if (is_input) { out[0] = this.pos[0] + offset; } else { @@ -4861,21 +4806,21 @@ this.console.shift(); } - if(this.graph.onNodeTrace) - this.graph.onNodeTrace(this, msg); + if(this.graph.onNodeTrace) + this.graph.onNodeTrace(this, msg); }; /* Forces to redraw or the main canvas (LGraphNode) or the bg canvas (links) */ LGraphNode.prototype.setDirtyCanvas = function( dirty_foreground, - dirty_background + dirty_background, ) { if (!this.graph) { return; } this.graph.sendActionToCanvas("setDirty", [ dirty_foreground, - dirty_background + dirty_background, ]); }; @@ -4892,7 +4837,7 @@ return img; }; - //safe LGraphNode action execution (not sure if safe) + // safe LGraphNode action execution (not sure if safe) /* LGraphNode.prototype.executeAction = function(action) { @@ -4941,12 +4886,12 @@ LGraphNode.prototype.executeAction = function(action) for (var i = 0; i < list.length; ++i) { var c = list[i]; - //releasing somebody elses capture?! + // releasing somebody elses capture?! if (!v && c.node_capturing_input != this) { continue; } - //change + // change c.node_capturing_input = v ? this : null; } }; @@ -4985,7 +4930,7 @@ LGraphNode.prototype.executeAction = function(action) LGraphNode.prototype.localToScreen = function(x, y, graphcanvas) { return [ (x + this.pos[0]) * graphcanvas.scale + graphcanvas.offset[0], - (y + this.pos[1]) * graphcanvas.scale + graphcanvas.offset[1] + (y + this.pos[1]) * graphcanvas.scale + graphcanvas.offset[1], ]; }; @@ -5018,7 +4963,7 @@ LGraphNode.prototype.executeAction = function(action) get: function() { return this._pos; }, - enumerable: true + enumerable: true, }); Object.defineProperty(this, "size", { @@ -5032,7 +4977,7 @@ LGraphNode.prototype.executeAction = function(action) get: function() { return this._size; }, - enumerable: true + enumerable: true, }); }; @@ -5051,10 +4996,10 @@ LGraphNode.prototype.executeAction = function(action) Math.round(b[0]), Math.round(b[1]), Math.round(b[2]), - Math.round(b[3]) + Math.round(b[3]), ], color: this.color, - font_size: this.font_size + font_size: this.font_size, }; }; @@ -5081,7 +5026,7 @@ LGraphNode.prototype.executeAction = function(action) node.getBounding(node_bounding); if (!overlapBounding(this._bounding, node_bounding)) { continue; - } //out of the visible area + } // out of the visible area this._nodes.push(node); } }; @@ -5089,9 +5034,9 @@ LGraphNode.prototype.executeAction = function(action) LGraphGroup.prototype.isPointInside = LGraphNode.prototype.isPointInside; LGraphGroup.prototype.setDirtyCanvas = LGraphNode.prototype.setDirtyCanvas; - //**************************************** + //* *************************************** - //Scale and Offset + // Scale and Offset function DragAndScale(element, skip_events) { this.offset = new Float32Array([0, 0]); this.scale = 1; @@ -5118,14 +5063,14 @@ LGraphNode.prototype.executeAction = function(action) this._binded_mouse_callback = this.onMouse.bind(this); - LiteGraph.pointerListenerAdd(element,"down", this._binded_mouse_callback); - LiteGraph.pointerListenerAdd(element,"move", this._binded_mouse_callback); - LiteGraph.pointerListenerAdd(element,"up", this._binded_mouse_callback); + LiteGraph.pointerListenerAdd(element,"down", this._binded_mouse_callback); + LiteGraph.pointerListenerAdd(element,"move", this._binded_mouse_callback); + LiteGraph.pointerListenerAdd(element,"up", this._binded_mouse_callback); element.addEventListener( "mousewheel", this._binded_mouse_callback, - false + false, ); element.addEventListener("wheel", this._binded_mouse_callback, false); }; @@ -5139,13 +5084,12 @@ LGraphNode.prototype.executeAction = function(action) var height = this.element.height; var startx = -this.offset[0]; var starty = -this.offset[1]; - if( viewport ) - { - startx += viewport[0] / this.scale; - starty += viewport[1] / this.scale; - width = viewport[2]; - height = viewport[3]; - } + if( viewport ) { + startx += viewport[0] / this.scale; + starty += viewport[1] / this.scale; + width = viewport[2]; + height = viewport[3]; + } var endx = startx + width / this.scale; var endy = starty + height / this.scale; this.visible_area[0] = startx; @@ -5166,11 +5110,11 @@ LGraphNode.prototype.executeAction = function(action) e.canvasx = x; e.canvasy = y; e.dragging = this.dragging; - - var is_inside = !this.viewport || ( this.viewport && x >= this.viewport[0] && x < (this.viewport[0] + this.viewport[2]) && y >= this.viewport[1] && y < (this.viewport[1] + this.viewport[3]) ); - //console.log("pointerevents: DragAndScale onMouse "+e.type+" "+is_inside); - + var is_inside = !this.viewport || ( this.viewport && x >= this.viewport[0] && x < (this.viewport[0] + this.viewport[2]) && y >= this.viewport[1] && y < (this.viewport[1] + this.viewport[3]) ); + + // console.log("pointerevents: DragAndScale onMouse "+e.type+" "+is_inside); + var ignore = false; if (this.onmouse) { ignore = this.onmouse(e); @@ -5178,9 +5122,9 @@ LGraphNode.prototype.executeAction = function(action) if (e.type == LiteGraph.pointerevents_method+"down" && is_inside) { this.dragging = true; - LiteGraph.pointerListenerRemove(canvas,"move",this._binded_mouse_callback); - LiteGraph.pointerListenerAdd(document,"move",this._binded_mouse_callback); - LiteGraph.pointerListenerAdd(document,"up",this._binded_mouse_callback); + LiteGraph.pointerListenerRemove(canvas,"move",this._binded_mouse_callback); + LiteGraph.pointerListenerAdd(document,"move",this._binded_mouse_callback); + LiteGraph.pointerListenerAdd(document,"up",this._binded_mouse_callback); } else if (e.type == LiteGraph.pointerevents_method+"move") { if (!ignore) { var deltax = x - this.last_mouse[0]; @@ -5191,9 +5135,9 @@ LGraphNode.prototype.executeAction = function(action) } } else if (e.type == LiteGraph.pointerevents_method+"up") { this.dragging = false; - LiteGraph.pointerListenerRemove(document,"move",this._binded_mouse_callback); - LiteGraph.pointerListenerRemove(document,"up",this._binded_mouse_callback); - LiteGraph.pointerListenerAdd(canvas,"move",this._binded_mouse_callback); + LiteGraph.pointerListenerRemove(document,"move",this._binded_mouse_callback); + LiteGraph.pointerListenerRemove(document,"up",this._binded_mouse_callback); + LiteGraph.pointerListenerAdd(canvas,"move",this._binded_mouse_callback); } else if ( is_inside && (e.type == "mousewheel" || e.type == "wheel" || @@ -5207,24 +5151,23 @@ LGraphNode.prototype.executeAction = function(action) e.wheelDeltaY != null ? e.wheelDeltaY : e.detail * -60; } - //from stack overflow + // from stack overflow e.delta = e.wheelDelta ? e.wheelDelta / 40 : e.deltaY - ? -e.deltaY / 3 - : 0; + ? -e.deltaY / 3 + : 0; this.changeDeltaScale(1.0 + e.delta * 0.05); } this.last_mouse[0] = x; this.last_mouse[1] = y; - if(is_inside) - { - e.preventDefault(); - e.stopPropagation(); - return false; - } + if(is_inside) { + e.preventDefault(); + e.stopPropagation(); + return false; + } }; DragAndScale.prototype.toCanvasContext = function(ctx) { @@ -5233,10 +5176,10 @@ LGraphNode.prototype.executeAction = function(action) }; DragAndScale.prototype.convertOffsetToCanvas = function(pos) { - //return [pos[0] / this.scale - this.offset[0], pos[1] / this.scale - this.offset[1]]; + // return [pos[0] / this.scale - this.offset[0], pos[1] / this.scale - this.offset[1]]; return [ (pos[0] + this.offset[0]) * this.scale, - (pos[1] + this.offset[1]) * this.scale + (pos[1] + this.offset[1]) * this.scale, ]; }; @@ -5278,7 +5221,7 @@ LGraphNode.prototype.executeAction = function(action) zooming_center = zooming_center || [ rect.width * 0.5, - rect.height * 0.5 + rect.height * 0.5, ]; var center = this.convertCanvasToOffset(zooming_center); this.scale = value; @@ -5289,7 +5232,7 @@ LGraphNode.prototype.executeAction = function(action) var new_center = this.convertCanvasToOffset(zooming_center); var delta_offset = [ new_center[0] - center[0], - new_center[1] - center[1] + new_center[1] - center[1], ]; this.offset[0] += delta_offset[0]; @@ -5310,9 +5253,9 @@ LGraphNode.prototype.executeAction = function(action) this.offset[1] = 0; }; - //********************************************************************************* + //* ******************************************************************************** // LGraphCanvas: LGraph renderer CLASS - //********************************************************************************* + //* ******************************************************************************** /** * This class is in charge of rendering one graph inside a canvas. And provides all the interaction required. @@ -5327,7 +5270,7 @@ LGraphNode.prototype.executeAction = function(action) function LGraphCanvas(canvas, graph, options) { this.options = options = options || {}; - //if(graph === undefined) + // if(graph === undefined) // throw ("No graph assigned"); this.background_image = LGraphCanvas.DEFAULT_BACKGROUND_IMAGE; @@ -5336,7 +5279,7 @@ LGraphNode.prototype.executeAction = function(action) } this.ds = new DragAndScale(); - this.zoom_modify_alpha = true; //otherwise it generates ugly patterns when scaling down too much + this.zoom_modify_alpha = true; // otherwise it generates ugly patterns when scaling down too much this.title_text_font = "" + LiteGraph.NODE_TEXT_SIZE + "px Arial"; this.inner_text_font = @@ -5345,93 +5288,92 @@ LGraphNode.prototype.executeAction = function(action) this.default_link_color = LiteGraph.LINK_COLOR; this.default_connection_color = { input_off: "#778", - input_on: "#7F7", //"#BBD" + input_on: "#7F7", // "#BBD" output_off: "#778", - output_on: "#7F7" //"#BBD" - }; - this.default_connection_color_byType = { - /*number: "#7F7", + output_on: "#7F7", // "#BBD" + }; + /* number: "#7F7", string: "#77F", boolean: "#F77",*/ - } - this.default_connection_color_byTypeOff = { - /*number: "#474", + this.default_connection_color_byType = {}; + + /* number: "#474", string: "#447", boolean: "#744",*/ - }; + this.default_connection_color_byTypeOff = {}; this.highquality_render = true; - this.use_gradients = false; //set to true to render titlebar with gradients - this.editor_alpha = 1; //used for transition + this.use_gradients = false; // set to true to render titlebar with gradients + this.editor_alpha = 1; // used for transition this.pause_rendering = false; this.clear_background = true; this.clear_background_color = "#222"; - this.read_only = false; //if set to true users cannot modify the graph + this.read_only = false; // if set to true users cannot modify the graph this.render_only_selected = true; this.live_mode = false; this.show_info = true; this.allow_dragcanvas = true; this.allow_dragnodes = true; - this.allow_interaction = true; //allow to control widgets, buttons, collapse, etc - this.multi_select = false; //allow selecting multi nodes without pressing extra keys + this.allow_interaction = true; // allow to control widgets, buttons, collapse, etc + this.multi_select = false; // allow selecting multi nodes without pressing extra keys this.allow_searchbox = true; - this.allow_reconnect_links = true; //allows to change a connection with having to redo it again - this.align_to_grid = false; //snap to grid + this.allow_reconnect_links = true; // allows to change a connection with having to redo it again + this.align_to_grid = false; // snap to grid this.drag_mode = false; this.dragging_rectangle = null; - this.filter = null; //allows to filter to only accept some type of nodes in a graph + this.filter = null; // allows to filter to only accept some type of nodes in a graph - this.set_canvas_dirty_on_mouse_event = true; //forces to redraw the canvas if the mouse does anything + this.set_canvas_dirty_on_mouse_event = true; // forces to redraw the canvas if the mouse does anything this.always_render_background = false; this.render_shadows = true; this.render_canvas_border = true; - this.render_connections_shadows = false; //too much cpu + this.render_connections_shadows = false; // too much cpu this.render_connections_border = true; this.render_curved_connections = false; this.render_connection_arrows = false; this.render_collapsed_slots = true; this.render_execution_order = false; this.render_title_colored = true; - this.render_link_tooltip = true; + this.render_link_tooltip = true; this.links_render_mode = LiteGraph.SPLINE_LINK; - this.mouse = [0, 0]; //mouse in canvas coordinates, where 0,0 is the top-left corner of the blue rectangle - this.graph_mouse = [0, 0]; //mouse in graph coordinates, where 0,0 is the top-left corner of the blue rectangle - this.canvas_mouse = this.graph_mouse; //LEGACY: REMOVE THIS, USE GRAPH_MOUSE INSTEAD + this.mouse = [0, 0]; // mouse in canvas coordinates, where 0,0 is the top-left corner of the blue rectangle + this.graph_mouse = [0, 0]; // mouse in graph coordinates, where 0,0 is the top-left corner of the blue rectangle + this.canvas_mouse = this.graph_mouse; // LEGACY: REMOVE THIS, USE GRAPH_MOUSE INSTEAD - //to personalize the search box + // to personalize the search box this.onSearchBox = null; this.onSearchBoxSelection = null; - //callbacks + // callbacks this.onMouse = null; - this.onDrawBackground = null; //to render background objects (behind nodes and connections) in the canvas affected by transform - this.onDrawForeground = null; //to render foreground objects (above nodes and connections) in the canvas affected by transform - this.onDrawOverlay = null; //to render foreground objects not affected by transform (for GUIs) - this.onDrawLinkTooltip = null; //called when rendering a tooltip - this.onNodeMoved = null; //called after moving a node - this.onSelectionChange = null; //called if the selection changes - this.onConnectingChange = null; //called before any link changes - this.onBeforeChange = null; //called before modifying the graph - this.onAfterChange = null; //called after modifying the graph + this.onDrawBackground = null; // to render background objects (behind nodes and connections) in the canvas affected by transform + this.onDrawForeground = null; // to render foreground objects (above nodes and connections) in the canvas affected by transform + this.onDrawOverlay = null; // to render foreground objects not affected by transform (for GUIs) + this.onDrawLinkTooltip = null; // called when rendering a tooltip + this.onNodeMoved = null; // called after moving a node + this.onSelectionChange = null; // called if the selection changes + this.onConnectingChange = null; // called before any link changes + this.onBeforeChange = null; // called before modifying the graph + this.onAfterChange = null; // called after modifying the graph this.connections_width = 3; this.round_radius = 8; this.current_node = null; - this.node_widget = null; //used for widgets - this.over_link_center = null; + this.node_widget = null; // used for widgets + this.over_link_center = null; this.last_mouse_position = [0, 0]; this.visible_area = this.ds.visible_area; this.visible_links = []; - this.viewport = options.viewport || null; //to constraint render area to a portion of the canvas + this.viewport = options.viewport || null; // to constraint render area to a portion of the canvas - //link canvas and graph + // link canvas and graph if (graph) { graph.attachCanvas(this); } @@ -5448,14 +5390,14 @@ LGraphNode.prototype.executeAction = function(action) global.LGraphCanvas = LiteGraph.LGraphCanvas = LGraphCanvas; - LGraphCanvas.DEFAULT_BACKGROUND_IMAGE = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAIAAAD/gAIDAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAQBJREFUeNrs1rEKwjAUhlETUkj3vP9rdmr1Ysammk2w5wdxuLgcMHyptfawuZX4pJSWZTnfnu/lnIe/jNNxHHGNn//HNbbv+4dr6V+11uF527arU7+u63qfa/bnmh8sWLBgwYJlqRf8MEptXPBXJXa37BSl3ixYsGDBMliwFLyCV/DeLIMFCxYsWLBMwSt4Be/NggXLYMGCBUvBK3iNruC9WbBgwYJlsGApeAWv4L1ZBgsWLFiwYJmCV/AK3psFC5bBggULloJX8BpdwXuzYMGCBctgwVLwCl7Be7MMFixYsGDBsu8FH1FaSmExVfAxBa/gvVmwYMGCZbBg/W4vAQYA5tRF9QYlv/QAAAAASUVORK5CYII="; + LGraphCanvas.DEFAULT_BACKGROUND_IMAGE = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAIAAAD/gAIDAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAQBJREFUeNrs1rEKwjAUhlETUkj3vP9rdmr1Ysammk2w5wdxuLgcMHyptfawuZX4pJSWZTnfnu/lnIe/jNNxHHGNn//HNbbv+4dr6V+11uF527arU7+u63qfa/bnmh8sWLBgwYJlqRf8MEptXPBXJXa37BSl3ixYsGDBMliwFLyCV/DeLIMFCxYsWLBMwSt4Be/NggXLYMGCBUvBK3iNruC9WbBgwYJlsGApeAWv4L1ZBgsWLFiwYJmCV/AK3psFC5bBggULloJX8BpdwXuzYMGCBctgwVLwCl7Be7MMFixYsGDBsu8FH1FaSmExVfAxBa/gvVmwYMGCZbBg/W4vAQYA5tRF9QYlv/QAAAAASUVORK5CYII="; LGraphCanvas.link_type_colors = { "-1": LiteGraph.EVENT_LINK_COLOR, number: "#AAA", - node: "#DCA" + node: "#DCA", }; - LGraphCanvas.gradients = {}; //cache of gradients + LGraphCanvas.gradients = {}; // cache of gradients /** * clears all the data inside @@ -5468,8 +5410,8 @@ LGraphNode.prototype.executeAction = function(action) this.render_time = 0; this.fps = 0; - //this.scale = 1; - //this.offset = [0,0]; + // this.scale = 1; + // this.offset = [0,0]; this.dragging_rectangle = null; @@ -5483,7 +5425,7 @@ LGraphNode.prototype.executeAction = function(action) this.connecting_node = null; this.highlighted_links = {}; - this.dragging_canvas = false; + this.dragging_canvas = false; this.dirty_canvas = true; this.dirty_bgcanvas = true; @@ -5494,8 +5436,8 @@ LGraphNode.prototype.executeAction = function(action) this.last_mouse = [0, 0]; this.last_mouseclick = 0; - this.pointer_is_down = false; - this.pointer_is_double = false; + this.pointer_is_down = false; + this.pointer_is_double = false; this.visible_area.set([0, 0, 0, 0]); if (this.onClear) { @@ -5525,9 +5467,9 @@ LGraphNode.prototype.executeAction = function(action) graph.attachCanvas(this); - //remove the graph stack in case a subgraph was open - if (this._graph_stack) - this._graph_stack = null; + // remove the graph stack in case a subgraph was open + if (this._graph_stack) + this._graph_stack = null; this.setDirty(true, true); }; @@ -5538,12 +5480,11 @@ LGraphNode.prototype.executeAction = function(action) * @method getTopGraph * @return {LGraph} graph */ - LGraphCanvas.prototype.getTopGraph = function() - { - if(this._graph_stack.length) - return this._graph_stack[0]; - return this.graph; - } + LGraphCanvas.prototype.getTopGraph = function() { + if(this._graph_stack.length) + return this._graph_stack[0]; + return this.graph; + } /** * opens a graph contained inside a node in the current graph @@ -5570,7 +5511,7 @@ LGraphNode.prototype.executeAction = function(action) } graph.attachCanvas(this); - this.checkPanels(); + this.checkPanels(); this.setDirty(true, true); }; @@ -5631,7 +5572,7 @@ LGraphNode.prototype.executeAction = function(action) } if (!canvas && this.canvas) { - //maybe detach events from old_canvas + // maybe detach events from old_canvas if (!skip_events) { this.unbindEvents(); } @@ -5644,12 +5585,12 @@ LGraphNode.prototype.executeAction = function(action) return; } - //this.canvas.tabindex = "1000"; + // this.canvas.tabindex = "1000"; canvas.className += " lgraphcanvas"; canvas.data = this; - canvas.tabindex = "1"; //to allow key events + canvas.tabindex = "1"; // to allow key events - //bg canvas: used for non changing stuff + // bg canvas: used for non changing stuff this.bgcanvas = null; if (!this.bgcanvas) { this.bgcanvas = document.createElement("canvas"); @@ -5668,14 +5609,12 @@ LGraphNode.prototype.executeAction = function(action) var ctx = (this.ctx = canvas.getContext("2d")); if (ctx == null) { if (!canvas.webgl_enabled) { - console.warn( - "This canvas seems to be WebGL, enabling WebGL renderer" - ); + console.warn("This canvas seems to be WebGL, enabling WebGL renderer"); } this.enableWebGL(); } - //input: (move and up could be unbinded) + // input: (move and up could be unbinded) // why here? this._mousemove_callback = this.processMouseMove.bind(this); // why here? this._mouseup_callback = this.processMouseUp.bind(this); @@ -5684,9 +5623,9 @@ LGraphNode.prototype.executeAction = function(action) } }; - //used in some events to capture them + // used in some events to capture them LGraphCanvas.prototype._doNothing = function doNothing(e) { - //console.log("pointerevents: _doNothing "+e.type); + // console.log("pointerevents: _doNothing "+e.type); e.preventDefault(); return false; }; @@ -5705,37 +5644,37 @@ LGraphNode.prototype.executeAction = function(action) return; } - //console.log("pointerevents: bindEvents"); - + // console.log("pointerevents: bindEvents"); + var canvas = this.canvas; var ref_window = this.getCanvasWindow(); - var document = ref_window.document; //hack used when moving canvas between windows + var document = ref_window.document; // hack used when moving canvas between windows this._mousedown_callback = this.processMouseDown.bind(this); this._mousewheel_callback = this.processMouseWheel.bind(this); // why mousemove and mouseup were not binded here? this._mousemove_callback = this.processMouseMove.bind(this); this._mouseup_callback = this.processMouseUp.bind(this); - - //touch events -- TODO IMPLEMENT - //this._touch_callback = this.touchHandler.bind(this); - LiteGraph.pointerListenerAdd(canvas,"down", this._mousedown_callback, true); //down do not need to store the binded + // touch events -- TODO IMPLEMENT + // this._touch_callback = this.touchHandler.bind(this); + + LiteGraph.pointerListenerAdd(canvas,"down", this._mousedown_callback, true); // down do not need to store the binded canvas.addEventListener("mousewheel", this._mousewheel_callback, false); LiteGraph.pointerListenerAdd(canvas,"up", this._mouseup_callback, true); // CHECK: ??? binded or not - LiteGraph.pointerListenerAdd(canvas,"move", this._mousemove_callback); - + LiteGraph.pointerListenerAdd(canvas,"move", this._mousemove_callback); + canvas.addEventListener("contextmenu", this._doNothing); canvas.addEventListener( "DOMMouseScroll", this._mousewheel_callback, - false + false, ); - //touch events -- THIS WAY DOES NOT WORK, finish implementing pointerevents, than clean the touchevents - /*if( 'touchstart' in document.documentElement ) + // touch events -- THIS WAY DOES NOT WORK, finish implementing pointerevents, than clean the touchevents + /* if( 'touchstart' in document.documentElement ) { canvas.addEventListener("touchstart", this._touch_callback, true); canvas.addEventListener("touchmove", this._touch_callback, true); @@ -5743,13 +5682,13 @@ LGraphNode.prototype.executeAction = function(action) canvas.addEventListener("touchcancel", this._touch_callback, true); }*/ - //Keyboard ****************** + // Keyboard ****************** this._key_callback = this.processKey.bind(this); - canvas.setAttribute("tabindex",1); //otherwise key events are ignored + canvas.setAttribute("tabindex",1); // otherwise key events are ignored canvas.addEventListener("keydown", this._key_callback, true); - document.addEventListener("keyup", this._key_callback, true); //in document, otherwise it doesn't fire keyup + document.addEventListener("keyup", this._key_callback, true); // in document, otherwise it doesn't fire keyup - //Dropping Stuff over nodes ************************************ + // Dropping Stuff over nodes ************************************ this._ondrop_callback = this.processDrop.bind(this); canvas.addEventListener("dragover", this._doNothing, false); @@ -5770,21 +5709,21 @@ LGraphNode.prototype.executeAction = function(action) return; } - //console.log("pointerevents: unbindEvents"); - + // console.log("pointerevents: unbindEvents"); + var ref_window = this.getCanvasWindow(); var document = ref_window.document; - LiteGraph.pointerListenerRemove(this.canvas,"move", this._mousedown_callback); + LiteGraph.pointerListenerRemove(this.canvas,"move", this._mousedown_callback); LiteGraph.pointerListenerRemove(this.canvas,"up", this._mousedown_callback); LiteGraph.pointerListenerRemove(this.canvas,"down", this._mousedown_callback); this.canvas.removeEventListener( "mousewheel", - this._mousewheel_callback + this._mousewheel_callback, ); this.canvas.removeEventListener( "DOMMouseScroll", - this._mousewheel_callback + this._mousewheel_callback, ); this.canvas.removeEventListener("keydown", this._key_callback); document.removeEventListener("keyup", this._key_callback); @@ -5792,8 +5731,8 @@ LGraphNode.prototype.executeAction = function(action) this.canvas.removeEventListener("drop", this._ondrop_callback); this.canvas.removeEventListener("dragenter", this._doReturnTrue); - //touch events -- THIS WAY DOES NOT WORK, finish implementing pointerevents, than clean the touchevents - /*this.canvas.removeEventListener("touchstart", this._touch_callback ); + // touch events -- THIS WAY DOES NOT WORK, finish implementing pointerevents, than clean the touchevents + /* this.canvas.removeEventListener("touchstart", this._touch_callback ); this.canvas.removeEventListener("touchmove", this._touch_callback ); this.canvas.removeEventListener("touchend", this._touch_callback ); this.canvas.removeEventListener("touchcancel", this._touch_callback );*/ @@ -5883,7 +5822,7 @@ LGraphNode.prototype.executeAction = function(action) LGraphCanvas.prototype.startRendering = function() { if (this.is_rendering) { return; - } //already rendering + } // already rendering this.is_rendering = true; renderFrame.call(this); @@ -5918,19 +5857,18 @@ LGraphNode.prototype.executeAction = function(action) /* LiteGraphCanvas input */ - //used to block future mouse events (because of im gui) - LGraphCanvas.prototype.blockClick = function() - { - this.block_click = true; - this.last_mouseclick = 0; - } - + // used to block future mouse events (because of im gui) + LGraphCanvas.prototype.blockClick = function() { + this.block_click = true; + this.last_mouseclick = 0; + } + LGraphCanvas.prototype.processMouseDown = function(e) { - - if( this.set_canvas_dirty_on_mouse_event ) - this.dirty_canvas = true; - - if (!this.graph) { + + if( this.set_canvas_dirty_on_mouse_event ) + this.dirty_canvas = true; + + if (!this.graph) { return; } @@ -5941,62 +5879,58 @@ LGraphNode.prototype.executeAction = function(action) LGraphCanvas.active_canvas = this; var that = this; - var x = e.clientX; - var y = e.clientY; - //console.log(y,this.viewport); - //console.log("pointerevents: processMouseDown pointerId:"+e.pointerId+" which:"+e.which+" isPrimary:"+e.isPrimary+" :: x y "+x+" "+y); + var x = e.clientX; + var y = e.clientY; + // console.log(y,this.viewport); + // console.log("pointerevents: processMouseDown pointerId:"+e.pointerId+" which:"+e.which+" isPrimary:"+e.isPrimary+" :: x y "+x+" "+y); - this.ds.viewport = this.viewport; - var is_inside = !this.viewport || ( this.viewport && x >= this.viewport[0] && x < (this.viewport[0] + this.viewport[2]) && y >= this.viewport[1] && y < (this.viewport[1] + this.viewport[3]) ); + this.ds.viewport = this.viewport; + var is_inside = !this.viewport || ( this.viewport && x >= this.viewport[0] && x < (this.viewport[0] + this.viewport[2]) && y >= this.viewport[1] && y < (this.viewport[1] + this.viewport[3]) ); - //move mouse move event to the window in case it drags outside of the canvas - if(!this.options.skip_events) - { - LiteGraph.pointerListenerRemove(this.canvas,"move", this._mousemove_callback); - LiteGraph.pointerListenerAdd(ref_window.document,"move", this._mousemove_callback,true); //catch for the entire window - LiteGraph.pointerListenerAdd(ref_window.document,"up", this._mouseup_callback,true); - } + // move mouse move event to the window in case it drags outside of the canvas + if(!this.options.skip_events) { + LiteGraph.pointerListenerRemove(this.canvas,"move", this._mousemove_callback); + LiteGraph.pointerListenerAdd(ref_window.document,"move", this._mousemove_callback,true); // catch for the entire window + LiteGraph.pointerListenerAdd(ref_window.document,"up", this._mouseup_callback,true); + } - if(!is_inside){ - return; - } + if(!is_inside) { + return; + } var node = this.graph.getNodeOnPos( e.canvasX, e.canvasY, this.visible_nodes, 5 ); var skip_dragging = false; var skip_action = false; var now = LiteGraph.getTime(); - var is_primary = (e.isPrimary === undefined || !e.isPrimary); + var is_primary = (e.isPrimary === undefined || !e.isPrimary); var is_double_click = (now - this.last_mouseclick < 300) && is_primary; - this.mouse[0] = e.clientX; - this.mouse[1] = e.clientY; + this.mouse[0] = e.clientX; + this.mouse[1] = e.clientY; this.graph_mouse[0] = e.canvasX; this.graph_mouse[1] = e.canvasY; - this.last_click_position = [this.mouse[0],this.mouse[1]]; - - if (this.pointer_is_down && is_primary ){ - this.pointer_is_double = true; - //console.log("pointerevents: pointer_is_double start"); - }else{ - this.pointer_is_double = false; - } - this.pointer_is_down = true; - - + this.last_click_position = [this.mouse[0],this.mouse[1]]; + + if (this.pointer_is_down && is_primary ) { + this.pointer_is_double = true; + // console.log("pointerevents: pointer_is_double start"); + } else { + this.pointer_is_double = false; + } + this.pointer_is_down = true; + + this.canvas.focus(); LiteGraph.closeAllContextMenus(ref_window); - if (this.onMouse) - { + if (this.onMouse) { if (this.onMouse(e) == true) return; } - //left button mouse / single finger - if (e.which == 1 && !this.pointer_is_double) - { - if (e.ctrlKey) - { + // left button mouse / single finger + if (e.which == 1 && !this.pointer_is_double) { + if (e.ctrlKey) { this.dragging_rectangle = new Float32Array(4); this.dragging_rectangle[0] = e.canvasX; this.dragging_rectangle[1] = e.canvasY; @@ -6006,9 +5940,9 @@ LGraphNode.prototype.executeAction = function(action) } // clone node ALT dragging - if (LiteGraph.alt_drag_do_clone_nodes && e.altKey && node && this.allow_interaction && !skip_action && !this.read_only) - { - if (cloned = node.clone()){ + if (LiteGraph.alt_drag_do_clone_nodes && e.altKey && node && this.allow_interaction && !skip_action && !this.read_only) { + cloned = node.clone(); + if (cloned) { cloned.pos[0] += 5; cloned.pos[1] += 5; this.graph.add(cloned,false,{doCalcSize: false}); @@ -6016,7 +5950,7 @@ LGraphNode.prototype.executeAction = function(action) skip_action = true; if (!block_drag_node) { if (this.allow_dragnodes) { - this.graph.beforeChange(); + this.graph.beforeChange(); this.node_dragged = node; } if (!this.selected_nodes[node.id]) { @@ -6025,35 +5959,36 @@ LGraphNode.prototype.executeAction = function(action) } } } - + var clicking_canvas_bg = false; - //when clicked on top of a node - //and it is not interactive + // when clicked on top of a node + // and it is not interactive if (node && (this.allow_interaction || node.flags.allow_interaction) && !skip_action && !this.read_only) { if (!this.live_mode && !node.flags.pinned) { this.bringToFront(node); - } //if it wasn't selected? + } // if it wasn't selected? - //not dragging mouse to connect two slots + // not dragging mouse to connect two slots if ( this.allow_interaction && !this.connecting_node && !node.flags.collapsed && !this.live_mode ) { - //Search for corner for resize + // Search for corner for resize if ( !skip_action && node.resizable !== false && - isInsideRectangle( e.canvasX, + isInsideRectangle( + e.canvasX, e.canvasY, node.pos[0] + node.size[0] - 5, node.pos[1] + node.size[1] - 5, 10, - 10 + 10, ) ) { - this.graph.beforeChange(); + this.graph.beforeChange(); this.resizing_node = node; this.canvas.style.cursor = "se-resize"; skip_action = true; } else { - //search for outputs + // search for outputs if (node.outputs) { for ( var i = 0, l = node.outputs.length; i < l; ++i ) { var output = node.outputs[i]; @@ -6065,7 +6000,7 @@ LGraphNode.prototype.executeAction = function(action) link_pos[0] - 15, link_pos[1] - 10, 30, - 20 + 20, ) ) { this.connecting_node = node; @@ -6074,7 +6009,7 @@ LGraphNode.prototype.executeAction = function(action) this.connecting_pos = node.getConnectionPos( false, i ); this.connecting_slot = i; - if (LiteGraph.shift_click_do_break_link_from){ + if (LiteGraph.shift_click_do_break_link_from) { if (e.shiftKey) { node.disconnectOutput(i); } @@ -6096,7 +6031,7 @@ LGraphNode.prototype.executeAction = function(action) } } - //search for inputs + // search for inputs if (node.inputs) { for ( var i = 0, l = node.inputs.length; i < l; ++i ) { var input = node.inputs[i]; @@ -6108,7 +6043,7 @@ LGraphNode.prototype.executeAction = function(action) link_pos[0] - 15, link_pos[1] - 10, 30, - 20 + 20, ) ) { if (is_double_click) { @@ -6124,8 +6059,8 @@ LGraphNode.prototype.executeAction = function(action) if (input.link !== null) { var link_info = this.graph.links[ input.link - ]; //before disconnecting - if (LiteGraph.click_do_break_link_to){ + ]; // before disconnecting + if (LiteGraph.click_do_break_link_to) { node.disconnectInput(i); this.dirty_bgcanvas = true; skip_action = true; @@ -6135,10 +6070,10 @@ LGraphNode.prototype.executeAction = function(action) if ( this.allow_reconnect_links || - //this.move_destination_link_without_shift || + // this.move_destination_link_without_shift || e.shiftKey ) { - if (!LiteGraph.click_do_break_link_to){ + if (!LiteGraph.click_do_break_link_to) { node.disconnectInput(i); } this.connecting_node = this.graph._nodes_by_id[ @@ -6150,48 +6085,48 @@ LGraphNode.prototype.executeAction = function(action) this.connecting_slot ]; this.connecting_pos = this.connecting_node.getConnectionPos( false, this.connecting_slot ); - + this.dirty_bgcanvas = true; skip_action = true; } - + }else{ // has not node } - - if (!skip_action){ + + if (!skip_action) { // connect from in to out, from to to from this.connecting_node = node; this.connecting_input = input; this.connecting_input.slot_index = i; this.connecting_pos = node.getConnectionPos( true, i ); this.connecting_slot = i; - + this.dirty_bgcanvas = true; skip_action = true; } } } } - } //not resizing + } // not resizing } - //it wasn't clicked on the links boxes + // it wasn't clicked on the links boxes if (!skip_action) { var block_drag_node = false; - var pos = [e.canvasX - node.pos[0], e.canvasY - node.pos[1]]; + var pos = [e.canvasX - node.pos[0], e.canvasY - node.pos[1]]; - //widgets + // widgets var widget = this.processNodeWidgets( node, this.graph_mouse, e ); if (widget) { block_drag_node = true; this.node_widget = [node, widget]; } - //double clicking + // double clicking if (this.allow_interaction && is_double_click && this.selected_nodes[node.id]) { - //double click node + // double click node if (node.onDblClick) { node.onDblClick( e, pos, this ); } @@ -6199,30 +6134,29 @@ LGraphNode.prototype.executeAction = function(action) block_drag_node = true; } - //if do not capture mouse + // if do not capture mouse if ( node.onMouseDown && node.onMouseDown( e, pos, this ) ) { block_drag_node = true; } else { - //open subgraph button - if(node.subgraph && !node.skip_subgraph_button) - { - if ( !node.flags.collapsed && pos[0] > node.size[0] - LiteGraph.NODE_TITLE_HEIGHT && pos[1] < 0 ) { - var that = this; - setTimeout(function() { - that.openSubgraph(node.subgraph); - }, 10); - } - } - - if (this.live_mode) { - clicking_canvas_bg = true; - block_drag_node = true; - } + // open subgraph button + if(node.subgraph && !node.skip_subgraph_button) { + if ( !node.flags.collapsed && pos[0] > node.size[0] - LiteGraph.NODE_TITLE_HEIGHT && pos[1] < 0 ) { + var that = this; + setTimeout(function() { + that.openSubgraph(node.subgraph); + }, 10); + } + } + + if (this.live_mode) { + clicking_canvas_bg = true; + block_drag_node = true; + } } if (!block_drag_node) { if (this.allow_dragnodes) { - this.graph.beforeChange(); + this.graph.beforeChange(); this.node_dragged = node; } this.processNodeSelected(node, e); @@ -6236,155 +6170,155 @@ LGraphNode.prototype.executeAction = function(action) this.dirty_canvas = true; } - } //clicked outside of nodes - else { - if (!skip_action){ - //search for link connector - if(!this.read_only) { - for (var i = 0; i < this.visible_links.length; ++i) { - var link = this.visible_links[i]; - var center = link._pos; - if ( - !center || + } else { // clicked outside of nodes + if (!skip_action) { + // search for link connector + if(!this.read_only) { + for (var i = 0; i < this.visible_links.length; ++i) { + var link = this.visible_links[i]; + var center = link._pos; + if ( + !center || e.canvasX < center[0] - 4 || e.canvasX > center[0] + 4 || e.canvasY < center[1] - 4 || e.canvasY > center[1] + 4 - ) { - continue; - } - //link clicked - this.showLinkMenu(link, e); - this.over_link_center = null; //clear tooltip - break; - } - } - - this.selected_group = this.graph.getGroupOnPos( e.canvasX, e.canvasY ); - this.selected_group_resizing = false; - if (this.selected_group && !this.read_only ) { - if (e.ctrlKey) { - this.dragging_rectangle = null; - } - - var dist = distance( [e.canvasX, e.canvasY], [ this.selected_group.pos[0] + this.selected_group.size[0], this.selected_group.pos[1] + this.selected_group.size[1] ] ); - if (dist * this.ds.scale < 10) { - this.selected_group_resizing = true; - } else { - this.selected_group.recomputeInsideNodes(); - } - } - - if (is_double_click && !this.read_only && this.allow_searchbox) { - this.showSearchBox(e); - e.preventDefault(); - e.stopPropagation(); - } - - clicking_canvas_bg = true; - } + ) { + continue; + } + // link clicked + this.showLinkMenu(link, e); + this.over_link_center = null; // clear tooltip + break; + } + } + + this.selected_group = this.graph.getGroupOnPos( e.canvasX, e.canvasY ); + this.selected_group_resizing = false; + if (this.selected_group && !this.read_only ) { + if (e.ctrlKey) { + this.dragging_rectangle = null; + } + + var dist = distance( [e.canvasX, e.canvasY], [ this.selected_group.pos[0] + this.selected_group.size[0], this.selected_group.pos[1] + this.selected_group.size[1] ] ); + if (dist * this.ds.scale < 10) { + this.selected_group_resizing = true; + } else { + this.selected_group.recomputeInsideNodes(); + } + } + + if (is_double_click && !this.read_only && this.allow_searchbox) { + this.showSearchBox(e); + e.preventDefault(); + e.stopPropagation(); + } + + clicking_canvas_bg = true; + } } if (!skip_action && clicking_canvas_bg && this.allow_dragcanvas) { - //console.log("pointerevents: dragging_canvas start"); - this.dragging_canvas = true; + // console.log("pointerevents: dragging_canvas start"); + this.dragging_canvas = true; } - + } else if (e.which == 2) { - //middle button - - if (LiteGraph.middle_click_slot_add_default_node){ - if (node && this.allow_interaction && !skip_action && !this.read_only){ - //not dragging mouse to connect two slots - if ( - !this.connecting_node && + // middle button + + if (LiteGraph.middle_click_slot_add_default_node) { + if (node && this.allow_interaction && !skip_action && !this.read_only) { + // not dragging mouse to connect two slots + if ( + !this.connecting_node && !node.flags.collapsed && !this.live_mode - ) { - var mClikSlot = false; - var mClikSlot_index = false; - var mClikSlot_isOut = false; - //search for outputs - if (node.outputs) { - for ( var i = 0, l = node.outputs.length; i < l; ++i ) { - var output = node.outputs[i]; - var link_pos = node.getConnectionPos(false, i); - if (isInsideRectangle(e.canvasX,e.canvasY,link_pos[0] - 15,link_pos[1] - 10,30,20)) { - mClikSlot = output; - mClikSlot_index = i; - mClikSlot_isOut = true; - break; - } - } - } - - //search for inputs - if (node.inputs) { - for ( var i = 0, l = node.inputs.length; i < l; ++i ) { - var input = node.inputs[i]; - var link_pos = node.getConnectionPos(true, i); - if (isInsideRectangle(e.canvasX,e.canvasY,link_pos[0] - 15,link_pos[1] - 10,30,20)) { - mClikSlot = input; - mClikSlot_index = i; - mClikSlot_isOut = false; - break; - } - } - } - //console.log("middleClickSlots? "+mClikSlot+" & "+(mClikSlot_index!==false)); - if (mClikSlot && mClikSlot_index!==false){ - - var alphaPosY = 0.5-((mClikSlot_index+1)/((mClikSlot_isOut?node.outputs.length:node.inputs.length))); - var node_bounding = node.getBounding(); - // estimate a position: this is a bad semi-bad-working mess .. REFACTOR with a correct autoplacement that knows about the others slots and nodes - var posRef = [ (!mClikSlot_isOut?node_bounding[0]:node_bounding[0]+node_bounding[2])// + node_bounding[0]/this.canvas.width*150 - ,e.canvasY-80// + node_bounding[0]/this.canvas.width*66 // vertical "derive" - ]; - var nodeCreated = this.createDefaultNodeForSlot({ nodeFrom: !mClikSlot_isOut?null:node - ,slotFrom: !mClikSlot_isOut?null:mClikSlot_index - ,nodeTo: !mClikSlot_isOut?node:null - ,slotTo: !mClikSlot_isOut?mClikSlot_index:null - ,position: posRef //,e: e - ,nodeType: "AUTO" //nodeNewType - ,posAdd:[!mClikSlot_isOut?-30:30, -alphaPosY*130] //-alphaPosY*30] - ,posSizeFix:[!mClikSlot_isOut?-1:0, 0] //-alphaPosY*2*/ - }); - - } - } - } - } else if (!skip_action && this.allow_dragcanvas) { - //console.log("pointerevents: dragging_canvas start from middle button"); - this.dragging_canvas = true; + ) { + var mClikSlot = false; + var mClikSlot_index = false; + var mClikSlot_isOut = false; + // search for outputs + if (node.outputs) { + for ( var i = 0, l = node.outputs.length; i < l; ++i ) { + var output = node.outputs[i]; + var link_pos = node.getConnectionPos(false, i); + if (isInsideRectangle(e.canvasX,e.canvasY,link_pos[0] - 15,link_pos[1] - 10,30,20)) { + mClikSlot = output; + mClikSlot_index = i; + mClikSlot_isOut = true; + break; + } + } + } + + // search for inputs + if (node.inputs) { + for ( var i = 0, l = node.inputs.length; i < l; ++i ) { + var input = node.inputs[i]; + var link_pos = node.getConnectionPos(true, i); + if (isInsideRectangle(e.canvasX,e.canvasY,link_pos[0] - 15,link_pos[1] - 10,30,20)) { + mClikSlot = input; + mClikSlot_index = i; + mClikSlot_isOut = false; + break; + } + } + } + // console.log("middleClickSlots? "+mClikSlot+" & "+(mClikSlot_index!==false)); + if (mClikSlot && mClikSlot_index!==false) { + + var alphaPosY = 0.5-((mClikSlot_index+1)/((mClikSlot_isOut?node.outputs.length:node.inputs.length))); + var node_bounding = node.getBounding(); + // estimate a position: this is a bad semi-bad-working mess .. REFACTOR with a correct autoplacement that knows about the others slots and nodes + var posRef = [ (!mClikSlot_isOut?node_bounding[0]:node_bounding[0]+node_bounding[2]),// + node_bounding[0]/this.canvas.width*150 + e.canvasY-80,// + node_bounding[0]/this.canvas.width*66 // vertical "derive" + ]; + var nodeCreated = this.createDefaultNodeForSlot({ + nodeFrom: !mClikSlot_isOut?null:node, + slotFrom: !mClikSlot_isOut?null:mClikSlot_index, + nodeTo: !mClikSlot_isOut?node:null, + slotTo: !mClikSlot_isOut?mClikSlot_index:null, + position: posRef, // ,e: e + nodeType: "AUTO", // nodeNewType + posAdd: [!mClikSlot_isOut?-30:30, -alphaPosY*130], // -alphaPosY*30] + posSizeFix: [!mClikSlot_isOut?-1:0, 0], // -alphaPosY*2*/ + }); + + } + } + } + } else if (!skip_action && this.allow_dragcanvas) { + // console.log("pointerevents: dragging_canvas start from middle button"); + this.dragging_canvas = true; } - + } else if (e.which == 3 || this.pointer_is_double) { - - //right button - if (this.allow_interaction && !skip_action && !this.read_only){ - - // is it hover a node ? - if (node){ - if(Object.keys(this.selected_nodes).length - && (this.selected_nodes[node.id] || e.shiftKey || e.ctrlKey || e.metaKey) - ){ - // is multiselected or using shift to include the now node - if (!this.selected_nodes[node.id]) this.selectNodes([node],true); // add this if not present - }else{ - // update selection - this.selectNodes([node]); - } - } - - // show menu on this node - this.processContextMenu(node, e); - } - + + // right button + if (this.allow_interaction && !skip_action && !this.read_only) { + + // is it hover a node ? + if (node) { + if(Object.keys(this.selected_nodes).length + && (this.selected_nodes[node.id] || e.shiftKey || e.ctrlKey || e.metaKey) + ) { + // is multiselected or using shift to include the now node + if (!this.selected_nodes[node.id]) this.selectNodes([node],true); // add this if not present + }else{ + // update selection + this.selectNodes([node]); + } + } + + // show menu on this node + this.processContextMenu(node, e); + } + } - //TODO - //if(this.node_selected != prev_selected) + // TODO + // if(this.node_selected != prev_selected) // this.onNodeSelectionChange(this.node_selected); this.last_mouse[0] = e.clientX; @@ -6399,7 +6333,7 @@ LGraphNode.prototype.executeAction = function(action) this.graph.change(); - //this is to ensure to defocus(blur) if a text input element is on focus + // this is to ensure to defocus(blur) if a text input element is on focus if ( !ref_window.document.activeElement || (ref_window.document.activeElement.nodeName.toLowerCase() != @@ -6427,8 +6361,8 @@ LGraphNode.prototype.executeAction = function(action) this.resize(); } - if( this.set_canvas_dirty_on_mouse_event ) - this.dirty_canvas = true; + if( this.set_canvas_dirty_on_mouse_event ) + this.dirty_canvas = true; if (!this.graph) { return; @@ -6437,24 +6371,23 @@ LGraphNode.prototype.executeAction = function(action) LGraphCanvas.active_canvas = this; this.adjustMouseEvent(e); var mouse = [e.clientX, e.clientY]; - this.mouse[0] = mouse[0]; - this.mouse[1] = mouse[1]; + this.mouse[0] = mouse[0]; + this.mouse[1] = mouse[1]; var delta = [ mouse[0] - this.last_mouse[0], - mouse[1] - this.last_mouse[1] + mouse[1] - this.last_mouse[1], ]; this.last_mouse = mouse; this.graph_mouse[0] = e.canvasX; this.graph_mouse[1] = e.canvasY; - //console.log("pointerevents: processMouseMove "+e.pointerId+" "+e.isPrimary); - - if(this.block_click) - { - //console.log("pointerevents: processMouseMove block_click"); - e.preventDefault(); - return false; - } + // console.log("pointerevents: processMouseMove "+e.pointerId+" "+e.isPrimary); + + if(this.block_click) { + // console.log("pointerevents: processMouseMove block_click"); + e.preventDefault(); + return false; + } e.dragging = this.last_mouse_dragging; @@ -6463,27 +6396,24 @@ LGraphNode.prototype.executeAction = function(action) this.node_widget[0], this.graph_mouse, e, - this.node_widget[1] + this.node_widget[1], ); this.dirty_canvas = true; } - //get node over + // get node over var node = this.graph.getNodeOnPos(e.canvasX,e.canvasY,this.visible_nodes); - if (this.dragging_rectangle) - { + if (this.dragging_rectangle) { this.dragging_rectangle[2] = e.canvasX - this.dragging_rectangle[0]; this.dragging_rectangle[3] = e.canvasY - this.dragging_rectangle[1]; this.dirty_canvas = true; - } - else if (this.selected_group && !this.read_only) - { - //moving/resizing a group + } else if (this.selected_group && !this.read_only) { + // moving/resizing a group if (this.selected_group_resizing) { this.selected_group.size = [ e.canvasX - this.selected_group.pos[0], - e.canvasY - this.selected_group.pos[1] + e.canvasY - this.selected_group.pos[1], ]; } else { var deltax = delta[0] / this.ds.scale; @@ -6495,7 +6425,7 @@ LGraphNode.prototype.executeAction = function(action) } this.dirty_bgcanvas = true; } else if (this.dragging_canvas) { - ////console.log("pointerevents: processMouseMove is dragging_canvas"); + // //console.log("pointerevents: processMouseMove is dragging_canvas"); this.ds.offset[0] += delta[0] / this.ds.scale; this.ds.offset[1] += delta[1] / this.ds.scale; this.dirty_canvas = true; @@ -6505,10 +6435,10 @@ LGraphNode.prototype.executeAction = function(action) this.dirty_canvas = true; } - //remove mouseover flag + // remove mouseover flag for (var i = 0, l = this.graph._nodes.length; i < l; ++i) { if (this.graph._nodes[i].mouseOver && node != this.graph._nodes[i] ) { - //mouse leave + // mouse leave this.graph._nodes[i].mouseOver = false; if (this.node_over && this.node_over.onMouseLeave) { this.node_over.onMouseLeave(e); @@ -6518,15 +6448,15 @@ LGraphNode.prototype.executeAction = function(action) } } - //mouse over a node + // mouse over a node if (node) { - if(node.redraw_on_mouse) + if(node.redraw_on_mouse) this.dirty_canvas = true; - //this.canvas.style.cursor = "move"; + // this.canvas.style.cursor = "move"; if (!node.mouseOver) { - //mouse enter + // mouse enter node.mouseOver = true; this.node_over = node; this.dirty_canvas = true; @@ -6536,45 +6466,45 @@ LGraphNode.prototype.executeAction = function(action) } } - //in case the node wants to do something + // in case the node wants to do something if (node.onMouseMove) { node.onMouseMove( e, [e.canvasX - node.pos[0], e.canvasY - node.pos[1]], this ); } - //if dragging a link + // if dragging a link if (this.connecting_node) { - - if (this.connecting_output){ - - var pos = this._highlight_input || [0, 0]; //to store the output of isOverNodeInput - //on top of input + if (this.connecting_output) { + + var pos = this._highlight_input || [0, 0]; // to store the output of isOverNodeInput + + // on top of input if (this.isOverNodeBox(node, e.canvasX, e.canvasY)) { - //mouse on top of the corner box, don't know what to do + // mouse on top of the corner box, don't know what to do } else { - //check if I have a slot below de mouse + // check if I have a slot below de mouse var slot = this.isOverNodeInput( node, e.canvasX, e.canvasY, pos ); if (slot != -1 && node.inputs[slot]) { var slot_type = node.inputs[slot].type; if ( LiteGraph.isValidConnection( this.connecting_output.type, slot_type ) ) { this._highlight_input = pos; - this._highlight_input_slot = node.inputs[slot]; // XXX CHECK THIS + this._highlight_input_slot = node.inputs[slot]; // XXX CHECK THIS } } else { this._highlight_input = null; - this._highlight_input_slot = null; // XXX CHECK THIS + this._highlight_input_slot = null; // XXX CHECK THIS } } - - }else if(this.connecting_input){ - - var pos = this._highlight_output || [0, 0]; //to store the output of isOverNodeOutput - //on top of output + }else if(this.connecting_input) { + + var pos = this._highlight_output || [0, 0]; // to store the output of isOverNodeOutput + + // on top of output if (this.isOverNodeBox(node, e.canvasX, e.canvasY)) { - //mouse on top of the corner box, don't know what to do + // mouse on top of the corner box, don't know what to do } else { - //check if I have a slot below de mouse + // check if I have a slot below de mouse var slot = this.isOverNodeOutput( node, e.canvasX, e.canvasY, pos ); if (slot != -1 && node.outputs[slot]) { var slot_type = node.outputs[slot].type; @@ -6588,7 +6518,7 @@ LGraphNode.prototype.executeAction = function(action) } } - //Search for corner + // Search for corner if (this.canvas) { if ( isInsideRectangle( @@ -6597,7 +6527,7 @@ LGraphNode.prototype.executeAction = function(action) node.pos[0] + node.size[0] - 5, node.pos[1] + node.size[1] - 5, 5, - 5 + 5, ) ) { this.canvas.style.cursor = "se-resize"; @@ -6605,44 +6535,43 @@ LGraphNode.prototype.executeAction = function(action) this.canvas.style.cursor = "crosshair"; } } - } else { //not over a node + } else { // not over a node - //search for link connector - var over_link = null; - for (var i = 0; i < this.visible_links.length; ++i) { - var link = this.visible_links[i]; - var center = link._pos; - if ( - !center || + // search for link connector + var over_link = null; + for (var i = 0; i < this.visible_links.length; ++i) { + var link = this.visible_links[i]; + var center = link._pos; + if ( + !center || e.canvasX < center[0] - 4 || e.canvasX > center[0] + 4 || e.canvasY < center[1] - 4 || e.canvasY > center[1] + 4 - ) { - continue; - } - over_link = link; - break; - } - if( over_link != this.over_link_center ) - { - this.over_link_center = over_link; - this.dirty_canvas = true; - } + ) { + continue; + } + over_link = link; + break; + } + if( over_link != this.over_link_center ) { + this.over_link_center = over_link; + this.dirty_canvas = true; + } - if (this.canvas) { - this.canvas.style.cursor = ""; - } - } //end + if (this.canvas) { + this.canvas.style.cursor = ""; + } + } // end - //send event to node if capturing input (used with widgets that allow drag outside of the area of the node) + // send event to node if capturing input (used with widgets that allow drag outside of the area of the node) if ( this.node_capturing_input && this.node_capturing_input != node && this.node_capturing_input.onMouseMove ) { this.node_capturing_input.onMouseMove(e,[e.canvasX - this.node_capturing_input.pos[0],e.canvasY - this.node_capturing_input.pos[1]], this); } - //node being dragged + // node being dragged if (this.node_dragged && !this.live_mode) { - //console.log("draggin!",this.selected_nodes); + // console.log("draggin!",this.selected_nodes); for (var i in this.selected_nodes) { var n = this.selected_nodes[i]; n.pos[0] += delta[0] / this.ds.scale; @@ -6658,12 +6587,12 @@ LGraphNode.prototype.executeAction = function(action) } if (this.resizing_node && !this.live_mode) { - //convert mouse to node space - var desired_size = [ e.canvasX - this.resizing_node.pos[0], e.canvasY - this.resizing_node.pos[1] ]; - var min_size = this.resizing_node.computeSize(); - desired_size[0] = Math.max( min_size[0], desired_size[0] ); - desired_size[1] = Math.max( min_size[1], desired_size[1] ); - this.resizing_node.setSize( desired_size ); + // convert mouse to node space + var desired_size = [ e.canvasX - this.resizing_node.pos[0], e.canvasY - this.resizing_node.pos[1] ]; + var min_size = this.resizing_node.computeSize(); + desired_size[0] = Math.max( min_size[0], desired_size[0] ); + desired_size[1] = Math.max( min_size[1], desired_size[1] ); + this.resizing_node.setSize( desired_size ); this.canvas.style.cursor = "se-resize"; this.dirty_canvas = true; @@ -6681,20 +6610,20 @@ LGraphNode.prototype.executeAction = function(action) **/ LGraphCanvas.prototype.processMouseUp = function(e) { - var is_primary = ( e.isPrimary === undefined || e.isPrimary ); - - //early exit for extra pointer - if(!is_primary){ - /*e.stopPropagation(); - e.preventDefault();*/ - //console.log("pointerevents: processMouseUp pointerN_stop "+e.pointerId+" "+e.isPrimary); - return false; - } - - //console.log("pointerevents: processMouseUp "+e.pointerId+" "+e.isPrimary+" :: "+e.clientX+" "+e.clientY); - - if( this.set_canvas_dirty_on_mouse_event ) - this.dirty_canvas = true; + var is_primary = ( e.isPrimary === undefined || e.isPrimary ); + + // early exit for extra pointer + if(!is_primary) { + /* e.stopPropagation(); + e.preventDefault();*/ + // console.log("pointerevents: processMouseUp pointerN_stop "+e.pointerId+" "+e.isPrimary); + return false; + } + + // console.log("pointerevents: processMouseUp "+e.pointerId+" "+e.isPrimary+" :: "+e.clientX+" "+e.clientY); + + if( this.set_canvas_dirty_on_mouse_event ) + this.dirty_canvas = true; if (!this.graph) return; @@ -6703,37 +6632,34 @@ LGraphNode.prototype.executeAction = function(action) var document = window.document; LGraphCanvas.active_canvas = this; - //restore the mousemove event back to the canvas - if(!this.options.skip_events) - { - //console.log("pointerevents: processMouseUp adjustEventListener"); - LiteGraph.pointerListenerRemove(document,"move", this._mousemove_callback,true); - LiteGraph.pointerListenerAdd(this.canvas,"move", this._mousemove_callback,true); - LiteGraph.pointerListenerRemove(document,"up", this._mouseup_callback,true); - } + // restore the mousemove event back to the canvas + if(!this.options.skip_events) { + // console.log("pointerevents: processMouseUp adjustEventListener"); + LiteGraph.pointerListenerRemove(document,"move", this._mousemove_callback,true); + LiteGraph.pointerListenerAdd(this.canvas,"move", this._mousemove_callback,true); + LiteGraph.pointerListenerRemove(document,"up", this._mouseup_callback,true); + } this.adjustMouseEvent(e); var now = LiteGraph.getTime(); e.click_time = now - this.last_mouseclick; this.last_mouse_dragging = false; - this.last_click_position = null; + this.last_click_position = null; - if(this.block_click) - { - //console.log("pointerevents: processMouseUp block_clicks"); - this.block_click = false; //used to avoid sending twice a click in a immediate button - } + if(this.block_click) { + // console.log("pointerevents: processMouseUp block_clicks"); + this.block_click = false; // used to avoid sending twice a click in a immediate button + } + + // console.log("pointerevents: processMouseUp which: "+e.which); - //console.log("pointerevents: processMouseUp which: "+e.which); - if (e.which == 1) { - if( this.node_widget ) - { - this.processNodeWidgets( this.node_widget[0], this.graph_mouse, e ); - } + if( this.node_widget ) { + this.processNodeWidgets( this.node_widget[0], this.graph_mouse, e ); + } - //left button + // left button this.node_widget = null; if (this.selected_group) { @@ -6744,12 +6670,8 @@ LGraphNode.prototype.executeAction = function(action) this.selected_group.pos[1] - Math.round(this.selected_group.pos[1]); this.selected_group.move(diffx, diffy, e.ctrlKey); - this.selected_group.pos[0] = Math.round( - this.selected_group.pos[0] - ); - this.selected_group.pos[1] = Math.round( - this.selected_group.pos[1] - ); + this.selected_group.pos[0] = Math.round(this.selected_group.pos[0]); + this.selected_group.pos[1] = Math.round(this.selected_group.pos[1]); if (this.selected_group._nodes.length) { this.dirty_canvas = true; } @@ -6757,18 +6679,18 @@ LGraphNode.prototype.executeAction = function(action) } this.selected_group_resizing = false; - var node = this.graph.getNodeOnPos( - e.canvasX, - e.canvasY, - this.visible_nodes - ); - + var node = this.graph.getNodeOnPos( + e.canvasX, + e.canvasY, + this.visible_nodes, + ); + if (this.dragging_rectangle) { if (this.graph) { var nodes = this.graph._nodes; var node_bounding = new Float32Array(4); - - //compute bounding and flip if left to right + + // compute bounding and flip if left to right var w = Math.abs(this.dragging_rectangle[2]); var h = Math.abs(this.dragging_rectangle[3]); var startx = @@ -6784,129 +6706,128 @@ LGraphNode.prototype.executeAction = function(action) this.dragging_rectangle[2] = w; this.dragging_rectangle[3] = h; - // test dragging rect size, if minimun simulate a click - if (!node || (w > 10 && h > 10 )){ - //test against all nodes (not visible because the rectangle maybe start outside - var to_select = []; - for (var i = 0; i < nodes.length; ++i) { - var nodeX = nodes[i]; - nodeX.getBounding(node_bounding); - if ( - !overlapBounding( - this.dragging_rectangle, - node_bounding - ) - ) { - continue; - } //out of the visible area - to_select.push(nodeX); - } - if (to_select.length) { - this.selectNodes(to_select,e.shiftKey); // add to selection with shift - } - }else{ - // will select of update selection - this.selectNodes([node],e.shiftKey||e.ctrlKey); // add to selection add to selection with ctrlKey or shiftKey - } - + // test dragging rect size, if minimun simulate a click + if (!node || (w > 10 && h > 10 )) { + // test against all nodes (not visible because the rectangle maybe start outside + var to_select = []; + for (var i = 0; i < nodes.length; ++i) { + var nodeX = nodes[i]; + nodeX.getBounding(node_bounding); + if ( + !overlapBounding( + this.dragging_rectangle, + node_bounding, + ) + ) { + continue; + } // out of the visible area + to_select.push(nodeX); + } + if (to_select.length) { + this.selectNodes(to_select,e.shiftKey); // add to selection with shift + } + }else{ + // will select of update selection + this.selectNodes([node],e.shiftKey||e.ctrlKey); // add to selection add to selection with ctrlKey or shiftKey + } + } this.dragging_rectangle = null; } else if (this.connecting_node) { - //dragging a connection + // dragging a connection this.dirty_canvas = true; this.dirty_bgcanvas = true; var connInOrOut = this.connecting_output || this.connecting_input; var connType = connInOrOut.type; - - //node below mouse + + // node below mouse if (node) { - + /* no need to condition on event type.. just another type if ( connType == LiteGraph.EVENT && this.isOverNodeBox(node, e.canvasX, e.canvasY) ) { - + this.connecting_node.connect( this.connecting_slot, node, LiteGraph.EVENT ); - + } else {*/ - - //slot below mouse? connect - - if (this.connecting_output){ - - var slot = this.isOverNodeInput( - node, - e.canvasX, - e.canvasY - ); - if (slot != -1) { - this.connecting_node.connect(this.connecting_slot, node, slot); - } else { - //not on top of an input - // look for a good slot - this.connecting_node.connectByType(this.connecting_slot,node,connType); - } - - }else if (this.connecting_input){ - - var slot = this.isOverNodeOutput( - node, - e.canvasX, - e.canvasY - ); - if (slot != -1) { - node.connect(slot, this.connecting_node, this.connecting_slot); // this is inverted has output-input nature like - } else { - //not on top of an input - // look for a good slot - this.connecting_node.connectByTypeOutput(this.connecting_slot,node,connType); - } - + // slot below mouse? connect + + if (this.connecting_output) { + + var slot = this.isOverNodeInput( + node, + e.canvasX, + e.canvasY, + ); + if (slot != -1) { + this.connecting_node.connect(this.connecting_slot, node, slot); + } else { + // not on top of an input + // look for a good slot + this.connecting_node.connectByType(this.connecting_slot,node,connType); } - - - //} - - }else{ - - // add menu when releasing link in empty space - if (LiteGraph.release_link_on_empty_shows_menu){ - if (e.shiftKey && this.allow_searchbox){ - if(this.connecting_output){ - this.showSearchBox(e,{node_from: this.connecting_node, slot_from: this.connecting_output, type_filter_in: this.connecting_output.type}); - }else if(this.connecting_input){ - this.showSearchBox(e,{node_to: this.connecting_node, slot_from: this.connecting_input, type_filter_out: this.connecting_input.type}); - } - }else{ - if(this.connecting_output){ - this.showConnectionMenu({nodeFrom: this.connecting_node, slotFrom: this.connecting_output, e: e}); - }else if(this.connecting_input){ - this.showConnectionMenu({nodeTo: this.connecting_node, slotTo: this.connecting_input, e: e}); - } - } - } - } - this.connecting_output = null; - this.connecting_input = null; - this.connecting_pos = null; - this.connecting_node = null; - this.connecting_slot = -1; - } //not dragging connection - else if (this.resizing_node) { - this.dirty_canvas = true; + }else if (this.connecting_input) { + + var slot = this.isOverNodeOutput( + node, + e.canvasX, + e.canvasY, + ); + + if (slot != -1) { + node.connect(slot, this.connecting_node, this.connecting_slot); // this is inverted has output-input nature like + } else { + // not on top of an input + // look for a good slot + this.connecting_node.connectByTypeOutput(this.connecting_slot,node,connType); + } + + } + + + // } + + } else { + + // add menu when releasing link in empty space + if (LiteGraph.release_link_on_empty_shows_menu) { + if (e.shiftKey && this.allow_searchbox) { + if(this.connecting_output) { + this.showSearchBox(e,{node_from: this.connecting_node, slot_from: this.connecting_output, type_filter_in: this.connecting_output.type}); + }else if(this.connecting_input) { + this.showSearchBox(e,{node_to: this.connecting_node, slot_from: this.connecting_input, type_filter_out: this.connecting_input.type}); + } + } else { + if(this.connecting_output) { + this.showConnectionMenu({nodeFrom: this.connecting_node, slotFrom: this.connecting_output, e: e}); + }else if(this.connecting_input) { + this.showConnectionMenu({nodeTo: this.connecting_node, slotTo: this.connecting_input, e: e}); + } + } + } + } + + this.connecting_output = null; + this.connecting_input = null; + this.connecting_pos = null; + this.connecting_node = null; + this.connecting_slot = -1; + } else if (this.resizing_node) { // not dragging connection + this.dirty_canvas = true; this.dirty_bgcanvas = true; - this.graph.afterChange(this.resizing_node); + this.graph.afterChange(this.resizing_node); this.resizing_node = null; } else if (this.node_dragged) { - //node being dragged? + // node being dragged? var node = this.node_dragged; if ( node && @@ -6923,17 +6844,16 @@ LGraphNode.prototype.executeAction = function(action) if (this.graph.config.align_to_grid || this.align_to_grid ) { this.node_dragged.alignToGrid(); } - if( this.onNodeMoved ) - this.onNodeMoved( this.node_dragged ); - this.graph.afterChange(this.node_dragged); + if( this.onNodeMoved ) + this.onNodeMoved( this.node_dragged ); + this.graph.afterChange(this.node_dragged); this.node_dragged = null; - } //no node being dragged - else { - //get node over + } else { // no node being dragged + // get node over var node = this.graph.getNodeOnPos( e.canvasX, e.canvasY, - this.visible_nodes + this.visible_nodes, ); if (!node && e.click_time < 300) { @@ -6952,18 +6872,18 @@ LGraphNode.prototype.executeAction = function(action) ) { this.node_capturing_input.onMouseUp(e, [ e.canvasX - this.node_capturing_input.pos[0], - e.canvasY - this.node_capturing_input.pos[1] + e.canvasY - this.node_capturing_input.pos[1], ]); } } } else if (e.which == 2) { - //middle button - //trace("middle"); + // middle button + // trace("middle"); this.dirty_canvas = true; this.dragging_canvas = false; } else if (e.which == 3) { - //right button - //trace("right"); + // right button + // trace("right"); this.dirty_canvas = true; this.dragging_canvas = false; } @@ -6973,15 +6893,14 @@ LGraphNode.prototype.executeAction = function(action) this.draw(); */ - if (is_primary) - { - this.pointer_is_down = false; - this.pointer_is_double = false; - } - + if (is_primary) { + this.pointer_is_down = false; + this.pointer_is_double = false; + } + this.graph.change(); - //console.log("pointerevents: processMouseUp stopPropagation"); + // console.log("pointerevents: processMouseUp stopPropagation"); e.stopPropagation(); e.preventDefault(); return false; @@ -7000,21 +6919,17 @@ LGraphNode.prototype.executeAction = function(action) this.adjustMouseEvent(e); - var x = e.clientX; - var y = e.clientY; - var is_inside = !this.viewport || ( this.viewport && x >= this.viewport[0] && x < (this.viewport[0] + this.viewport[2]) && y >= this.viewport[1] && y < (this.viewport[1] + this.viewport[3]) ); - if(!is_inside) - return; + var x = e.clientX; + var y = e.clientY; + var is_inside = !this.viewport || ( this.viewport && x >= this.viewport[0] && x < (this.viewport[0] + this.viewport[2]) && y >= this.viewport[1] && y < (this.viewport[1] + this.viewport[3]) ); + if(!is_inside) + return; var scale = this.ds.scale; - if (delta > 0) { - scale *= 1.1; - } else if (delta < 0) { - scale *= 1 / 1.1; - } + scale *= Math.pow(1.1, delta * 0.01); - //this.setZoom( scale, [ e.clientX, e.clientY ] ); + // this.setZoom( scale, [ e.clientX, e.clientY ] ); this.ds.changeScale(scale, [e.clientX, e.clientY]); this.graph.change(); @@ -7036,7 +6951,7 @@ LGraphNode.prototype.executeAction = function(action) node.pos[0] + 2, node.pos[1] + 2 - title_height, title_height - 4, - title_height - 4 + title_height - 4, ) ) { return true; @@ -7052,7 +6967,7 @@ LGraphNode.prototype.executeAction = function(action) node, canvasx, canvasy, - slot_pos + slot_pos, ) { if (node.inputs) { for (var i = 0, l = node.inputs.length; i < l; ++i) { @@ -7066,7 +6981,7 @@ LGraphNode.prototype.executeAction = function(action) link_pos[0] - 5, link_pos[1] - 10, 10, - 20 + 20, ); } else { is_inside = isInsideRectangle( @@ -7075,7 +6990,7 @@ LGraphNode.prototype.executeAction = function(action) link_pos[0] - 10, link_pos[1] - 5, 40, - 10 + 10, ); } if (is_inside) { @@ -7089,7 +7004,7 @@ LGraphNode.prototype.executeAction = function(action) } return -1; }; - + /** * returns the INDEX if a position (in graph space) is on top of a node output slot * @method isOverNodeOuput @@ -7098,7 +7013,7 @@ LGraphNode.prototype.executeAction = function(action) node, canvasx, canvasy, - slot_pos + slot_pos, ) { if (node.outputs) { for (var i = 0, l = node.outputs.length; i < l; ++i) { @@ -7112,7 +7027,7 @@ LGraphNode.prototype.executeAction = function(action) link_pos[0] - 5, link_pos[1] - 10, 10, - 20 + 20, ); } else { is_inside = isInsideRectangle( @@ -7121,7 +7036,7 @@ LGraphNode.prototype.executeAction = function(action) link_pos[0] - 10, link_pos[1] - 5, 40, - 10 + 10, ); } if (is_inside) { @@ -7146,7 +7061,7 @@ LGraphNode.prototype.executeAction = function(action) } var block_default = false; - //console.log(e); //debug + // console.log(e); //debug if (e.target.localName == "input") { return; @@ -7154,26 +7069,26 @@ LGraphNode.prototype.executeAction = function(action) if (e.type == "keydown") { if (e.keyCode == 32) { - //space + // space this.dragging_canvas = true; block_default = true; } - + if (e.keyCode == 27) { - //esc + // esc if(this.node_panel) this.node_panel.close(); if(this.options_panel) this.options_panel.close(); block_default = true; } - //select all Control A + // select all Control A if (e.keyCode == 65 && e.ctrlKey) { this.selectNodes(); block_default = true; } if ((e.keyCode === 67) && (e.metaKey || e.ctrlKey) && !e.shiftKey) { - //copy + // copy if (this.selected_nodes) { this.copyToClipboard(); block_default = true; @@ -7181,11 +7096,11 @@ LGraphNode.prototype.executeAction = function(action) } if ((e.keyCode === 86) && (e.metaKey || e.ctrlKey)) { - //paste + // paste this.pasteFromClipboard(e.shiftKey); } - //delete or backspace + // delete or backspace if (e.keyCode == 46 || e.keyCode == 8) { if ( e.target.localName != "input" && @@ -7196,10 +7111,10 @@ LGraphNode.prototype.executeAction = function(action) } } - //collapse - //... + // collapse + // ... - //TODO + // TODO if (this.selected_nodes) { for (var i in this.selected_nodes) { if (this.selected_nodes[i].onKeyDown) { @@ -7234,16 +7149,17 @@ LGraphNode.prototype.executeAction = function(action) LGraphCanvas.prototype.copyToClipboard = function() { var clipboard_info = { nodes: [], - links: [] + links: [], }; var index = 0; var selected_nodes_array = []; + var map = {}; for (var i in this.selected_nodes) { var node = this.selected_nodes[i]; if (node.clonable === false) continue; - node._relative_id = index; selected_nodes_array.push(node); + map[node.id] = index; index += 1; } @@ -7252,8 +7168,7 @@ LGraphNode.prototype.executeAction = function(action) if(node.clonable === false) continue; var cloned = node.clone(); - if(!cloned) - { + if(!cloned) { console.warn("node type not found: " + node.type ); continue; } @@ -7268,25 +7183,23 @@ LGraphNode.prototype.executeAction = function(action) if (!link_info) { continue; } - var target_node = this.graph.getNodeById( - link_info.origin_id - ); + var target_node = this.graph.getNodeById(link_info.origin_id); if (!target_node) { continue; } clipboard_info.links.push([ - target_node._relative_id, - link_info.origin_slot, //j, - node._relative_id, + map[target_node.id], + link_info.origin_slot, // j, + map[node.id], link_info.target_slot, - target_node.id + target_node.id, ]); } } } localStorage.setItem( "litegrapheditor_clipboard", - JSON.stringify(clipboard_info) + JSON.stringify(clipboard_info), ); }; @@ -7300,25 +7213,24 @@ LGraphNode.prototype.executeAction = function(action) return; } - this.graph.beforeChange(); + this.graph.beforeChange(); - //create nodes + // create nodes var clipboard_info = JSON.parse(data); // calculate top-left node, could work without this processing but using diff with last node pos :: clipboard_info.nodes[clipboard_info.nodes.length-1].pos var posMin = false; var posMinIndexes = false; for (var i = 0; i < clipboard_info.nodes.length; ++i) { - if (posMin){ - if(posMin[0]>clipboard_info.nodes[i].pos[0]){ + if (posMin) { + if(posMin[0]>clipboard_info.nodes[i].pos[0]) { posMin[0] = clipboard_info.nodes[i].pos[0]; posMinIndexes[0] = i; } - if(posMin[1]>clipboard_info.nodes[i].pos[1]){ + if(posMin[1]>clipboard_info.nodes[i].pos[1]) { posMin[1] = clipboard_info.nodes[i].pos[1]; posMinIndexes[1] = i; } - } - else{ + } else{ posMin = [clipboard_info.nodes[i].pos[0], clipboard_info.nodes[i].pos[1]]; posMinIndexes = [i, i]; } @@ -7329,21 +7241,21 @@ LGraphNode.prototype.executeAction = function(action) var node = LiteGraph.createNode(node_data.type); if (node) { node.configure(node_data); - - //paste in last known mouse position - node.pos[0] += this.graph_mouse[0] - posMin[0]; //+= 5; - node.pos[1] += this.graph_mouse[1] - posMin[1]; //+= 5; - this.graph.add(node,{doProcessChange:false}); - + // paste in last known mouse position + node.pos[0] += this.graph_mouse[0] - posMin[0]; // += 5; + node.pos[1] += this.graph_mouse[1] - posMin[1]; // += 5; + + this.graph.add(node,{doProcessChange: false}); + nodes.push(node); } } - //create links + // create links for (var i = 0; i < clipboard_info.links.length; ++i) { var link_info = clipboard_info.links[i]; - var origin_node; + var origin_node = null; var origin_node_relative_id = link_info[0]; if (origin_node_relative_id != null) { origin_node = nodes[origin_node_relative_id]; @@ -7354,15 +7266,15 @@ LGraphNode.prototype.executeAction = function(action) } } var target_node = nodes[link_info[2]]; - if( origin_node && target_node ) - origin_node.connect(link_info[1], target_node, link_info[3]); - else - console.warn("Warning, nodes missing on pasting"); + if( origin_node && target_node ) + origin_node.connect(link_info[1], target_node, link_info[3]); + else + console.warn("Warning, nodes missing on pasting"); } this.selectNodes(nodes); - this.graph.afterChange(); + this.graph.afterChange(); }; /** @@ -7372,13 +7284,13 @@ LGraphNode.prototype.executeAction = function(action) LGraphCanvas.prototype.processDrop = function(e) { e.preventDefault(); this.adjustMouseEvent(e); - var x = e.clientX; - var y = e.clientY; - var is_inside = !this.viewport || ( this.viewport && x >= this.viewport[0] && x < (this.viewport[0] + this.viewport[2]) && y >= this.viewport[1] && y < (this.viewport[1] + this.viewport[3]) ); - if(!is_inside){ - return; - // --- BREAK --- - } + var x = e.clientX; + var y = e.clientY; + var is_inside = !this.viewport || ( this.viewport && x >= this.viewport[0] && x < (this.viewport[0] + this.viewport[2]) && y >= this.viewport[1] && y < (this.viewport[1] + this.viewport[3]) ); + if(!is_inside) { + return; + // --- BREAK --- + } var pos = [e.canvasX, e.canvasY]; @@ -7403,22 +7315,22 @@ LGraphNode.prototype.executeAction = function(action) var file = e.dataTransfer.files[0]; var filename = file.name; var ext = LGraphCanvas.getFileExtension(filename); - //console.log(file); + // console.log(file); if (node.onDropFile) { node.onDropFile(file); } if (node.onDropData) { - //prepare reader + // prepare reader var reader = new FileReader(); reader.onload = function(event) { - //console.log(event.target); + // console.log(event.target); var data = event.target.result; node.onDropData(data, filename, file); }; - //read data + // read data var type = file.type.split("/")[0]; if (type == "text" || type == "") { reader.readAsText(file); @@ -7445,21 +7357,21 @@ LGraphNode.prototype.executeAction = function(action) return false; }; - //called if the graph doesn't have a default drop item behaviour + // called if the graph doesn't have a default drop item behaviour LGraphCanvas.prototype.checkDropItem = function(e) { if (e.dataTransfer.files.length) { var file = e.dataTransfer.files[0]; var ext = LGraphCanvas.getFileExtension(file.name).toLowerCase(); var nodetype = LiteGraph.node_types_by_file_extension[ext]; if (nodetype) { - this.graph.beforeChange(); + this.graph.beforeChange(); var node = LiteGraph.createNode(nodetype.type); node.pos = [e.canvasX, e.canvasY]; this.graph.add(node); if (node.onDropFile) { node.onDropFile(file); } - this.graph.afterChange(); + this.graph.afterChange(); } } }; @@ -7467,11 +7379,9 @@ LGraphNode.prototype.executeAction = function(action) LGraphCanvas.prototype.processNodeDblClicked = function(n) { if (this.onShowNodePanel) { this.onShowNodePanel(n); + } else { + this.showShowNodePanel(n); } - else - { - this.showShowNodePanel(n); - } if (this.onNodeDblClicked) { this.onNodeDblClicked(n); @@ -7493,7 +7403,7 @@ LGraphNode.prototype.executeAction = function(action) **/ LGraphCanvas.prototype.selectNode = function( node, - add_to_current_selection + add_to_current_selection, ) { if (node == null) { this.deselectAllNodes(); @@ -7506,14 +7416,13 @@ LGraphNode.prototype.executeAction = function(action) * selects several nodes (or adds them to the current selection) * @method selectNodes **/ - LGraphCanvas.prototype.selectNodes = function( nodes, add_to_current_selection ) - { - if (!add_to_current_selection) { + LGraphCanvas.prototype.selectNodes = function( nodes, add_to_current_selection ) { + if (!add_to_current_selection) { this.deselectAllNodes(); } nodes = nodes || this.graph._nodes; - if (typeof nodes == "string") nodes = [nodes]; + if (typeof nodes == "string") nodes = [nodes]; for (var i in nodes) { var node = nodes[i]; if (node.is_selected) { @@ -7544,8 +7453,8 @@ LGraphNode.prototype.executeAction = function(action) } } - if( this.onSelectionChange ) - this.onSelectionChange( this.selected_nodes ); + if( this.onSelectionChange ) + this.onSelectionChange( this.selected_nodes ); this.setDirty(true); }; @@ -7567,7 +7476,7 @@ LGraphNode.prototype.executeAction = function(action) this.onNodeDeselected(node); } - //remove highlighted + // remove highlighted if (node.inputs) { for (var i = 0; i < node.inputs.length; ++i) { delete this.highlighted_links[node.inputs[i].link]; @@ -7603,15 +7512,15 @@ LGraphNode.prototype.executeAction = function(action) node.onDeselected(); } node.is_selected = false; - if (this.onNodeDeselected) { - this.onNodeDeselected(node); - } + if (this.onNodeDeselected) { + this.onNodeDeselected(node); + } } this.selected_nodes = {}; this.current_node = null; this.highlighted_links = {}; - if( this.onSelectionChange ) - this.onSelectionChange( this.selected_nodes ); + if( this.onSelectionChange ) + this.onSelectionChange( this.selected_nodes ); this.setDirty(true); }; @@ -7621,36 +7530,35 @@ LGraphNode.prototype.executeAction = function(action) **/ LGraphCanvas.prototype.deleteSelectedNodes = function() { - this.graph.beforeChange(); + this.graph.beforeChange(); for (var i in this.selected_nodes) { var node = this.selected_nodes[i]; - if(node.block_delete) - continue; + if(node.block_delete) + continue; - //autoconnect when possible (very basic, only takes into account first input-output) - if(node.inputs && node.inputs.length && node.outputs && node.outputs.length && LiteGraph.isValidConnection( node.inputs[0].type, node.outputs[0].type ) && node.inputs[0].link && node.outputs[0].links && node.outputs[0].links.length ) - { - var input_link = node.graph.links[ node.inputs[0].link ]; - var output_link = node.graph.links[ node.outputs[0].links[0] ]; - var input_node = node.getInputNode(0); - var output_node = node.getOutputNodes(0)[0]; - if(input_node && output_node) - input_node.connect( input_link.origin_slot, output_node, output_link.target_slot ); - } + // autoconnect when possible (very basic, only takes into account first input-output) + if(node.inputs && node.inputs.length && node.outputs && node.outputs.length && LiteGraph.isValidConnection( node.inputs[0].type, node.outputs[0].type ) && node.inputs[0].link && node.outputs[0].links && node.outputs[0].links.length ) { + var input_link = node.graph.links[node.inputs[0].link]; + var output_link = node.graph.links[node.outputs[0].links[0]]; + var input_node = node.getInputNode(0); + var output_node = node.getOutputNodes(0)[0]; + if(input_node && output_node) + input_node.connect( input_link.origin_slot, output_node, output_link.target_slot ); + } this.graph.remove(node); - if (this.onNodeDeselected) { - this.onNodeDeselected(node); - } + if (this.onNodeDeselected) { + this.onNodeDeselected(node); + } } this.selected_nodes = {}; this.current_node = null; this.highlighted_links = {}; this.setDirty(true); - this.graph.afterChange(); + this.graph.afterChange(); }; - + /** * centers the camera on a given node * @method centerOnNode @@ -7672,18 +7580,18 @@ LGraphNode.prototype.executeAction = function(action) * @method adjustMouseEvent **/ LGraphCanvas.prototype.adjustMouseEvent = function(e) { - var clientX_rel = 0; + var clientX_rel = 0; var clientY_rel = 0; - - if (this.canvas) { + + if (this.canvas) { var b = this.canvas.getBoundingClientRect(); clientX_rel = e.clientX - b.left; clientY_rel = e.clientY - b.top; } else { - clientX_rel = e.clientX; - clientY_rel = e.clientY; + clientX_rel = e.clientX; + clientY_rel = e.clientY; } - + // e.deltaX = clientX_rel - this.last_mouse_position[0]; // e.deltaY = clientY_rel- this.last_mouse_position[1]; @@ -7692,8 +7600,8 @@ LGraphNode.prototype.executeAction = function(action) e.canvasX = clientX_rel / this.ds.scale - this.ds.offset[0]; e.canvasY = clientY_rel / this.ds.scale - this.ds.offset[1]; - - //console.log("pointerevents: adjustMouseEvent "+e.clientX+":"+e.clientY+" "+clientX_rel+":"+clientY_rel+" "+e.canvasX+":"+e.canvasY); + + // console.log("pointerevents: adjustMouseEvent "+e.clientX+":"+e.clientY+" "+clientX_rel+":"+clientY_rel+" "+e.canvasX+":"+e.canvasY); }; /** @@ -7742,12 +7650,12 @@ LGraphNode.prototype.executeAction = function(action) return this.ds.convertCanvasToOffset(pos, out); }; - //converts event coordinates from canvas2D to graph coordinates + // converts event coordinates from canvas2D to graph coordinates LGraphCanvas.prototype.convertEventToCanvasOffset = function(e) { var rect = this.canvas.getBoundingClientRect(); return this.convertCanvasToOffset([ e.clientX - rect.left, - e.clientY - rect.top + e.clientY - rect.top, ]); }; @@ -7795,14 +7703,14 @@ LGraphNode.prototype.executeAction = function(action) for (var i = 0, l = nodes.length; i < l; ++i) { var n = nodes[i]; - //skip rendering nodes in live mode + // skip rendering nodes in live mode if (this.live_mode && !n.onDrawBackground && !n.onDrawForeground) { continue; } if (!overlapBounding(this.visible_area, n.getBounding(temp, true))) { continue; - } //out of the visible area + } // out of the visible area visible_nodes.push(n); } @@ -7818,7 +7726,7 @@ LGraphNode.prototype.executeAction = function(action) return; } - //fps counting + // fps counting var now = LiteGraph.getTime(); this.render_time = (now - this.last_draw_time) * 0.001; this.last_draw_time = now; @@ -7858,19 +7766,19 @@ LGraphNode.prototype.executeAction = function(action) } var ctx = this.ctx; if (!ctx) { - //maybe is using webgl... + // maybe is using webgl... return; } var canvas = this.canvas; if ( ctx.start2D && !this.viewport ) { ctx.start2D(); - ctx.restore(); - ctx.setTransform(1, 0, 0, 1, 0, 0); + ctx.restore(); + ctx.setTransform(1, 0, 0, 1, 0, 0); } - //clip dirty area if there is one, otherwise work in full canvas - var area = this.viewport || this.dirty_area; + // clip dirty area if there is one, otherwise work in full canvas + var area = this.viewport || this.dirty_area; if (area) { ctx.save(); ctx.beginPath(); @@ -7878,89 +7786,88 @@ LGraphNode.prototype.executeAction = function(action) ctx.clip(); } - //clear - //canvas.width = canvas.width; + // clear + // canvas.width = canvas.width; if (this.clear_background) { - if(area) - ctx.clearRect( area[0],area[1],area[2],area[3] ); - else - ctx.clearRect(0, 0, canvas.width, canvas.height); + if(area) + ctx.clearRect( area[0],area[1],area[2],area[3] ); + else + ctx.clearRect(0, 0, canvas.width, canvas.height); } - //draw bg canvas + // draw bg canvas if (this.bgcanvas == this.canvas) { this.drawBackCanvas(); } else { ctx.drawImage( this.bgcanvas, 0, 0 ); } - //rendering + // rendering if (this.onRender) { this.onRender(canvas, ctx); } - //info widget + // info widget if (this.show_info) { this.renderInfo(ctx, area ? area[0] : 0, area ? area[1] : 0 ); } if (this.graph) { - //apply transformations + // apply transformations ctx.save(); this.ds.toCanvasContext(ctx); - //draw nodes + // draw nodes var drawn_nodes = 0; var visible_nodes = this.computeVisibleNodes( null, - this.visible_nodes + this.visible_nodes, ); for (var i = 0; i < visible_nodes.length; ++i) { var node = visible_nodes[i]; - //transform coords system + // transform coords system ctx.save(); ctx.translate(node.pos[0], node.pos[1]); - //Draw + // Draw this.drawNode(node, ctx); drawn_nodes += 1; - //Restore + // Restore ctx.restore(); } - //on top (debug) + // on top (debug) if (this.render_execution_order) { this.drawExecutionOrder(ctx); } - //connections ontop? + // connections ontop? if (this.graph.config.links_ontop) { if (!this.live_mode) { this.drawConnections(ctx); } } - //current connection (the one being dragged by the mouse) + // current connection (the one being dragged by the mouse) if (this.connecting_pos != null) { ctx.lineWidth = this.connections_width; var link_color = null; - + var connInOrOut = this.connecting_output || this.connecting_input; var connType = connInOrOut.type; var connDir = connInOrOut.dir; - if(connDir == null) - { - if (this.connecting_output) - connDir = this.connecting_node.horizontal ? LiteGraph.DOWN : LiteGraph.RIGHT; - else - connDir = this.connecting_node.horizontal ? LiteGraph.UP : LiteGraph.LEFT; - } + if(connDir == null) { + if (this.connecting_output) + connDir = this.connecting_node.horizontal ? LiteGraph.DOWN : LiteGraph.RIGHT; + else + connDir = this.connecting_node.horizontal ? LiteGraph.UP : LiteGraph.LEFT; + } var connShape = connInOrOut.shape; - + switch (connType) { case LiteGraph.EVENT: link_color = LiteGraph.EVENT_LINK_COLOR; @@ -7969,7 +7876,7 @@ LGraphNode.prototype.executeAction = function(action) link_color = LiteGraph.CONNECTING_LINK_COLOR; } - //the connection being dragged by the mouse + // the connection being dragged by the mouse this.renderLink( ctx, this.connecting_pos, @@ -7979,7 +7886,7 @@ LGraphNode.prototype.executeAction = function(action) null, link_color, connDir, - LiteGraph.CENTER + LiteGraph.CENTER, ); ctx.beginPath(); @@ -7991,38 +7898,37 @@ LGraphNode.prototype.executeAction = function(action) this.connecting_pos[0] - 6 + 0.5, this.connecting_pos[1] - 5 + 0.5, 14, - 10 + 10, ); - ctx.fill(); - ctx.beginPath(); + ctx.fill(); + ctx.beginPath(); ctx.rect( this.graph_mouse[0] - 6 + 0.5, this.graph_mouse[1] - 5 + 0.5, 14, - 10 + 10, ); } else if (connShape === LiteGraph.ARROW_SHAPE) { ctx.moveTo(this.connecting_pos[0] + 8, this.connecting_pos[1] + 0.5); ctx.lineTo(this.connecting_pos[0] - 4, this.connecting_pos[1] + 6 + 0.5); ctx.lineTo(this.connecting_pos[0] - 4, this.connecting_pos[1] - 6 + 0.5); ctx.closePath(); - } - else { + } else { ctx.arc( this.connecting_pos[0], this.connecting_pos[1], 4, 0, - Math.PI * 2 + Math.PI * 2, ); - ctx.fill(); - ctx.beginPath(); + ctx.fill(); + ctx.beginPath(); ctx.arc( this.graph_mouse[0], this.graph_mouse[1], 4, 0, - Math.PI * 2 + Math.PI * 2, ); } ctx.fill(); @@ -8042,7 +7948,7 @@ LGraphNode.prototype.executeAction = function(action) this._highlight_input[1], 6, 0, - Math.PI * 2 + Math.PI * 2, ); } ctx.fill(); @@ -8060,32 +7966,32 @@ LGraphNode.prototype.executeAction = function(action) this._highlight_output[1], 6, 0, - Math.PI * 2 + Math.PI * 2, ); } ctx.fill(); } } - //the selection rectangle + // the selection rectangle if (this.dragging_rectangle) { ctx.strokeStyle = "#FFF"; ctx.strokeRect( this.dragging_rectangle[0], this.dragging_rectangle[1], this.dragging_rectangle[2], - this.dragging_rectangle[3] + this.dragging_rectangle[3], ); } - //on top of link center - if(this.over_link_center && this.render_link_tooltip) - this.drawLinkTooltip( ctx, this.over_link_center ); - else - if(this.onDrawLinkTooltip) //to remove - this.onDrawLinkTooltip(ctx,null); + // on top of link center + if(this.over_link_center && this.render_link_tooltip) + this.drawLinkTooltip( ctx, this.over_link_center ); + else + if(this.onDrawLinkTooltip) // to remove + this.onDrawLinkTooltip(ctx,null); - //custom info + // custom info if (this.onDrawForeground) { this.onDrawForeground(ctx, this.visible_rect); } @@ -8093,22 +7999,22 @@ LGraphNode.prototype.executeAction = function(action) ctx.restore(); } - //draws panel in the corner - if (this._graph_stack && this._graph_stack.length) { - this.drawSubgraphPanel( ctx ); - } + // draws panel in the corner + if (this._graph_stack && this._graph_stack.length) { + this.drawSubgraphPanel( ctx ); + } if (this.onDrawOverlay) { this.onDrawOverlay(ctx); } - if (area){ + if (area) { ctx.restore(); } if (ctx.finish2D) { - //this is a function I use in webgl renderer + // this is a function I use in webgl renderer ctx.finish2D(); } }; @@ -8159,7 +8065,7 @@ LGraphNode.prototype.executeAction = function(action) if (input.not_subgraph_input) continue; - //input button clicked + // input button clicked if (this.drawButton(20, y + 2, w - 20, h - 2)) { var type = subnode.constructor.input_node_type || "graph/input"; this.graph.beforeChange(); @@ -8176,8 +8082,7 @@ LGraphNode.prototype.executeAction = function(action) this.node_dragged.pos[0] = this.graph_mouse[0] - 5; this.node_dragged.pos[1] = this.graph_mouse[1] - 5; this.graph.afterChange(); - } - else + } else console.error("graph input node not found:", type); } ctx.fillStyle = "#9C9"; @@ -8191,7 +8096,7 @@ LGraphNode.prototype.executeAction = function(action) ctx.fillText(input.type, 130, y + h * 0.75); y += h; } - //add + button + // add + button if (this.drawButton(20, y + 2, w - 20, h - 2, "+", "#151515", "#222")) { this.showSubgraphPropertiesDialog(subnode); } @@ -8229,7 +8134,7 @@ LGraphNode.prototype.executeAction = function(action) if (output.not_subgraph_input) continue; - //output button clicked + // output button clicked if (this.drawButton(canvas_w - w, y + 2, w - 20, h - 2)) { var type = subnode.constructor.output_node_type || "graph/output"; this.graph.beforeChange(); @@ -8246,8 +8151,7 @@ LGraphNode.prototype.executeAction = function(action) this.node_dragged.pos[0] = this.graph_mouse[0] - 5; this.node_dragged.pos[1] = this.graph_mouse[1] - 5; this.graph.afterChange(); - } - else + } else console.error("graph input node not found:", type); } ctx.fillStyle = "#9C9"; @@ -8261,64 +8165,60 @@ LGraphNode.prototype.executeAction = function(action) ctx.fillText(output.type, canvas_w - w + 130, y + h * 0.75); y += h; } - //add + button + // add + button if (this.drawButton(canvas_w - w, y + 2, w - 20, h - 2, "+", "#151515", "#222")) { this.showSubgraphPropertiesDialogRight(subnode); } } - //Draws a button into the canvas overlay and computes if it was clicked using the immediate gui paradigm - LGraphCanvas.prototype.drawButton = function( x,y,w,h, text, bgcolor, hovercolor, textcolor ) - { - var ctx = this.ctx; - bgcolor = bgcolor || LiteGraph.NODE_DEFAULT_COLOR; - hovercolor = hovercolor || "#555"; - textcolor = textcolor || LiteGraph.NODE_TEXT_COLOR; - var pos = this.ds.convertOffsetToCanvas(this.graph_mouse); - var hover = LiteGraph.isInsideRectangle( pos[0], pos[1], x,y,w,h ); - pos = this.last_click_position ? [this.last_click_position[0], this.last_click_position[1]] : null; + // Draws a button into the canvas overlay and computes if it was clicked using the immediate gui paradigm + LGraphCanvas.prototype.drawButton = function( x,y,w,h, text, bgcolor, hovercolor, textcolor ) { + var ctx = this.ctx; + bgcolor = bgcolor || LiteGraph.NODE_DEFAULT_COLOR; + hovercolor = hovercolor || "#555"; + textcolor = textcolor || LiteGraph.NODE_TEXT_COLOR; + var pos = this.ds.convertOffsetToCanvas(this.graph_mouse); + var hover = LiteGraph.isInsideRectangle( pos[0], pos[1], x,y,w,h ); + pos = this.last_click_position ? [this.last_click_position[0], this.last_click_position[1]] : null; if(pos) { var rect = this.canvas.getBoundingClientRect(); pos[0] -= rect.left; pos[1] -= rect.top; } - var clicked = pos && LiteGraph.isInsideRectangle( pos[0], pos[1], x,y,w,h ); + var clicked = pos && LiteGraph.isInsideRectangle( pos[0], pos[1], x,y,w,h ); - ctx.fillStyle = hover ? hovercolor : bgcolor; - if(clicked) - ctx.fillStyle = "#AAA"; - ctx.beginPath(); - ctx.roundRect(x,y,w,h,[4] ); - ctx.fill(); + ctx.fillStyle = hover ? hovercolor : bgcolor; + if(clicked) + ctx.fillStyle = "#AAA"; + ctx.beginPath(); + ctx.roundRect(x,y,w,h,[4] ); + ctx.fill(); - if(text != null) - { - if(text.constructor == String) - { - ctx.fillStyle = textcolor; - ctx.textAlign = "center"; - ctx.font = ((h * 0.65)|0) + "px Arial"; - ctx.fillText( text, x + w * 0.5,y + h * 0.75 ); - ctx.textAlign = "left"; - } - } + if(text != null) { + if(text.constructor == String) { + ctx.fillStyle = textcolor; + ctx.textAlign = "center"; + ctx.font = ((h * 0.65)|0) + "px Arial"; + ctx.fillText( text, x + w * 0.5,y + h * 0.75 ); + ctx.textAlign = "left"; + } + } - var was_clicked = clicked && !this.block_click; - if(clicked) - this.blockClick(); - return was_clicked; - } + var was_clicked = clicked && !this.block_click; + if(clicked) + this.blockClick(); + return was_clicked; + } - LGraphCanvas.prototype.isAreaClicked = function( x,y,w,h, hold_click ) - { - var pos = this.mouse; - var hover = LiteGraph.isInsideRectangle( pos[0], pos[1], x,y,w,h ); - pos = this.last_click_position; - var clicked = pos && LiteGraph.isInsideRectangle( pos[0], pos[1], x,y,w,h ); - var was_clicked = clicked && !this.block_click; - if(clicked && hold_click) - this.blockClick(); - return was_clicked; - } + LGraphCanvas.prototype.isAreaClicked = function( x,y,w,h, hold_click ) { + var pos = this.mouse; + var hover = LiteGraph.isInsideRectangle( pos[0], pos[1], x,y,w,h ); + pos = this.last_click_position; + var clicked = pos && LiteGraph.isInsideRectangle( pos[0], pos[1], x,y,w,h ); + var was_clicked = clicked && !this.block_click; + if(clicked && hold_click) + this.blockClick(); + return was_clicked; + } /** * draws some useful stats in the corner of the canvas @@ -8333,7 +8233,7 @@ LGraphNode.prototype.executeAction = function(action) ctx.font = "10px Arial"; ctx.fillStyle = "#888"; - ctx.textAlign = "left"; + ctx.textAlign = "left"; if (this.graph) { ctx.fillText( "T: " + this.graph.globaltime.toFixed(2) + "s", 5, 13 * 1 ); ctx.fillText("I: " + this.graph.iteration, 5, 13 * 2 ); @@ -8368,14 +8268,14 @@ LGraphNode.prototype.executeAction = function(action) ctx.start(); } - var viewport = this.viewport || [0,0,ctx.canvas.width,ctx.canvas.height]; + var viewport = this.viewport || [0,0,ctx.canvas.width,ctx.canvas.height]; - //clear + // clear if (this.clear_background) { ctx.clearRect( viewport[0], viewport[1], viewport[2], viewport[3] ); } - //show subgraph stack header + // show subgraph stack header if (this._graph_stack && this._graph_stack.length) { ctx.save(); var parent_graph = this._graph_stack[this._graph_stack.length - 1]; @@ -8395,7 +8295,7 @@ LGraphNode.prototype.executeAction = function(action) ctx.fillText( title + subgraph_node.getTitle(), canvas.width * 0.5, - 40 + 40, ); ctx.restore(); } @@ -8405,28 +8305,26 @@ LGraphNode.prototype.executeAction = function(action) bg_already_painted = this.onRenderBackground(canvas, ctx); } - //reset in case of error - if ( !this.viewport ) - { - ctx.restore(); - ctx.setTransform(1, 0, 0, 1, 0, 0); - } + // reset in case of error + if ( !this.viewport ) { + ctx.restore(); + ctx.setTransform(1, 0, 0, 1, 0, 0); + } this.visible_links.length = 0; if (this.graph) { - //apply transformations + // apply transformations ctx.save(); this.ds.toCanvasContext(ctx); - //render BG - if ( this.ds.scale < 1.5 && !bg_already_painted && this.clear_background_color ) - { + // render BG + if ( this.ds.scale < 1.5 && !bg_already_painted && this.clear_background_color ) { ctx.fillStyle = this.clear_background_color; ctx.fillRect( this.visible_area[0], this.visible_area[1], this.visible_area[2], - this.visible_area[3] + this.visible_area[3], ); } @@ -8441,7 +8339,7 @@ LGraphNode.prototype.executeAction = function(action) } else { ctx.globalAlpha = this.editor_alpha; } - ctx.imageSmoothingEnabled = ctx.imageSmoothingEnabled = false; // ctx.mozImageSmoothingEnabled = + ctx.imageSmoothingEnabled = ctx.imageSmoothingEnabled = false; // ctx.mozImageSmoothingEnabled = if ( !this._bg_img || this._bg_img.name != this.background_image @@ -8469,16 +8367,16 @@ LGraphNode.prototype.executeAction = function(action) this.visible_area[0], this.visible_area[1], this.visible_area[2], - this.visible_area[3] + this.visible_area[3], ); ctx.fillStyle = "transparent"; } ctx.globalAlpha = 1.0; - ctx.imageSmoothingEnabled = ctx.imageSmoothingEnabled = true; //= ctx.mozImageSmoothingEnabled + ctx.imageSmoothingEnabled = ctx.imageSmoothingEnabled = true; // = ctx.mozImageSmoothingEnabled } - //groups + // groups if (this.graph._groups.length && !this.live_mode) { this.drawGroups(canvas, ctx); } @@ -8487,18 +8385,16 @@ LGraphNode.prototype.executeAction = function(action) this.onDrawBackground(ctx, this.visible_area); } if (this.onBackgroundRender) { - //LEGACY - console.error( - "WARNING! onBackgroundRender deprecated, now is named onDrawBackground " - ); + // LEGACY + console.error("WARNING! onBackgroundRender deprecated, now is named onDrawBackground "); this.onBackgroundRender = null; } - //DEBUG: show clipping area - //ctx.fillStyle = "red"; - //ctx.fillRect( this.visible_area[0] + 10, this.visible_area[1] + 10, this.visible_area[2] - 20, this.visible_area[3] - 20); + // DEBUG: show clipping area + // ctx.fillStyle = "red"; + // ctx.fillRect( this.visible_area[0] + 10, this.visible_area[1] + 10, this.visible_area[2] - 20, this.visible_area[3] - 20); - //bg + // bg if (this.render_canvas_border) { ctx.strokeStyle = "#235"; ctx.strokeRect(0, 0, canvas.width, canvas.height); @@ -8513,14 +8409,14 @@ LGraphNode.prototype.executeAction = function(action) ctx.shadowColor = "rgba(0,0,0,0)"; } - //draw connections + // draw connections if (!this.live_mode) { this.drawConnections(ctx); } ctx.shadowColor = "rgba(0,0,0,0)"; - //restore state + // restore state ctx.restore(); } @@ -8529,7 +8425,7 @@ LGraphNode.prototype.executeAction = function(action) } this.dirty_bgcanvas = false; - this.dirty_canvas = true; //to force to repaint the front canvas with the bgcanvas + this.dirty_canvas = true; // to force to repaint the front canvas with the bgcanvas }; var temp_vec2 = new Float32Array(2); @@ -8545,14 +8441,14 @@ LGraphNode.prototype.executeAction = function(action) var color = node.color || node.constructor.color || LiteGraph.NODE_DEFAULT_COLOR; var bgcolor = node.bgcolor || node.constructor.bgcolor || LiteGraph.NODE_DEFAULT_BGCOLOR; - //shadow and glow + // shadow and glow if (node.mouseOver) { glow = true; } - var low_quality = this.ds.scale < 0.6; //zoomed out + var low_quality = this.ds.scale < 0.6; // zoomed out - //only render if it forces it to do it + // only render if it forces it to do it if (this.live_mode) { if (!node.flags.collapsed) { ctx.shadowColor = "transparent"; @@ -8575,7 +8471,7 @@ LGraphNode.prototype.executeAction = function(action) ctx.shadowColor = "transparent"; } - //custom draw collapsed method (draw after shadows because they are affected) + // custom draw collapsed method (draw after shadows because they are affected) if ( node.flags.collapsed && node.onDrawCollapsed && @@ -8584,7 +8480,7 @@ LGraphNode.prototype.executeAction = function(action) return; } - //clip if required (mask) + // clip if required (mask) var shape = node._shape || LiteGraph.BOX_SHAPE; var size = temp_vec2; temp_vec2.set(node.size); @@ -8597,15 +8493,15 @@ LGraphNode.prototype.executeAction = function(action) node._collapsed_width = Math.min( node.size[0], ctx.measureText(title).width + - LiteGraph.NODE_TITLE_HEIGHT * 2 - ); //LiteGraph.NODE_COLLAPSED_WIDTH; + LiteGraph.NODE_TITLE_HEIGHT * 2, + ); // LiteGraph.NODE_COLLAPSED_WIDTH; size[0] = node._collapsed_width; size[1] = 0; } } if (node.clip_area) { - //Start clipping + // Start clipping ctx.save(); ctx.beginPath(); if (shape == LiteGraph.BOX_SHAPE) { @@ -8618,13 +8514,13 @@ LGraphNode.prototype.executeAction = function(action) size[1] * 0.5, size[0] * 0.5, 0, - Math.PI * 2 + Math.PI * 2, ); } ctx.clip(); } - //draw shape + // draw shape if (node.has_errors) { bgcolor = "red"; } @@ -8635,16 +8531,16 @@ LGraphNode.prototype.executeAction = function(action) color, bgcolor, node.is_selected, - node.mouseOver + node.mouseOver, ); ctx.shadowColor = "transparent"; - //draw foreground + // draw foreground if (node.onDrawForeground) { node.onDrawForeground(ctx, this, this.canvas); } - //connection slots + // connection slots ctx.textAlign = horizontal ? "center" : "left"; ctx.font = this.inner_text_font; @@ -8655,20 +8551,20 @@ LGraphNode.prototype.executeAction = function(action) ctx.lineWidth = 1; var max_y = 0; - var slot_pos = new Float32Array(2); //to reuse + var slot_pos = new Float32Array(2); // to reuse - //render inputs and outputs + // render inputs and outputs if (!node.flags.collapsed) { - //input connection slots + // input connection slots if (node.inputs) { for (var i = 0; i < node.inputs.length; i++) { var slot = node.inputs[i]; - + var slot_type = slot.type; var slot_shape = slot.shape; - + ctx.globalAlpha = editor_alpha; - //change opacity of incompatible slots when dragging a connection + // change opacity of incompatible slots when dragging a connection if ( this.connecting_output && !LiteGraph.isValidConnection( slot.type , out_slot.type) ) { ctx.globalAlpha = 0.4 * editor_alpha; } @@ -8692,12 +8588,12 @@ LGraphNode.prototype.executeAction = function(action) ctx.beginPath(); - if (slot_type == "array"){ + if (slot_type == "array") { slot_shape = LiteGraph.GRID_SHAPE; // place in addInput? addOutput instead? } - + var doStroke = true; - + if ( slot.type === LiteGraph.EVENT || slot.shape === LiteGraph.BOX_SHAPE @@ -8707,14 +8603,14 @@ LGraphNode.prototype.executeAction = function(action) pos[0] - 5 + 0.5, pos[1] - 8 + 0.5, 10, - 14 + 14, ); } else { ctx.rect( pos[0] - 6 + 0.5, pos[1] - 5 + 0.5, 14, - 10 + 10, ); } } else if (slot_shape === LiteGraph.ARROW_SHAPE) { @@ -8734,14 +8630,14 @@ LGraphNode.prototype.executeAction = function(action) ctx.rect(pos[0] + 2, pos[1] + 2, 2, 2); doStroke = false; } else { - if(low_quality) - ctx.rect(pos[0] - 4, pos[1] - 4, 8, 8 ); //faster - else - ctx.arc(pos[0], pos[1], 4, 0, Math.PI * 2); + if(low_quality) + ctx.rect(pos[0] - 4, pos[1] - 4, 8, 8 ); // faster + else + ctx.arc(pos[0], pos[1], 4, 0, Math.PI * 2); } ctx.fill(); - //render name + // render name if (render_text) { var text = slot.label != null ? slot.label : slot.name; if (text) { @@ -8756,22 +8652,22 @@ LGraphNode.prototype.executeAction = function(action) } } - //output connection slots + // output connection slots ctx.textAlign = horizontal ? "center" : "right"; ctx.strokeStyle = "black"; if (node.outputs) { for (var i = 0; i < node.outputs.length; i++) { var slot = node.outputs[i]; - + var slot_type = slot.type; var slot_shape = slot.shape; - - //change opacity of incompatible slots when dragging a connection + + // change opacity of incompatible slots when dragging a connection if (this.connecting_input && !LiteGraph.isValidConnection( slot_type , in_slot.type) ) { ctx.globalAlpha = 0.4 * editor_alpha; } - + var pos = node.getConnectionPos(false, i, slot_pos); pos[0] -= node.pos[0]; pos[1] -= node.pos[1]; @@ -8789,14 +8685,14 @@ LGraphNode.prototype.executeAction = function(action) this.default_connection_color_byType[slot_type] || this.default_connection_color.output_off; ctx.beginPath(); - //ctx.rect( node.size[0] - 14,i*14,10,10); + // ctx.rect( node.size[0] - 14,i*14,10,10); - if (slot_type == "array"){ + if (slot_type == "array") { slot_shape = LiteGraph.GRID_SHAPE; } - + var doStroke = true; - + if ( slot_type === LiteGraph.EVENT || slot_shape === LiteGraph.BOX_SHAPE @@ -8806,14 +8702,14 @@ LGraphNode.prototype.executeAction = function(action) pos[0] - 5 + 0.5, pos[1] - 8 + 0.5, 10, - 14 + 14, ); } else { ctx.rect( pos[0] - 6 + 0.5, pos[1] - 5 + 0.5, 14, - 10 + 10, ); } } else if (slot_shape === LiteGraph.ARROW_SHAPE) { @@ -8821,7 +8717,7 @@ LGraphNode.prototype.executeAction = function(action) ctx.lineTo(pos[0] - 4, pos[1] + 6 + 0.5); ctx.lineTo(pos[0] - 4, pos[1] - 6 + 0.5); ctx.closePath(); - } else if (slot_shape === LiteGraph.GRID_SHAPE) { + } else if (slot_shape === LiteGraph.GRID_SHAPE) { ctx.rect(pos[0] - 4, pos[1] - 4, 2, 2); ctx.rect(pos[0] - 1, pos[1] - 4, 2, 2); ctx.rect(pos[0] + 2, pos[1] - 4, 2, 2); @@ -8833,22 +8729,22 @@ LGraphNode.prototype.executeAction = function(action) ctx.rect(pos[0] + 2, pos[1] + 2, 2, 2); doStroke = false; } else { - if(low_quality) - ctx.rect(pos[0] - 4, pos[1] - 4, 8, 8 ); - else - ctx.arc(pos[0], pos[1], 4, 0, Math.PI * 2); + if(low_quality) + ctx.rect(pos[0] - 4, pos[1] - 4, 8, 8 ); + else + ctx.arc(pos[0], pos[1], 4, 0, Math.PI * 2); } - //trigger - //if(slot.node_id != null && slot.slot == -1) + // trigger + // if(slot.node_id != null && slot.slot == -1) // ctx.fillStyle = "#F85"; - //if(slot.links != null && slot.links.length) + // if(slot.links != null && slot.links.length) ctx.fill(); - if(!low_quality && doStroke) - ctx.stroke(); + if(!low_quality && doStroke) + ctx.stroke(); - //render output name + // render output name if (render_text) { var text = slot.label != null ? slot.label : slot.name; if (text) { @@ -8867,11 +8763,11 @@ LGraphNode.prototype.executeAction = function(action) ctx.globalAlpha = 1; if (node.widgets) { - var widgets_y = max_y; + var widgets_y = max_y; if (horizontal || node.widgets_up) { widgets_y = 2; } - if( node.widgets_start_y != null ) + if( node.widgets_start_y != null ) widgets_y = node.widgets_start_y; this.drawNodeWidgets( node, @@ -8879,15 +8775,15 @@ LGraphNode.prototype.executeAction = function(action) ctx, this.node_widget && this.node_widget[0] == node ? this.node_widget[1] - : null + : null, ); } } else if (this.render_collapsed_slots) { - //if collapsed + // if collapsed var input_slot = null; var output_slot = null; - //get first connected slot to render + // get first connected slot to render if (node.inputs) { for (var i = 0; i < node.inputs.length; i++) { var slot = node.inputs[i]; @@ -8910,7 +8806,7 @@ LGraphNode.prototype.executeAction = function(action) if (input_slot) { var x = 0; - var y = LiteGraph.NODE_TITLE_HEIGHT * -0.5; //center + var y = LiteGraph.NODE_TITLE_HEIGHT * -0.5; // center if (horizontal) { x = node._collapsed_width * 0.5; y = -LiteGraph.NODE_TITLE_HEIGHT; @@ -8935,7 +8831,7 @@ LGraphNode.prototype.executeAction = function(action) if (output_slot) { var x = node._collapsed_width; - var y = LiteGraph.NODE_TITLE_HEIGHT * -0.5; //center + var y = LiteGraph.NODE_TITLE_HEIGHT * -0.5; // center if (horizontal) { x = node._collapsed_width * 0.5; y = 0; @@ -8957,7 +8853,7 @@ LGraphNode.prototype.executeAction = function(action) ctx.arc(x, y, 4, 0, Math.PI * 2); } ctx.fill(); - //ctx.stroke(); + // ctx.stroke(); } } @@ -8968,60 +8864,59 @@ LGraphNode.prototype.executeAction = function(action) ctx.globalAlpha = 1.0; }; - //used by this.over_link_center - LGraphCanvas.prototype.drawLinkTooltip = function( ctx, link ) - { - var pos = link._pos; - ctx.fillStyle = "black"; - ctx.beginPath(); - ctx.arc( pos[0], pos[1], 3, 0, Math.PI * 2 ); - ctx.fill(); + // used by this.over_link_center + LGraphCanvas.prototype.drawLinkTooltip = function( ctx, link ) { + var pos = link._pos; + ctx.fillStyle = "black"; + ctx.beginPath(); + ctx.arc( pos[0], pos[1], 3, 0, Math.PI * 2 ); + ctx.fill(); - if(link.data == null) - return; + if(link.data == null) + return; - if(this.onDrawLinkTooltip) - if( this.onDrawLinkTooltip(ctx,link,this) == true ) - return; - - var data = link.data; - var text = null; - - if( data.constructor === Number ) - text = data.toFixed(2); - else if( data.constructor === String ) - text = "\"" + data + "\""; - else if( data.constructor === Boolean ) - text = String(data); - else if (data.toToolTip) - text = data.toToolTip(); - else - text = "[" + data.constructor.name + "]"; + if(this.onDrawLinkTooltip) + if( this.onDrawLinkTooltip(ctx,link,this) == true ) + return; - if(text == null) - return; - text = text.substr(0,30); //avoid weird - - ctx.font = "14px Courier New"; - var info = ctx.measureText(text); - var w = info.width + 20; - var h = 24; - ctx.shadowColor = "black"; - ctx.shadowOffsetX = 2; - ctx.shadowOffsetY = 2; - ctx.shadowBlur = 3; - ctx.fillStyle = "#454"; - ctx.beginPath(); - ctx.roundRect( pos[0] - w*0.5, pos[1] - 15 - h, w, h, [3]); - ctx.moveTo( pos[0] - 10, pos[1] - 15 ); - ctx.lineTo( pos[0] + 10, pos[1] - 15 ); - ctx.lineTo( pos[0], pos[1] - 5 ); - ctx.fill(); + var data = link.data; + var text = null; + + if( data.constructor === Number ) + text = data.toFixed(2); + else if( data.constructor === String ) + text = "\"" + data + "\""; + else if( data.constructor === Boolean ) + text = String(data); + else if (data.toToolTip) + text = data.toToolTip(); + else + text = "[" + data.constructor.name + "]"; + + if(text == null) + return; + text = text.substr(0,30); // avoid weird + + ctx.font = "14px Courier New"; + var info = ctx.measureText(text); + var w = info.width + 20; + var h = 24; + ctx.shadowColor = "black"; + ctx.shadowOffsetX = 2; + ctx.shadowOffsetY = 2; + ctx.shadowBlur = 3; + ctx.fillStyle = "#454"; + ctx.beginPath(); + ctx.roundRect( pos[0] - w*0.5, pos[1] - 15 - h, w, h, [3]); + ctx.moveTo( pos[0] - 10, pos[1] - 15 ); + ctx.lineTo( pos[0] + 10, pos[1] - 15 ); + ctx.lineTo( pos[0], pos[1] - 5 ); + ctx.fill(); ctx.shadowColor = "transparent"; - ctx.textAlign = "center"; - ctx.fillStyle = "#CEC"; - ctx.fillText(text, pos[0], pos[1] - 15 - h * 0.3); - } + ctx.textAlign = "center"; + ctx.fillStyle = "#CEC"; + ctx.fillText(text, pos[0], pos[1] - 15 - h * 0.3); + } /** * draws the shape of the given node in the canvas @@ -9036,16 +8931,16 @@ LGraphNode.prototype.executeAction = function(action) fgcolor, bgcolor, selected, - mouse_over + mouse_over, ) { - //bg rect + // bg rect ctx.strokeStyle = fgcolor; ctx.fillStyle = bgcolor; var title_height = LiteGraph.NODE_TITLE_HEIGHT; var low_quality = this.ds.scale < 0.5; - //render node area depending on shape + // render node area depending on shape var shape = node._shape || node.constructor.shape || LiteGraph.ROUND_SHAPE; @@ -9059,15 +8954,15 @@ LGraphNode.prototype.executeAction = function(action) } var area = tmp_area; - area[0] = 0; //x - area[1] = render_title ? -title_height : 0; //y - area[2] = size[0] + 1; //w - area[3] = render_title ? size[1] + title_height : size[1]; //h + area[0] = 0; // x + area[1] = render_title ? -title_height : 0; // y + area[2] = size[0] + 1; // w + area[3] = render_title ? size[1] + title_height : size[1]; // h var old_alpha = ctx.globalAlpha; - //full node shape - //if(node.flags.collapsed) + // full node shape + // if(node.flags.collapsed) { ctx.beginPath(); if (shape == LiteGraph.BOX_SHAPE || low_quality) { @@ -9081,7 +8976,7 @@ LGraphNode.prototype.executeAction = function(action) area[1], area[2], area[3], - shape == LiteGraph.CARD_SHAPE ? [this.round_radius,this.round_radius,0,0] : [this.round_radius] + shape == LiteGraph.CARD_SHAPE ? [this.round_radius,this.round_radius,0,0] : [this.round_radius], ); } else if (shape == LiteGraph.CIRCLE_SHAPE) { ctx.arc( @@ -9089,18 +8984,17 @@ LGraphNode.prototype.executeAction = function(action) size[1] * 0.5, size[0] * 0.5, 0, - Math.PI * 2 + Math.PI * 2, ); } ctx.fill(); - //separator - if(!node.flags.collapsed && render_title) - { - ctx.shadowColor = "transparent"; - ctx.fillStyle = "rgba(0,0,0,0.2)"; - ctx.fillRect(0, -1, area[2], 2); - } + // separator + if(!node.flags.collapsed && render_title) { + ctx.shadowColor = "transparent"; + ctx.fillStyle = "rgba(0,0,0,0.2)"; + ctx.fillRect(0, -1, area[2], 2); + } } ctx.shadowColor = "transparent"; @@ -9108,9 +9002,9 @@ LGraphNode.prototype.executeAction = function(action) node.onDrawBackground(ctx, this, this.canvas, this.graph_mouse ); } - //title bg (remember, it is rendered ABOVE the node) + // title bg (remember, it is rendered ABOVE the node) if (render_title || title_mode == LiteGraph.TRANSPARENT_TITLE) { - //title bar + // title bar if (node.onDrawTitleBar) { node.onDrawTitleBar( ctx, title_height, size, this.ds.scale, fgcolor ); } else if ( @@ -9127,7 +9021,7 @@ LGraphNode.prototype.executeAction = function(action) if (this.use_gradients) { var grad = LGraphCanvas.gradients[title_color]; if (!grad) { - grad = LGraphCanvas.gradients[ title_color ] = ctx.createLinearGradient(0, 0, 400, 0); + grad = LGraphCanvas.gradients[title_color] = ctx.createLinearGradient(0, 0, 400, 0); grad.addColorStop(0, title_color); // TODO refactor: validate color !! prevent DOMException grad.addColorStop(1, "#000"); } @@ -9136,17 +9030,17 @@ LGraphNode.prototype.executeAction = function(action) ctx.fillStyle = title_color; } - //ctx.globalAlpha = 0.5 * old_alpha; + // ctx.globalAlpha = 0.5 * old_alpha; ctx.beginPath(); if (shape == LiteGraph.BOX_SHAPE || low_quality) { ctx.rect(0, -title_height, size[0] + 1, title_height); - } else if ( shape == LiteGraph.ROUND_SHAPE || shape == LiteGraph.CARD_SHAPE ) { + } else if ( shape == LiteGraph.ROUND_SHAPE || shape == LiteGraph.CARD_SHAPE ) { ctx.roundRect( 0, -title_height, size[0] + 1, title_height, - node.flags.collapsed ? [this.round_radius] : [this.round_radius,this.round_radius,0,0] + node.flags.collapsed ? [this.round_radius] : [this.round_radius,this.round_radius,0,0], ); } ctx.fill(); @@ -9154,16 +9048,16 @@ LGraphNode.prototype.executeAction = function(action) } var colState = false; - if (LiteGraph.node_box_coloured_by_mode){ - if(LiteGraph.NODE_MODES_COLORS[node.mode]){ + if (LiteGraph.node_box_coloured_by_mode) { + if(LiteGraph.NODE_MODES_COLORS[node.mode]) { colState = LiteGraph.NODE_MODES_COLORS[node.mode]; } } - if (LiteGraph.node_box_coloured_when_on){ + if (LiteGraph.node_box_coloured_when_on) { colState = node.action_triggered ? "#FFF" : (node.execute_triggered ? "#AAA" : colState); } - - //title box + + // title box var box_size = 10; if (node.onDrawTitleBox) { node.onDrawTitleBox(ctx, title_height, size, this.ds.scale); @@ -9180,26 +9074,25 @@ LGraphNode.prototype.executeAction = function(action) title_height * -0.5, box_size * 0.5 + 1, 0, - Math.PI * 2 + Math.PI * 2, ); ctx.fill(); } - + ctx.fillStyle = node.boxcolor || colState || LiteGraph.NODE_DEFAULT_BOXCOLOR; - if(low_quality) - ctx.fillRect( title_height * 0.5 - box_size *0.5, title_height * -0.5 - box_size *0.5, box_size , box_size ); - else - { - ctx.beginPath(); - ctx.arc( - title_height * 0.5, - title_height * -0.5, - box_size * 0.5, - 0, - Math.PI * 2 - ); - ctx.fill(); - } + if(low_quality) + ctx.fillRect( title_height * 0.5 - box_size *0.5, title_height * -0.5 - box_size *0.5, box_size , box_size ); + else { + ctx.beginPath(); + ctx.arc( + title_height * 0.5, + title_height * -0.5, + box_size * 0.5, + 0, + Math.PI * 2, + ); + ctx.fill(); + } } else { if (low_quality) { ctx.fillStyle = "black"; @@ -9207,7 +9100,7 @@ LGraphNode.prototype.executeAction = function(action) (title_height - box_size) * 0.5 - 1, (title_height + box_size) * -0.5 - 1, box_size + 2, - box_size + 2 + box_size + 2, ); } ctx.fillStyle = node.boxcolor || colState || LiteGraph.NODE_DEFAULT_BOXCOLOR; @@ -9215,12 +9108,12 @@ LGraphNode.prototype.executeAction = function(action) (title_height - box_size) * 0.5, (title_height + box_size) * -0.5, box_size, - box_size + box_size, ); } ctx.globalAlpha = old_alpha; - //title text + // title text if (node.onDrawTitleText) { node.onDrawTitleText( ctx, @@ -9228,7 +9121,7 @@ LGraphNode.prototype.executeAction = function(action) size, this.ds.scale, this.title_text_font, - selected + selected, ); } if (!low_quality) { @@ -9246,9 +9139,9 @@ LGraphNode.prototype.executeAction = function(action) ctx.textAlign = "left"; var measure = ctx.measureText(title); ctx.fillText( - title.substr(0,20), //avoid urls too long + title.substr(0,20), // avoid urls too long title_height,// + measure.width * 0.5, - LiteGraph.NODE_TITLE_TEXT_Y - title_height + LiteGraph.NODE_TITLE_TEXT_Y - title_height, ); ctx.textAlign = "left"; } else { @@ -9256,41 +9149,40 @@ LGraphNode.prototype.executeAction = function(action) ctx.fillText( title, title_height, - LiteGraph.NODE_TITLE_TEXT_Y - title_height + LiteGraph.NODE_TITLE_TEXT_Y - title_height, ); } } } - //subgraph box - if (!node.flags.collapsed && node.subgraph && !node.skip_subgraph_button) { - var w = LiteGraph.NODE_TITLE_HEIGHT; - var x = node.size[0] - w; - var over = LiteGraph.isInsideRectangle( this.graph_mouse[0] - node.pos[0], this.graph_mouse[1] - node.pos[1], x+2, -w+2, w-4, w-4 ); - ctx.fillStyle = over ? "#888" : "#555"; - if( shape == LiteGraph.BOX_SHAPE || low_quality) - ctx.fillRect(x+2, -w+2, w-4, w-4); - else - { - ctx.beginPath(); - ctx.roundRect(x+2, -w+2, w-4, w-4,[4]); - ctx.fill(); - } - ctx.fillStyle = "#333"; - ctx.beginPath(); - ctx.moveTo(x + w * 0.2, -w * 0.6); - ctx.lineTo(x + w * 0.8, -w * 0.6); - ctx.lineTo(x + w * 0.5, -w * 0.3); - ctx.fill(); - } + // subgraph box + if (!node.flags.collapsed && node.subgraph && !node.skip_subgraph_button) { + var w = LiteGraph.NODE_TITLE_HEIGHT; + var x = node.size[0] - w; + var over = LiteGraph.isInsideRectangle( this.graph_mouse[0] - node.pos[0], this.graph_mouse[1] - node.pos[1], x+2, -w+2, w-4, w-4 ); + ctx.fillStyle = over ? "#888" : "#555"; + if( shape == LiteGraph.BOX_SHAPE || low_quality) + ctx.fillRect(x+2, -w+2, w-4, w-4); + else { + ctx.beginPath(); + ctx.roundRect(x+2, -w+2, w-4, w-4,[4]); + ctx.fill(); + } + ctx.fillStyle = "#333"; + ctx.beginPath(); + ctx.moveTo(x + w * 0.2, -w * 0.6); + ctx.lineTo(x + w * 0.8, -w * 0.6); + ctx.lineTo(x + w * 0.5, -w * 0.3); + ctx.fill(); + } - //custom title render + // custom title render if (node.onDrawTitle) { node.onDrawTitle(ctx); } } - //render selection marker + // render selection marker if (selected) { if (node.onBounding) { node.onBounding(area); @@ -9308,7 +9200,7 @@ LGraphNode.prototype.executeAction = function(action) -6 + area[0], -6 + area[1], 12 + area[2], - 12 + area[3] + 12 + area[3], ); } else if ( shape == LiteGraph.ROUND_SHAPE || @@ -9319,7 +9211,7 @@ LGraphNode.prototype.executeAction = function(action) -6 + area[1], 12 + area[2], 12 + area[3], - [this.round_radius * 2] + [this.round_radius * 2], ); } else if (shape == LiteGraph.CARD_SHAPE) { ctx.roundRect( @@ -9327,7 +9219,7 @@ LGraphNode.prototype.executeAction = function(action) -6 + area[1], 12 + area[2], 12 + area[3], - [this.round_radius * 2,2,this.round_radius * 2,2] + [this.round_radius * 2,2,this.round_radius * 2,2], ); } else if (shape == LiteGraph.CIRCLE_SHAPE) { ctx.arc( @@ -9335,7 +9227,7 @@ LGraphNode.prototype.executeAction = function(action) size[1] * 0.5, size[0] * 0.5 + 6, 0, - Math.PI * 2 + Math.PI * 2, ); } ctx.strokeStyle = LiteGraph.NODE_BOX_OUTLINE_COLOR; @@ -9343,7 +9235,7 @@ LGraphNode.prototype.executeAction = function(action) ctx.strokeStyle = fgcolor; ctx.globalAlpha = 1; } - + // these counter helps in conditioning drawing based on if the node has been executed or an action occurred if (node.execute_triggered>0) node.execute_triggered--; if (node.action_triggered>0) node.action_triggered--; @@ -9367,17 +9259,17 @@ LGraphNode.prototype.executeAction = function(action) margin_area[2] = visible_area[2] + 40; margin_area[3] = visible_area[3] + 40; - //draw connections + // draw connections ctx.lineWidth = this.connections_width; ctx.fillStyle = "#AAA"; ctx.strokeStyle = "#AAA"; ctx.globalAlpha = this.editor_alpha; - //for every node + // for every node var nodes = this.graph._nodes; for (var n = 0, l = nodes.length; n < l; ++n) { var node = nodes[n]; - //for every input (we render just inputs because it is easier as every slot can only have one input) + // for every input (we render just inputs because it is easier as every slot can only have one input) if (!node.inputs || !node.inputs.length) { continue; } @@ -9393,7 +9285,7 @@ LGraphNode.prototype.executeAction = function(action) continue; } - //find link info + // find link info var start_node = this.graph.getNodeById(link.origin_id); if (start_node == null) { continue; @@ -9403,18 +9295,18 @@ LGraphNode.prototype.executeAction = function(action) if (start_node_slot == -1) { start_node_slotpos = [ start_node.pos[0] + 10, - start_node.pos[1] + 10 + start_node.pos[1] + 10, ]; } else { start_node_slotpos = start_node.getConnectionPos( false, start_node_slot, - tempA + tempA, ); } var end_node_slotpos = node.getConnectionPos(true, i, tempB); - //compute link bounding + // compute link bounding link_bounding[0] = start_node_slotpos[0]; link_bounding[1] = start_node_slotpos[1]; link_bounding[2] = end_node_slotpos[0] - start_node_slotpos[0]; @@ -9428,7 +9320,7 @@ LGraphNode.prototype.executeAction = function(action) link_bounding[3] = Math.abs(link_bounding[3]); } - //skip links outside of the visible area of the canvas + // skip links outside of the visible area of the canvas if (!overlapBounding(link_bounding, margin_area)) { continue; } @@ -9454,10 +9346,10 @@ LGraphNode.prototype.executeAction = function(action) 0, null, start_dir, - end_dir + end_dir, ); - //event triggered rendered on top + // event triggered rendered on top if (link && link._last_time && now - link._last_time < 1000) { var f = 2.0 - (now - link._last_time) * 0.002; var tmp = ctx.globalAlpha; @@ -9471,7 +9363,7 @@ LGraphNode.prototype.executeAction = function(action) f, "white", start_dir, - end_dir + end_dir, ); ctx.globalAlpha = tmp; } @@ -9503,13 +9395,13 @@ LGraphNode.prototype.executeAction = function(action) color, start_dir, end_dir, - num_sublines + num_sublines, ) { if (link) { this.visible_links.push(link); } - //choose color + // choose color if (!color && link) { color = link.color || LGraphCanvas.link_type_colors[link.type]; } @@ -9534,7 +9426,7 @@ LGraphNode.prototype.executeAction = function(action) ctx.lineWidth = 0.5; } - //begin line shape + // begin line shape ctx.beginPath(); for (var i = 0; i < num_sublines; i += 1) { var offsety = (i - (num_sublines - 1) * 0.5) * 5; @@ -9579,7 +9471,7 @@ LGraphNode.prototype.executeAction = function(action) b[0] + end_offset_x, b[1] + end_offset_y + offsety, b[0], - b[1] + offsety + b[1] + offsety, ); } else if (this.links_render_mode == LiteGraph.LINEAR_LINK) { ctx.moveTo(a[0], a[1] + offsety); @@ -9618,11 +9510,11 @@ LGraphNode.prototype.executeAction = function(action) var l = 15; ctx.lineTo( a[0] + start_offset_x * l, - a[1] + start_offset_y * l + offsety + a[1] + start_offset_y * l + offsety, ); ctx.lineTo( b[0] + end_offset_x * l, - b[1] + end_offset_y * l + offsety + b[1] + end_offset_y * l + offsety, ); ctx.lineTo(b[0], b[1] + offsety); } else if (this.links_render_mode == LiteGraph.STRAIGHT_LINK) { @@ -9648,10 +9540,10 @@ LGraphNode.prototype.executeAction = function(action) ctx.lineTo(b[0], b[1]); } else { return; - } //unknown + } // unknown } - //rendering the outline of the connection can be a little bit slow + // rendering the outline of the connection can be a little bit slow if ( this.render_connections_border && this.ds.scale > 0.6 && @@ -9664,7 +9556,7 @@ LGraphNode.prototype.executeAction = function(action) ctx.lineWidth = this.connections_width; ctx.fillStyle = ctx.strokeStyle = color; ctx.stroke(); - //end line shape + // end line shape var pos = this.computeConnectionPoint(a, b, 0.5, start_dir, end_dir); if (link && link._pos) { @@ -9672,45 +9564,45 @@ LGraphNode.prototype.executeAction = function(action) link._pos[1] = pos[1]; } - //render arrow in the middle + // render arrow in the middle if ( this.ds.scale >= 0.6 && this.highquality_render && end_dir != LiteGraph.CENTER ) { - //render arrow + // render arrow if (this.render_connection_arrows) { - //compute two points in the connection + // compute two points in the connection var posA = this.computeConnectionPoint( a, b, 0.25, start_dir, - end_dir + end_dir, ); var posB = this.computeConnectionPoint( a, b, 0.26, start_dir, - end_dir + end_dir, ); var posC = this.computeConnectionPoint( a, b, 0.75, start_dir, - end_dir + end_dir, ); var posD = this.computeConnectionPoint( a, b, 0.76, start_dir, - end_dir + end_dir, ); - //compute the angle between them so the arrow points in the right direction + // compute the angle between them so the arrow points in the right direction var angleA = 0; var angleB = 0; if (this.render_curved_connections) { @@ -9720,7 +9612,7 @@ LGraphNode.prototype.executeAction = function(action) angleB = angleA = b[1] > a[1] ? 0 : Math.PI; } - //render arrow + // render arrow ctx.save(); ctx.translate(posA[0], posA[1]); ctx.rotate(angleA); @@ -9741,13 +9633,13 @@ LGraphNode.prototype.executeAction = function(action) ctx.restore(); } - //circle + // circle ctx.beginPath(); ctx.arc(pos[0], pos[1], 5, 0, Math.PI * 2); ctx.fill(); } - //render flowing points + // render flowing points if (flow) { ctx.fillStyle = color; for (var i = 0; i < 5; ++i) { @@ -9757,7 +9649,7 @@ LGraphNode.prototype.executeAction = function(action) b, f, start_dir, - end_dir + end_dir, ); ctx.beginPath(); ctx.arc(pos[0], pos[1], 5, 0, 2 * Math.PI); @@ -9766,13 +9658,13 @@ LGraphNode.prototype.executeAction = function(action) } }; - //returns the link center point based on curvature + // returns the link center point based on curvature LGraphCanvas.prototype.computeConnectionPoint = function( a, b, t, start_dir, - end_dir + end_dir, ) { start_dir = start_dir || LiteGraph.RIGHT; end_dir = end_dir || LiteGraph.LEFT; @@ -9838,21 +9730,21 @@ LGraphNode.prototype.executeAction = function(action) node.pos[0] - LiteGraph.NODE_TITLE_HEIGHT, node.pos[1] - LiteGraph.NODE_TITLE_HEIGHT, LiteGraph.NODE_TITLE_HEIGHT, - LiteGraph.NODE_TITLE_HEIGHT + LiteGraph.NODE_TITLE_HEIGHT, ); if (node.order == 0) { ctx.strokeRect( node.pos[0] - LiteGraph.NODE_TITLE_HEIGHT + 0.5, node.pos[1] - LiteGraph.NODE_TITLE_HEIGHT + 0.5, LiteGraph.NODE_TITLE_HEIGHT, - LiteGraph.NODE_TITLE_HEIGHT + LiteGraph.NODE_TITLE_HEIGHT, ); } ctx.fillStyle = "#FFF"; ctx.fillText( node.order, node.pos[0] + LiteGraph.NODE_TITLE_HEIGHT * -0.5, - node.pos[1] - 6 + node.pos[1] - 6, ); } ctx.globalAlpha = 1; @@ -9866,7 +9758,7 @@ LGraphNode.prototype.executeAction = function(action) node, posY, ctx, - active_widget + active_widget, ) { if (!node.widgets || !node.widgets.length) { return 0; @@ -9881,7 +9773,7 @@ LGraphNode.prototype.executeAction = function(action) var outline_color = LiteGraph.WIDGET_OUTLINE_COLOR; var background_color = LiteGraph.WIDGET_BGCOLOR; var text_color = LiteGraph.WIDGET_TEXT_COLOR; - var secondary_text_color = LiteGraph.WIDGET_SECONDARY_TEXT_COLOR; + var secondary_text_color = LiteGraph.WIDGET_SECONDARY_TEXT_COLOR; var margin = 15; for (var i = 0; i < widgets.length; ++i) { @@ -9894,10 +9786,10 @@ LGraphNode.prototype.executeAction = function(action) ctx.strokeStyle = outline_color; ctx.fillStyle = "#222"; ctx.textAlign = "left"; - //ctx.lineWidth = 2; - if(w.disabled) - ctx.globalAlpha *= 0.5; - var widget_width = w.width || width; + // ctx.lineWidth = 2; + if(w.disabled) + ctx.globalAlpha *= 0.5; + var widget_width = w.width || width; switch (w.type) { case "button": @@ -9907,8 +9799,8 @@ LGraphNode.prototype.executeAction = function(action) this.dirty_canvas = true; } ctx.fillRect(margin, y, widget_width - margin * 2, H); - if(show_text && !w.disabled) - ctx.strokeRect( margin, y, widget_width - margin * 2, H ); + if(show_text && !w.disabled) + ctx.strokeRect( margin, y, widget_width - margin * 2, H ); if (show_text) { ctx.textAlign = "center"; ctx.fillStyle = text_color; @@ -9921,19 +9813,19 @@ LGraphNode.prototype.executeAction = function(action) ctx.fillStyle = background_color; ctx.beginPath(); if (show_text) - ctx.roundRect(margin, y, widget_width - margin * 2, H, [H * 0.5]); - else - ctx.rect(margin, y, widget_width - margin * 2, H ); + ctx.roundRect(margin, y, widget_width - margin * 2, H, [H * 0.5]); + else + ctx.rect(margin, y, widget_width - margin * 2, H ); ctx.fill(); - if(show_text && !w.disabled) - ctx.stroke(); + if(show_text && !w.disabled) + ctx.stroke(); ctx.fillStyle = w.value ? "#89A" : "#333"; ctx.beginPath(); ctx.arc( widget_width - margin * 2, y + H * 0.5, H * 0.36, 0, Math.PI * 2 ); ctx.fill(); if (show_text) { ctx.fillStyle = secondary_text_color; - const label = w.label || w.name; + const label = w.label || w.name; if (label != null) { ctx.fillText(label, margin * 2, y + H * 0.7); } @@ -9944,7 +9836,7 @@ LGraphNode.prototype.executeAction = function(action) ? w.options.on || "true" : w.options.off || "false", widget_width - 40, - y + H * 0.7 + y + H * 0.7, ); } break; @@ -9953,16 +9845,16 @@ LGraphNode.prototype.executeAction = function(action) ctx.fillRect(margin, y, widget_width - margin * 2, H); var range = w.options.max - w.options.min; var nvalue = (w.value - w.options.min) / range; - if(nvalue < 0.0) nvalue = 0.0; - if(nvalue > 1.0) nvalue = 1.0; + if(nvalue < 0.0) nvalue = 0.0; + if(nvalue > 1.0) nvalue = 1.0; ctx.fillStyle = w.options.hasOwnProperty("slider_color") ? w.options.slider_color : (active_widget == w ? "#89A" : "#678"); ctx.fillRect(margin, y, nvalue * (widget_width - margin * 2), H); - if(show_text && !w.disabled) - ctx.strokeRect(margin, y, widget_width - margin * 2, H); + if(show_text && !w.disabled) + ctx.strokeRect(margin, y, widget_width - margin * 2, H); if (w.marker) { var marker_nvalue = (w.marker - w.options.min) / range; - if(marker_nvalue < 0.0) marker_nvalue = 0.0; - if(marker_nvalue > 1.0) marker_nvalue = 1.0; + if(marker_nvalue < 0.0) marker_nvalue = 0.0; + if(marker_nvalue > 1.0) marker_nvalue = 1.0; ctx.fillStyle = w.options.hasOwnProperty("marker_color") ? w.options.marker_color : "#AA9"; ctx.fillRect( margin + marker_nvalue * (widget_width - margin * 2), y, 2, H ); } @@ -9970,13 +9862,11 @@ LGraphNode.prototype.executeAction = function(action) ctx.textAlign = "center"; ctx.fillStyle = text_color; ctx.fillText( - w.label || w.name + " " + Number(w.value).toFixed( - w.options.precision != null - ? w.options.precision - : 3 - ), + w.label || w.name + " " + Number(w.value).toFixed(w.options.precision != null + ? w.options.precision + : 3), widget_width * 0.5, - y + H * 0.7 + y + H * 0.7, ); } break; @@ -9986,56 +9876,52 @@ LGraphNode.prototype.executeAction = function(action) ctx.strokeStyle = outline_color; ctx.fillStyle = background_color; ctx.beginPath(); - if(show_text) - ctx.roundRect(margin, y, widget_width - margin * 2, H, [H * 0.5] ); - else - ctx.rect(margin, y, widget_width - margin * 2, H ); + if(show_text) + ctx.roundRect(margin, y, widget_width - margin * 2, H, [H * 0.5] ); + else + ctx.rect(margin, y, widget_width - margin * 2, H ); ctx.fill(); if (show_text) { - if(!w.disabled) - ctx.stroke(); + if(!w.disabled) + ctx.stroke(); ctx.fillStyle = text_color; - if(!w.disabled) - { - ctx.beginPath(); - ctx.moveTo(margin + 16, y + 5); - ctx.lineTo(margin + 6, y + H * 0.5); - ctx.lineTo(margin + 16, y + H - 5); - ctx.fill(); - ctx.beginPath(); - ctx.moveTo(widget_width - margin - 16, y + 5); - ctx.lineTo(widget_width - margin - 6, y + H * 0.5); - ctx.lineTo(widget_width - margin - 16, y + H - 5); - ctx.fill(); - } + if(!w.disabled) { + ctx.beginPath(); + ctx.moveTo(margin + 16, y + 5); + ctx.lineTo(margin + 6, y + H * 0.5); + ctx.lineTo(margin + 16, y + H - 5); + ctx.fill(); + ctx.beginPath(); + ctx.moveTo(widget_width - margin - 16, y + 5); + ctx.lineTo(widget_width - margin - 6, y + H * 0.5); + ctx.lineTo(widget_width - margin - 16, y + H - 5); + ctx.fill(); + } ctx.fillStyle = secondary_text_color; ctx.fillText(w.label || w.name, margin * 2 + 5, y + H * 0.7); ctx.fillStyle = text_color; ctx.textAlign = "right"; if (w.type == "number") { ctx.fillText( - Number(w.value).toFixed( - w.options.precision !== undefined - ? w.options.precision - : 3 - ), + Number(w.value).toFixed(w.options.precision !== undefined + ? w.options.precision + : 3), widget_width - margin * 2 - 20, - y + H * 0.7 + y + H * 0.7, ); } else { - var v = w.value; - if( w.options.values ) - { - var values = w.options.values; - if( values.constructor === Function ) - values = values(); - if(values && values.constructor !== Array) - v = values[ w.value ]; - } + var v = w.value; + if( w.options.values ) { + var values = w.options.values; + if( values.constructor === Function ) + values = values(); + if(values && values.constructor !== Array) + v = values[w.value]; + } ctx.fillText( v, widget_width - margin * 2 - 20, - y + H * 0.7 + y + H * 0.7, ); } } @@ -10047,28 +9933,28 @@ LGraphNode.prototype.executeAction = function(action) ctx.fillStyle = background_color; ctx.beginPath(); if (show_text) - ctx.roundRect(margin, y, widget_width - margin * 2, H, [H * 0.5]); - else - ctx.rect( margin, y, widget_width - margin * 2, H ); + ctx.roundRect(margin, y, widget_width - margin * 2, H, [H * 0.5]); + else + ctx.rect( margin, y, widget_width - margin * 2, H ); ctx.fill(); - if (show_text) { - if(!w.disabled) - ctx.stroke(); - ctx.save(); - ctx.beginPath(); - ctx.rect(margin, y, widget_width - margin * 2, H); - ctx.clip(); - - //ctx.stroke(); + if (show_text) { + if(!w.disabled) + ctx.stroke(); + ctx.save(); + ctx.beginPath(); + ctx.rect(margin, y, widget_width - margin * 2, H); + ctx.clip(); + + // ctx.stroke(); ctx.fillStyle = secondary_text_color; - const label = w.label || w.name; + const label = w.label || w.name; if (label != null) { ctx.fillText(label, margin * 2, y + H * 0.7); } ctx.fillStyle = text_color; ctx.textAlign = "right"; - ctx.fillText(String(w.value).substr(0,30), widget_width - margin * 2, y + H * 0.7); //30 chars max - ctx.restore(); + ctx.fillText(String(w.value).substr(0,30), widget_width - margin * 2, y + H * 0.7); // 30 chars max + ctx.restore(); } break; default: @@ -10078,11 +9964,11 @@ LGraphNode.prototype.executeAction = function(action) break; } posY += (w.computeSize ? w.computeSize(widget_width)[1] : H) + 4; - ctx.globalAlpha = this.editor_alpha; + ctx.globalAlpha = this.editor_alpha; } ctx.restore(); - ctx.textAlign = "left"; + ctx.textAlign = "left"; }; /** @@ -10093,7 +9979,7 @@ LGraphNode.prototype.executeAction = function(action) node, pos, event, - active_widget + active_widget, ) { if (!node.widgets || !node.widgets.length || (!this.allow_interaction && !node.flags.allow_interaction)) { return null; @@ -10108,22 +9994,22 @@ LGraphNode.prototype.executeAction = function(action) for (var i = 0; i < node.widgets.length; ++i) { var w = node.widgets[i]; - if(!w || w.disabled) - continue; - var widget_height = w.computeSize ? w.computeSize(width)[1] : LiteGraph.NODE_WIDGET_HEIGHT; - var widget_width = w.width || width; - //outside - if ( w != active_widget && - (x < 6 || x > widget_width - 12 || y < w.last_y || y > w.last_y + widget_height || w.last_y === undefined) ) - continue; - - var old_value = w.value; - - //if ( w == active_widget || (x > 6 && x < widget_width - 12 && y > w.last_y && y < w.last_y + widget_height) ) { - //inside widget - switch (w.type) { - case "button": - if (event.type === LiteGraph.pointerevents_method+"down") { + if(!w || w.disabled) + continue; + var widget_height = w.computeSize ? w.computeSize(width)[1] : LiteGraph.NODE_WIDGET_HEIGHT; + var widget_width = w.width || width; + // outside + if ( w != active_widget && + (x < 6 || x > widget_width - 12 || y < w.last_y || y > w.last_y + widget_height || w.last_y === undefined) ) + continue; + + var old_value = w.value; + + // if ( w == active_widget || (x > 6 && x < widget_width - 12 && y > w.last_y && y < w.last_y + widget_height) ) { + // inside widget + switch (w.type) { + case "button": + if (event.type === LiteGraph.pointerevents_method+"down") { if (w.callback) { setTimeout(function() { w.callback(w, that, node, pos, event); @@ -10132,150 +10018,155 @@ LGraphNode.prototype.executeAction = function(action) w.clicked = true; this.dirty_canvas = true; } - break; - case "slider": - var old_value = w.value; - var nvalue = clamp((x - 15) / (widget_width - 30), 0, 1); - if(w.options.read_only) break; - w.value = w.options.min + (w.options.max - w.options.min) * nvalue; - if (old_value != w.value) { - setTimeout(function() { - inner_value_change(w, w.value); - }, 20); - } - this.dirty_canvas = true; - break; - case "number": - case "combo": - var old_value = w.value; - if (event.type == LiteGraph.pointerevents_method+"move" && w.type == "number") { + break; + case "slider": + var old_value = w.value; + var nvalue = clamp((x - 15) / (widget_width - 30), 0, 1); + if(w.options.read_only) break; + w.value = w.options.min + (w.options.max - w.options.min) * nvalue; + if (old_value != w.value) { + setTimeout(function() { + inner_value_change(w, w.value); + }, 20); + } + this.dirty_canvas = true; + break; + case "number": + case "combo": + var old_value = w.value; + if (event.type == LiteGraph.pointerevents_method+"move" && w.type == "number") { if(deltaX) - w.value += deltaX * 0.1 * (w.options.step || 1); - if ( w.options.min != null && w.value < w.options.min ) { - w.value = w.options.min; - } - if ( w.options.max != null && w.value > w.options.max ) { - w.value = w.options.max; - } - } else if (event.type == LiteGraph.pointerevents_method+"down") { - var values = w.options.values; - if (values && values.constructor === Function) { - values = w.options.values(w, node); - } - var values_list = null; - - if( w.type != "number") - values_list = values.constructor === Array ? values : Object.keys(values); - - var delta = x < 40 ? -1 : x > widget_width - 40 ? 1 : 0; - if (w.type == "number") { - w.value += delta * 0.1 * (w.options.step || 1); - if ( w.options.min != null && w.value < w.options.min ) { - w.value = w.options.min; - } - if ( w.options.max != null && w.value > w.options.max ) { - w.value = w.options.max; - } - } else if (delta) { //clicked in arrow, used for combos - var index = -1; - this.last_mouseclick = 0; //avoids dobl click event - if(values.constructor === Object) - index = values_list.indexOf( String( w.value ) ) + delta; - else - index = values_list.indexOf( w.value ) + delta; - if (index >= values_list.length) { - index = values_list.length - 1; - } - if (index < 0) { - index = 0; - } - if( values.constructor === Array ) - w.value = values[index]; - else - w.value = index; - } else { //combo clicked - var text_values = values != values_list ? Object.values(values) : values; - var menu = new LiteGraph.ContextMenu(text_values, { - scale: Math.max(1, this.ds.scale), - event: event, - className: "dark", - callback: inner_clicked.bind(w) - }, - ref_window); - function inner_clicked(v, option, event) { - if(values != values_list) - v = text_values.indexOf(v); - this.value = v; - inner_value_change(this, v); - that.dirty_canvas = true; - return false; - } - } - } //end mousedown - else if(event.type == LiteGraph.pointerevents_method+"up" && w.type == "number") - { - var delta = x < 40 ? -1 : x > widget_width - 40 ? 1 : 0; - if (event.click_time < 200 && delta == 0) { - this.prompt("Value",w.value,function(v) { - // check if v is a valid equation or a number - if (/^[0-9+\-*/()\s]+|\d+\.\d+$/.test(v)) { - try {//solve the equation if possible - v = eval(v); - } catch (e) { } - } - this.value = Number(v); - inner_value_change(this, this.value); - }.bind(w), - event); - } - } - - if( old_value != w.value ) - setTimeout( - function() { - inner_value_change(this, this.value); - }.bind(w), - 20 - ); - this.dirty_canvas = true; - break; - case "toggle": - if (event.type == LiteGraph.pointerevents_method+"down") { - w.value = !w.value; - setTimeout(function() { - inner_value_change(w, w.value); - }, 20); - } - break; - case "string": - case "text": - if (event.type == LiteGraph.pointerevents_method+"down") { - this.prompt("Value",w.value,function(v) { - inner_value_change(this, v); - }.bind(w), - event,w.options ? w.options.multiline : false ); - } - break; - default: - if (w.mouse) { - this.dirty_canvas = w.mouse(event, [x, y], node); - } - break; - } //end switch - - //value changed - if( old_value != w.value ) - { - if(node.onWidgetChanged) - node.onWidgetChanged( w.name,w.value,old_value,w ); + w.value += deltaX * 0.1 * (w.options.step || 1); + if ( w.options.min != null && w.value < w.options.min ) { + w.value = w.options.min; + } + if ( w.options.max != null && w.value > w.options.max ) { + w.value = w.options.max; + } + } else if (event.type == LiteGraph.pointerevents_method+"down") { + var values = w.options.values; + if (values && values.constructor === Function) { + values = w.options.values(w, node); + } + var values_list = null; + + if( w.type != "number") + values_list = values.constructor === Array ? values : Object.keys(values); + + var delta = x < 40 ? -1 : x > widget_width - 40 ? 1 : 0; + if (w.type == "number") { + w.value += delta * 0.1 * (w.options.step || 1); + if ( w.options.min != null && w.value < w.options.min ) { + w.value = w.options.min; + } + if ( w.options.max != null && w.value > w.options.max ) { + w.value = w.options.max; + } + } else if (delta) { // clicked in arrow, used for combos + var index = -1; + this.last_mouseclick = 0; // avoids dobl click event + if(values.constructor === Object) + index = values_list.indexOf( String( w.value ) ) + delta; + else + index = values_list.indexOf( w.value ) + delta; + if (index >= values_list.length) { + index = values_list.length - 1; + } + if (index < 0) { + index = 0; + } + if( values.constructor === Array ) + w.value = values[index]; + else + w.value = index; + } else { // combo clicked + var text_values = values != values_list ? Object.values(values) : values; + var menu = new LiteGraph.ContextMenu( + text_values, { + scale: Math.max(1, this.ds.scale), + event: event, + className: "dark", + callback: inner_clicked.bind(w), + }, + ref_window, + ); + function inner_clicked(v, option, event) { + if(values != values_list) + v = text_values.indexOf(v); + this.value = v; + inner_value_change(this, v); + that.dirty_canvas = true; + return false; + } + } + } else if(event.type == LiteGraph.pointerevents_method+"up" && w.type == "number") { // end mousedown + var delta = x < 40 ? -1 : x > widget_width - 40 ? 1 : 0; + if (event.click_time < 200 && delta == 0) { + this.prompt( + "Value",w.value,function(v) { + // check if v is a valid equation or a number + if (/^[0-9+\-*/()\s]+|\d+\.\d+$/.test(v)) { + try { // solve the equation if possible + v = eval(v); + } catch (e) { + console.error(e); + } + } + this.value = Number(v); + inner_value_change(this, this.value); + }.bind(w), + event, + ); + } + } + + if( old_value != w.value ) + setTimeout( + function() { + inner_value_change(this, this.value); + }.bind(w), + 20, + ); + this.dirty_canvas = true; + break; + case "toggle": + if (event.type == LiteGraph.pointerevents_method+"down") { + w.value = !w.value; + setTimeout(function() { + inner_value_change(w, w.value); + }, 20); + } + break; + case "string": + case "text": + if (event.type == LiteGraph.pointerevents_method+"down") { + this.prompt( + "Value",w.value,function(v) { + inner_value_change(this, v); + }.bind(w), + event,w.options ? w.options.multiline : false, + ); + } + break; + default: + if (w.mouse) { + this.dirty_canvas = w.mouse(event, [x, y], node); + } + break; + } // end switch + + // value changed + if( old_value != w.value ) { + if(node.onWidgetChanged) + node.onWidgetChanged( w.name,w.value,old_value,w ); node.graph._version++; - } + } - return w; - }//end for + return w; + }// end for function inner_value_change(widget, value) { - if(widget.type == "number"){ + if(widget.type == "number") { value = Number(value); } widget.value = value; @@ -10309,7 +10200,7 @@ LGraphNode.prototype.executeAction = function(action) if (!overlapBounding(this.visible_area, group._bounding)) { continue; - } //out of the visible area + } // out of the visible area ctx.fillStyle = group.color || "#335"; ctx.strokeStyle = group.color || "#335"; @@ -10331,7 +10222,7 @@ LGraphNode.prototype.executeAction = function(action) var font_size = group.font_size || LiteGraph.DEFAULT_GROUP_FONT_SIZE; ctx.font = font_size + "px Arial"; - ctx.textAlign = "left"; + ctx.textAlign = "left"; ctx.fillText(group.title, pos[0] + 4, pos[1] + font_size); } @@ -10407,12 +10298,12 @@ LGraphNode.prototype.executeAction = function(action) }; LGraphCanvas.prototype.onNodeSelectionChange = function(node) { - return; //disabled + return; // disabled }; /* this is an implementation for touch not in production and not ready */ - /*LGraphCanvas.prototype.touchHandler = function(event) { + /* LGraphCanvas.prototype.touchHandler = function(event) { //alert("foo"); var touches = event.changedTouches, first = touches[0], @@ -10442,7 +10333,7 @@ LGraphNode.prototype.executeAction = function(action) }else{ var window = this.getCanvasWindow(); } - + var document = window.document; var simulatedEvent = document.createEvent("MouseEvent"); @@ -10511,7 +10402,7 @@ LGraphNode.prototype.executeAction = function(action) "top": top, "right": right, "bottom": bottom, - "left": left + "left": left, }; } /** @@ -10542,7 +10433,7 @@ LGraphNode.prototype.executeAction = function(action) "top": align_to, "right": align_to, "bottom": align_to, - "left": align_to + "left": align_to, } } @@ -10599,41 +10490,48 @@ LGraphNode.prototype.executeAction = function(action) if (!graph) return; - function inner_onMenuAdded(base_category ,prev_menu){ - - var categories = LiteGraph.getNodeTypesCategories(canvas.filter || graph.filter).filter(function(category){return category.startsWith(base_category)}); + function inner_onMenuAdded(base_category ,prev_menu) { + + var categories = LiteGraph.getNodeTypesCategories(canvas.filter || graph.filter).filter(function(category) { + return category.startsWith(base_category) + }); var entries = []; - - categories.map(function(category){ - - if (!category) + + categories.map(function(category) { + + if (!category) return; - + var base_category_regex = new RegExp('^(' + base_category + ')'); var category_name = category.replace(base_category_regex,"").split('/')[0]; - var category_path = base_category === '' ? category_name + '/' : base_category + category_name + '/'; - + var category_path = base_category === '' ? category_name + '/' : base_category + category_name + '/'; + var name = category_name; - if(name.indexOf("::") != -1) //in case it has a namespace like "shader::math/rand" it hides the namespace + if(name.indexOf("::") != -1) // in case it has a namespace like "shader::math/rand" it hides the namespace name = name.split("::")[1]; - - var index = entries.findIndex(function(entry){return entry.value === category_path}); + + var index = entries.findIndex(function(entry) { + return entry.value === category_path + }); if (index === -1) { - entries.push({ value: category_path, content: name, has_submenu: true, callback : function(value, event, mouseEvent, contextMenu){ - inner_onMenuAdded(value.value, contextMenu) - }}); + entries.push({ + value: category_path, content: name, has_submenu: true, callback: function(value, event, mouseEvent, contextMenu) { + inner_onMenuAdded(value.value, contextMenu) + }, + }); } - + }); - + var nodes = LiteGraph.getNodeTypesInCategory(base_category.slice(0, -1), canvas.filter || graph.filter ); - nodes.map(function(node){ - + nodes.map(function(node) { + if (node.skip_list) return; - - var entry = { value: node.type, content: node.title, has_submenu: false , callback : function(value, event, mouseEvent, contextMenu){ - + + var entry = { + value: node.type, content: node.title, has_submenu: false , callback: function(value, event, mouseEvent, contextMenu) { + var first_event = contextMenu.getFirstEvent(); canvas.graph.beforeChange(); var node = LiteGraph.createNode(value.value); @@ -10644,21 +10542,21 @@ LGraphNode.prototype.executeAction = function(action) if(callback) callback(node); canvas.graph.afterChange(); - - } + + }, } - + entries.push(entry); - + }); - + new LiteGraph.ContextMenu( entries, { event: e, parentMenu: prev_menu }, ref_window ); - + } - + inner_onMenuAdded('',prev_menu); return false; - + }; LGraphCanvas.onMenuCollapseAll = function() {}; @@ -10670,7 +10568,7 @@ LGraphNode.prototype.executeAction = function(action) options, e, prev_menu, - node + node, ) { if (!node) { return; @@ -10694,14 +10592,14 @@ LGraphNode.prototype.executeAction = function(action) continue; } var label = entry[0]; - if(!entry[2]) - entry[2] = {}; + if(!entry[2]) + entry[2] = {}; if (entry[2].label) { label = entry[2].label; } - entry[2].removable = true; + entry[2].removable = true; var data = { content: label, value: entry }; if (entry[1] == LiteGraph.ACTION) { data.className = "event"; @@ -10716,7 +10614,7 @@ LGraphNode.prototype.executeAction = function(action) } if (!entries.length) { - console.log("no input entries"); + console.log("no input entries"); return; } @@ -10726,9 +10624,9 @@ LGraphNode.prototype.executeAction = function(action) event: e, callback: inner_clicked, parentMenu: prev_menu, - node: node + node: node, }, - ref_window + ref_window, ); function inner_clicked(v, e, prev) { @@ -10741,14 +10639,14 @@ LGraphNode.prototype.executeAction = function(action) } if (v.value) { - node.graph.beforeChange(); + node.graph.beforeChange(); node.addInput(v.value[0], v.value[1], v.value[2]); if (node.onNodeInputAdd) { // callback to the node when adding a slot node.onNodeInputAdd(v.value); } node.setDirtyCanvas(true, true); - node.graph.afterChange(); + node.graph.afterChange(); } } @@ -10760,7 +10658,7 @@ LGraphNode.prototype.executeAction = function(action) options, e, prev_menu, - node + node, ) { if (!node) { return; @@ -10780,7 +10678,7 @@ LGraphNode.prototype.executeAction = function(action) for (var i=0; i < options.length; i++) { var entry = options[i]; if (!entry) { - //separator? + // separator? entries.push(null); continue; } @@ -10791,14 +10689,14 @@ LGraphNode.prototype.executeAction = function(action) node.findOutputSlot(entry[0]) != -1 ) { continue; - } //skip the ones already on + } // skip the ones already on var label = entry[0]; - if(!entry[2]) - entry[2] = {}; + if(!entry[2]) + entry[2] = {}; if (entry[2].label) { label = entry[2].label; } - entry[2].removable = true; + entry[2].removable = true; var data = { content: label, value: entry }; if (entry[1] == LiteGraph.EVENT) { data.className = "event"; @@ -10810,9 +10708,9 @@ LGraphNode.prototype.executeAction = function(action) if (this.onMenuNodeOutputs) { entries = this.onMenuNodeOutputs(entries); } - if (LiteGraph.do_add_triggers_slots){ //canvas.allow_addOutSlot_onExecuted - if (node.findOutputSlot("onExecuted") == -1){ - entries.push({content: "On Executed", value: ["onExecuted", LiteGraph.EVENT, {nameLocked: true}], className: "event"}); //, opts: {} + if (LiteGraph.do_add_triggers_slots) { // canvas.allow_addOutSlot_onExecuted + if (node.findOutputSlot("onExecuted") == -1) { + entries.push({content: "On Executed", value: ["onExecuted", LiteGraph.EVENT, {nameLocked: true}], className: "event"}); // , opts: {} } } // add callback for modifing the menu elements onMenuNodeOutputs @@ -10831,9 +10729,9 @@ LGraphNode.prototype.executeAction = function(action) event: e, callback: inner_clicked, parentMenu: prev_menu, - node: node + node: node, }, - ref_window + ref_window, ); function inner_clicked(v, e, prev) { @@ -10855,7 +10753,7 @@ LGraphNode.prototype.executeAction = function(action) value && (value.constructor === Object || value.constructor === Array) ) { - //submenu why? + // submenu why? var entries = []; for (var i in value) { entries.push({ content: i, value: value[i] }); @@ -10864,18 +10762,18 @@ LGraphNode.prototype.executeAction = function(action) event: e, callback: inner_clicked, parentMenu: prev_menu, - node: node + node: node, }); return false; } else { - node.graph.beforeChange(); + node.graph.beforeChange(); node.addOutput(v.value[0], v.value[1], v.value[2]); if (node.onNodeOutputAdd) { // a callback to the node when adding a slot node.onNodeOutputAdd(v.value); } node.setDirtyCanvas(true, true); - node.graph.afterChange(); + node.graph.afterChange(); } } @@ -10887,7 +10785,7 @@ LGraphNode.prototype.executeAction = function(action) options, e, prev_menu, - node + node, ) { if (!node || !node.properties) { return; @@ -10900,13 +10798,13 @@ LGraphNode.prototype.executeAction = function(action) var entries = []; for (var i in node.properties) { var value = node.properties[i] !== undefined ? node.properties[i] : " "; - if( typeof value == "object" ) - value = JSON.stringify(value); - var info = node.getPropertyInfo(i); - if(info.type == "enum" || info.type == "combo") - value = LGraphCanvas.getPropertyPrintableValue( value, info.values ); + if( typeof value == "object" ) + value = JSON.stringify(value); + var info = node.getPropertyInfo(i); + if(info.type == "enum" || info.type == "combo") + value = LGraphCanvas.getPropertyPrintableValue( value, info.values ); - //value could contain invalid html characters, clean that + // value could contain invalid html characters, clean that value = LGraphCanvas.decodeHTML(value); entries.push({ content: @@ -10916,7 +10814,7 @@ LGraphNode.prototype.executeAction = function(action) "" + value + "", - value: i + value: i, }); } if (!entries.length) { @@ -10930,9 +10828,9 @@ LGraphNode.prototype.executeAction = function(action) callback: inner_clicked, parentMenu: prev_menu, allow_html: true, - node: node + node: node, }, - ref_window + ref_window, ); function inner_clicked(v, options, e, prev) { @@ -10940,9 +10838,7 @@ LGraphNode.prototype.executeAction = function(action) return; } var rect = this.getBoundingClientRect(); - canvas.showEditPropertyValue(node, v.value, { - position: [rect.left, rect.top] - }); + canvas.showEditPropertyValue(node, v.value, {position: [rect.left, rect.top]}); } return false; @@ -10958,65 +10854,65 @@ LGraphNode.prototype.executeAction = function(action) if (!node) { return; } - - var fApplyMultiNode = function(node){ - node.size = node.computeSize(); - if (node.onResize) - node.onResize(node.size); - } - - var graphcanvas = LGraphCanvas.active_canvas; - if (!graphcanvas.selected_nodes || Object.keys(graphcanvas.selected_nodes).length <= 1){ - fApplyMultiNode(node); - }else{ - for (var i in graphcanvas.selected_nodes) { - fApplyMultiNode(graphcanvas.selected_nodes[i]); - } - } - + + var fApplyMultiNode = function(node) { + node.size = node.computeSize(); + if (node.onResize) + node.onResize(node.size); + } + + var graphcanvas = LGraphCanvas.active_canvas; + if (!graphcanvas.selected_nodes || Object.keys(graphcanvas.selected_nodes).length <= 1) { + fApplyMultiNode(node); + }else{ + for (var i in graphcanvas.selected_nodes) { + fApplyMultiNode(graphcanvas.selected_nodes[i]); + } + } + node.setDirtyCanvas(true, true); }; LGraphCanvas.prototype.showLinkMenu = function(link, e) { var that = this; - // console.log(link); - var node_left = that.graph.getNodeById( link.origin_id ); - var node_right = that.graph.getNodeById( link.target_id ); - var fromType = false; - if (node_left && node_left.outputs && node_left.outputs[link.origin_slot]) fromType = node_left.outputs[link.origin_slot].type; + // console.log(link); + var node_left = that.graph.getNodeById( link.origin_id ); + var node_right = that.graph.getNodeById( link.target_id ); + var fromType = false; + if (node_left && node_left.outputs && node_left.outputs[link.origin_slot]) fromType = node_left.outputs[link.origin_slot].type; var destType = false; - if (node_right && node_right.outputs && node_right.outputs[link.target_slot]) destType = node_right.inputs[link.target_slot].type; - - var options = ["Add Node",null,"Delete",null]; - - + if (node_right && node_right.outputs && node_right.outputs[link.target_slot]) destType = node_right.inputs[link.target_slot].type; + + var options = ["Add Node",null,"Delete",null]; + + var menu = new LiteGraph.ContextMenu(options, { event: e, - title: link.data != null ? link.data.constructor.name : null, - callback: inner_clicked + title: link.data != null ? link.data.constructor.name : null, + callback: inner_clicked, }); function inner_clicked(v,options,e) { switch (v) { case "Add Node": - LGraphCanvas.onMenuAdd(null, null, e, menu, function(node){ - // console.debug("node autoconnect"); - if(!node.inputs || !node.inputs.length || !node.outputs || !node.outputs.length){ - return; - } - // leave the connection type checking inside connectByType - if (node_left.connectByType( link.origin_slot, node, fromType )){ - node.connectByType( link.target_slot, node_right, destType ); + LGraphCanvas.onMenuAdd(null, null, e, menu, function(node) { + // console.debug("node autoconnect"); + if(!node.inputs || !node.inputs.length || !node.outputs || !node.outputs.length) { + return; + } + // leave the connection type checking inside connectByType + if (node_left.connectByType( link.origin_slot, node, fromType )) { + node.connectByType( link.target_slot, node_right, destType ); node.pos[0] -= node.size[0] * 0.5; } - }); - break; - + }); + break; + case "Delete": that.graph.removeLink(link.id); break; default: - /*var nodeCreated = createDefaultNodeForSlot({ nodeFrom: node_left + /* var nodeCreated = createDefaultNodeForSlot({ nodeFrom: node_left ,slotFrom: link.origin_slot ,nodeTo: node ,slotTo: link.target_slot @@ -11029,264 +10925,271 @@ LGraphNode.prototype.executeAction = function(action) return false; }; - - LGraphCanvas.prototype.createDefaultNodeForSlot = function(optPass) { // addNodeMenu for connection + + LGraphCanvas.prototype.createDefaultNodeForSlot = function(optPass) { // addNodeMenu for connection var optPass = optPass || {}; - var opts = Object.assign({ nodeFrom: null // input - ,slotFrom: null // input - ,nodeTo: null // output - ,slotTo: null // output - ,position: [] // pass the event coords - ,nodeType: null // choose a nodetype to add, AUTO to set at first good - ,posAdd:[0,0] // adjust x,y - ,posSizeFix:[0,0] // alpha, adjust the position x,y based on the new node size w,h - } - ,optPass - ); + var opts = Object.assign( + { + nodeFrom: null, // input + slotFrom: null, // input + nodeTo: null, // output + slotTo: null, // output + position: [], // pass the event coords + nodeType: null, // choose a nodetype to add, AUTO to set at first good + posAdd: [0,0], // adjust x,y + posSizeFix: [0,0], // alpha, adjust the position x,y based on the new node size w,h + }, + optPass, + ); var that = this; - + var isFrom = opts.nodeFrom && opts.slotFrom!==null; var isTo = !isFrom && opts.nodeTo && opts.slotTo!==null; - - if (!isFrom && !isTo){ + + if (!isFrom && !isTo) { console.warn("No data passed to createDefaultNodeForSlot "+opts.nodeFrom+" "+opts.slotFrom+" "+opts.nodeTo+" "+opts.slotTo); return false; } - if (!opts.nodeType){ + if (!opts.nodeType) { console.warn("No type to createDefaultNodeForSlot"); return false; } - + var nodeX = isFrom ? opts.nodeFrom : opts.nodeTo; var slotX = isFrom ? opts.slotFrom : opts.slotTo; - + var iSlotConn = false; - switch (typeof slotX){ + switch (typeof slotX) { case "string": iSlotConn = isFrom ? nodeX.findOutputSlot(slotX,false) : nodeX.findInputSlot(slotX,false); slotX = isFrom ? nodeX.outputs[slotX] : nodeX.inputs[slotX]; - break; + break; case "object": // ok slotX iSlotConn = isFrom ? nodeX.findOutputSlot(slotX.name) : nodeX.findInputSlot(slotX.name); - break; + break; case "number": iSlotConn = slotX; slotX = isFrom ? nodeX.outputs[slotX] : nodeX.inputs[slotX]; - break; - case "undefined": + break; + case "undefined": default: // bad ? - //iSlotConn = 0; + // iSlotConn = 0; console.warn("Cant get slot information "+slotX); return false; } - - if (slotX===false || iSlotConn===false){ - console.warn("createDefaultNodeForSlot bad slotX "+slotX+" "+iSlotConn); - } - - // check for defaults nodes for this slottype - var fromSlotType = slotX.type==LiteGraph.EVENT?"_event_":slotX.type; - var slotTypesDefault = isFrom ? LiteGraph.slot_types_default_out : LiteGraph.slot_types_default_in; - if(slotTypesDefault && slotTypesDefault[fromSlotType]){ - if (slotX.link !== null) { - // is connected - }else{ - // is not not connected - } - nodeNewType = false; - if(typeof slotTypesDefault[fromSlotType] == "object" || typeof slotTypesDefault[fromSlotType] == "array"){ - for(var typeX in slotTypesDefault[fromSlotType]){ - if (opts.nodeType == slotTypesDefault[fromSlotType][typeX] || opts.nodeType == "AUTO"){ - nodeNewType = slotTypesDefault[fromSlotType][typeX]; - // console.log("opts.nodeType == slotTypesDefault[fromSlotType][typeX] :: "+opts.nodeType); - break; // -------- - } - } - }else{ - if (opts.nodeType == slotTypesDefault[fromSlotType] || opts.nodeType == "AUTO") nodeNewType = slotTypesDefault[fromSlotType]; - } - if (nodeNewType) { - var nodeNewOpts = false; - if (typeof nodeNewType == "object" && nodeNewType.node){ - nodeNewOpts = nodeNewType; - nodeNewType = nodeNewType.node; - } - - //that.graph.beforeChange(); - - var newNode = LiteGraph.createNode(nodeNewType); - if(newNode){ - // if is object pass options - if (nodeNewOpts){ - if (nodeNewOpts.properties) { - for (var i in nodeNewOpts.properties) { - newNode.addProperty( i, nodeNewOpts.properties[i] ); - } - } - if (nodeNewOpts.inputs) { - newNode.inputs = []; - for (var i in nodeNewOpts.inputs) { - newNode.addOutput( - nodeNewOpts.inputs[i][0], - nodeNewOpts.inputs[i][1] - ); - } - } - if (nodeNewOpts.outputs) { - newNode.outputs = []; - for (var i in nodeNewOpts.outputs) { - newNode.addOutput( - nodeNewOpts.outputs[i][0], - nodeNewOpts.outputs[i][1] - ); - } - } - if (nodeNewOpts.title) { - newNode.title = nodeNewOpts.title; - } - if (nodeNewOpts.json) { - newNode.configure(nodeNewOpts.json); - } - - } - - // add the node - that.graph.add(newNode); - newNode.pos = [ opts.position[0]+opts.posAdd[0]+(opts.posSizeFix[0]?opts.posSizeFix[0]*newNode.size[0]:0) - ,opts.position[1]+opts.posAdd[1]+(opts.posSizeFix[1]?opts.posSizeFix[1]*newNode.size[1]:0)]; //that.last_click_position; //[e.canvasX+30, e.canvasX+5];*/ - - //that.graph.afterChange(); - - // connect the two! - if (isFrom){ - opts.nodeFrom.connectByType( iSlotConn, newNode, fromSlotType ); - }else{ - opts.nodeTo.connectByTypeOutput( iSlotConn, newNode, fromSlotType ); - } - - // if connecting in between - if (isFrom && isTo){ - // TODO - } - - return true; - - }else{ - console.log("failed creating "+nodeNewType); - } - } - } - return false; - } - + + if (slotX===false || iSlotConn===false) { + console.warn("createDefaultNodeForSlot bad slotX "+slotX+" "+iSlotConn); + } + + // check for defaults nodes for this slottype + var fromSlotType = slotX.type==LiteGraph.EVENT?"_event_":slotX.type; + var slotTypesDefault = isFrom ? LiteGraph.slot_types_default_out : LiteGraph.slot_types_default_in; + if(slotTypesDefault && slotTypesDefault[fromSlotType]) { + if (slotX.link !== null) { + // is connected + }else{ + // is not not connected + } + nodeNewType = false; + if(Array.isArray(slotTypesDefault[fromSlotType]) || (typeof slotTypesDefault[fromSlotType] === "object")) { + for(var typeX in slotTypesDefault[fromSlotType]) { + if (opts.nodeType == slotTypesDefault[fromSlotType][typeX] || opts.nodeType == "AUTO") { + nodeNewType = slotTypesDefault[fromSlotType][typeX]; + // console.log("opts.nodeType == slotTypesDefault[fromSlotType][typeX] :: "+opts.nodeType); + break; // -------- + } + } + }else{ + if (opts.nodeType == slotTypesDefault[fromSlotType] || opts.nodeType == "AUTO") nodeNewType = slotTypesDefault[fromSlotType]; + } + if (nodeNewType) { + var nodeNewOpts = false; + if (typeof nodeNewType == "object" && nodeNewType.node) { + nodeNewOpts = nodeNewType; + nodeNewType = nodeNewType.node; + } + + // that.graph.beforeChange(); + + var newNode = LiteGraph.createNode(nodeNewType); + if(newNode) { + // if is object pass options + if (nodeNewOpts) { + if (nodeNewOpts.properties) { + for (var i in nodeNewOpts.properties) { + newNode.addProperty( i, nodeNewOpts.properties[i] ); + } + } + if (nodeNewOpts.inputs) { + newNode.inputs = []; + for (var i in nodeNewOpts.inputs) { + newNode.addOutput( + nodeNewOpts.inputs[i][0], + nodeNewOpts.inputs[i][1], + ); + } + } + if (nodeNewOpts.outputs) { + newNode.outputs = []; + for (var i in nodeNewOpts.outputs) { + newNode.addOutput( + nodeNewOpts.outputs[i][0], + nodeNewOpts.outputs[i][1], + ); + } + } + if (nodeNewOpts.title) { + newNode.title = nodeNewOpts.title; + } + if (nodeNewOpts.json) { + newNode.configure(nodeNewOpts.json); + } + + } + + // add the node + that.graph.add(newNode); + newNode.pos = [ + opts.position[0]+opts.posAdd[0]+(opts.posSizeFix[0]?opts.posSizeFix[0]*newNode.size[0]:0), + opts.position[1]+opts.posAdd[1]+(opts.posSizeFix[1]?opts.posSizeFix[1]*newNode.size[1]:0), + ]; // that.last_click_position; //[e.canvasX+30, e.canvasX+5];*/ + + // that.graph.afterChange(); + + // connect the two! + if (isFrom) { + opts.nodeFrom.connectByType( iSlotConn, newNode, fromSlotType ); + }else{ + opts.nodeTo.connectByTypeOutput( iSlotConn, newNode, fromSlotType ); + } + + // if connecting in between + if (isFrom && isTo) { + // TODO + } + + return true; + + }else{ + console.log("failed creating "+nodeNewType); + } + } + } + return false; + } + LGraphCanvas.prototype.showConnectionMenu = function(optPass) { // addNodeMenu for connection var optPass = optPass || {}; - var opts = Object.assign({ nodeFrom: null // input - ,slotFrom: null // input - ,nodeTo: null // output - ,slotTo: null // output - ,e: null - } - ,optPass - ); + var opts = Object.assign( + { + nodeFrom: null, // input + slotFrom: null, // input + nodeTo: null, // output + slotTo: null, // output + e: null, + } + ,optPass, + ); var that = this; - + var isFrom = opts.nodeFrom && opts.slotFrom; var isTo = !isFrom && opts.nodeTo && opts.slotTo; - - if (!isFrom && !isTo){ + + if (!isFrom && !isTo) { console.warn("No data passed to showConnectionMenu"); return false; } - + var nodeX = isFrom ? opts.nodeFrom : opts.nodeTo; var slotX = isFrom ? opts.slotFrom : opts.slotTo; - + var iSlotConn = false; - switch (typeof slotX){ + switch (typeof slotX) { case "string": iSlotConn = isFrom ? nodeX.findOutputSlot(slotX,false) : nodeX.findInputSlot(slotX,false); slotX = isFrom ? nodeX.outputs[slotX] : nodeX.inputs[slotX]; - break; + break; case "object": // ok slotX iSlotConn = isFrom ? nodeX.findOutputSlot(slotX.name) : nodeX.findInputSlot(slotX.name); - break; + break; case "number": iSlotConn = slotX; slotX = isFrom ? nodeX.outputs[slotX] : nodeX.inputs[slotX]; - break; + break; default: // bad ? - //iSlotConn = 0; + // iSlotConn = 0; console.warn("Cant get slot information "+slotX); return false; } - - var options = ["Add Node",null]; - - if (that.allow_searchbox){ - options.push("Search"); - options.push(null); - } - - // get defaults nodes for this slottype - var fromSlotType = slotX.type==LiteGraph.EVENT?"_event_":slotX.type; - var slotTypesDefault = isFrom ? LiteGraph.slot_types_default_out : LiteGraph.slot_types_default_in; - if(slotTypesDefault && slotTypesDefault[fromSlotType]){ - if(typeof slotTypesDefault[fromSlotType] == "object" || typeof slotTypesDefault[fromSlotType] == "array"){ - for(var typeX in slotTypesDefault[fromSlotType]){ - options.push(slotTypesDefault[fromSlotType][typeX]); - } - }else{ - options.push(slotTypesDefault[fromSlotType]); - } - } - - // build menu + + var options = ["Add Node",null]; + + if (that.allow_searchbox) { + options.push("Search"); + options.push(null); + } + + // get defaults nodes for this slottype + var fromSlotType = slotX.type==LiteGraph.EVENT?"_event_":slotX.type; + var slotTypesDefault = isFrom ? LiteGraph.slot_types_default_out : LiteGraph.slot_types_default_in; + if(slotTypesDefault && slotTypesDefault[fromSlotType]) { + if(Array.isArray(slotTypesDefault[fromSlotType]) || (typeof slotTypesDefault[fromSlotType] === "object")) { + for(var typeX in slotTypesDefault[fromSlotType]) { + options.push(slotTypesDefault[fromSlotType][typeX]); + } + }else{ + options.push(slotTypesDefault[fromSlotType]); + } + } + + // build menu var menu = new LiteGraph.ContextMenu(options, { event: opts.e, - title: (slotX && slotX.name!="" ? (slotX.name + (fromSlotType?" | ":"")) : "")+(slotX && fromSlotType ? fromSlotType : ""), - callback: inner_clicked + title: (slotX && slotX.name!="" ? (slotX.name + (fromSlotType?" | ":"")) : "")+(slotX && fromSlotType ? fromSlotType : ""), + callback: inner_clicked, }); - - // callback + + // callback function inner_clicked(v,options,e) { - //console.log("Process showConnectionMenu selection"); + // console.log("Process showConnectionMenu selection"); switch (v) { case "Add Node": - LGraphCanvas.onMenuAdd(null, null, e, menu, function(node){ - if (isFrom){ + LGraphCanvas.onMenuAdd(null, null, e, menu, function(node) { + if (isFrom) { opts.nodeFrom.connectByType( iSlotConn, node, fromSlotType ); }else{ opts.nodeTo.connectByTypeOutput( iSlotConn, node, fromSlotType ); } }); break; - case "Search": - if(isFrom){ - that.showSearchBox(e,{node_from: opts.nodeFrom, slot_from: slotX, type_filter_in: fromSlotType}); - }else{ - that.showSearchBox(e,{node_to: opts.nodeTo, slot_from: slotX, type_filter_out: fromSlotType}); - } - break; + case "Search": + if(isFrom) { + that.showSearchBox(e,{node_from: opts.nodeFrom, slot_from: slotX, type_filter_in: fromSlotType}); + }else{ + that.showSearchBox(e,{node_to: opts.nodeTo, slot_from: slotX, type_filter_out: fromSlotType}); + } + break; default: - // check for defaults nodes for this slottype - var nodeCreated = that.createDefaultNodeForSlot(Object.assign(opts,{ position: [opts.e.canvasX, opts.e.canvasY] - ,nodeType: v - })); - if (nodeCreated){ - // new node created - //console.log("node "+v+" created") - }else{ - // failed or v is not in defaults - } - break; - } - } - + // check for defaults nodes for this slottype + var nodeCreated = that.createDefaultNodeForSlot(Object.assign(opts,{ + position: [opts.e.canvasX, opts.e.canvasY], + nodeType: v, + })); + if (nodeCreated) { + // new node created + // console.log("node "+v+" created") + }else{ + // failed or v is not in defaults + } + break; + } + } + return false; }; @@ -11297,7 +11200,7 @@ LGraphNode.prototype.executeAction = function(action) var value = node[property]; // TODO refactor :: use createDialog ? - + var dialog = document.createElement("div"); dialog.is_modified = false; dialog.className = "graphdialog"; @@ -11319,7 +11222,7 @@ LGraphNode.prototype.executeAction = function(action) input.addEventListener("keydown", function(e) { dialog.is_modified = true; if (e.keyCode == 27) { - //ESC + // ESC dialog.close(); } else if (e.keyCode == 13) { inner(); // save @@ -11355,18 +11258,18 @@ LGraphNode.prototype.executeAction = function(action) canvas.parentNode.appendChild(dialog); if(input) input.focus(); - + var dialogCloseTimer = null; dialog.addEventListener("mouseleave", function(e) { if(LiteGraph.dialog_close_on_mouse_leave) if (!dialog.is_modified && LiteGraph.dialog_close_on_mouse_leave) - dialogCloseTimer = setTimeout(dialog.close, LiteGraph.dialog_close_on_mouse_leave_delay); //dialog.close(); + dialogCloseTimer = setTimeout(dialog.close, LiteGraph.dialog_close_on_mouse_leave_delay); // dialog.close(); }); dialog.addEventListener("mouseenter", function(e) { if(LiteGraph.dialog_close_on_mouse_leave) if(dialogCloseTimer) clearTimeout(dialogCloseTimer); }); - + function inner() { if(input) setValue(input.value); } @@ -11395,9 +11298,9 @@ LGraphNode.prototype.executeAction = function(action) dialog.is_modified = false; dialog.className = "graphdialog rounded"; if(multiline) - dialog.innerHTML = " "; - else - dialog.innerHTML = " "; + dialog.innerHTML = " "; + else + dialog.innerHTML = " "; dialog.close = function() { that.prompt_box = null; if (dialog.parentNode) { @@ -11408,7 +11311,7 @@ LGraphNode.prototype.executeAction = function(action) var graphcanvas = LGraphCanvas.active_canvas; var canvas = graphcanvas.canvas; canvas.parentNode.appendChild(dialog); - + if (this.ds.scale > 1) { dialog.style.transform = "scale(" + this.ds.scale + ")"; } @@ -11420,21 +11323,21 @@ LGraphNode.prototype.executeAction = function(action) return; if(LiteGraph.dialog_close_on_mouse_leave) if (!dialog.is_modified && LiteGraph.dialog_close_on_mouse_leave) - dialogCloseTimer = setTimeout(dialog.close, LiteGraph.dialog_close_on_mouse_leave_delay); //dialog.close(); + dialogCloseTimer = setTimeout(dialog.close, LiteGraph.dialog_close_on_mouse_leave_delay); // dialog.close(); }); LiteGraph.pointerListenerAdd(dialog,"enter", function(e) { if(LiteGraph.dialog_close_on_mouse_leave) if(dialogCloseTimer) clearTimeout(dialogCloseTimer); }); var selInDia = dialog.querySelectorAll("select"); - if (selInDia){ + if (selInDia) { // if filtering, check focus changed to comboboxes and prevent closing selInDia.forEach(function(selIn) { selIn.addEventListener("click", function(e) { prevent_timeout++; }); selIn.addEventListener("blur", function(e) { - prevent_timeout = 0; + prevent_timeout = 0; }); selIn.addEventListener("change", function(e) { prevent_timeout = -1; @@ -11460,7 +11363,7 @@ LGraphNode.prototype.executeAction = function(action) input.addEventListener("keydown", function(e) { dialog.is_modified = true; if (e.keyCode == 27) { - //ESC + // ESC dialog.close(); } else if (e.keyCode == 13 && e.target.localName != "textarea") { if (callback) { @@ -11509,22 +11412,23 @@ LGraphNode.prototype.executeAction = function(action) LGraphCanvas.search_limit = -1; LGraphCanvas.prototype.showSearchBox = function(event, options) { // proposed defaults - var def_options = { slot_from: null - ,node_from: null - ,node_to: null - ,do_type_filter: LiteGraph.search_filter_enabled // TODO check for registered_slot_[in/out]_types not empty // this will be checked for functionality enabled : filter on slot type, in and out - ,type_filter_in: false // these are default: pass to set initially set values - ,type_filter_out: false - ,show_general_if_none_on_typefilter: true - ,show_general_after_typefiltered: true - ,hide_on_mouse_leave: LiteGraph.search_hide_on_mouse_leave - ,show_all_if_empty: true - ,show_all_on_open: LiteGraph.search_show_all_on_open - }; + var def_options = { + slot_from: null, + node_from: null, + node_to: null, + do_type_filter: LiteGraph.search_filter_enabled, // TODO check for registered_slot_[in/out]_types not empty // this will be checked for functionality enabled : filter on slot type, in and out + type_filter_in: false, // these are default: pass to set initially set values + type_filter_out: false, + show_general_if_none_on_typefilter: true, + show_general_after_typefiltered: true, + hide_on_mouse_leave: LiteGraph.search_hide_on_mouse_leave, + show_all_if_empty: true, + show_all_on_open: LiteGraph.search_show_all_on_open, + }; options = Object.assign(def_options, options || {}); - - //console.log(options); - + + // console.log(options); + var that = this; var input_html = ""; var graphcanvas = LGraphCanvas.active_canvas; @@ -11534,35 +11438,34 @@ LGraphNode.prototype.executeAction = function(action) var dialog = document.createElement("div"); dialog.className = "litegraph litesearchbox graphdialog rounded"; dialog.innerHTML = "Search "; - if (options.do_type_filter){ + if (options.do_type_filter) { dialog.innerHTML += ""; dialog.innerHTML += ""; } dialog.innerHTML += "
"; - + if( root_document.fullscreenElement ) - root_document.fullscreenElement.appendChild(dialog); - else - { - root_document.body.appendChild(dialog); - root_document.body.style.overflow = "hidden"; - } + root_document.fullscreenElement.appendChild(dialog); + else { + root_document.body.appendChild(dialog); + root_document.body.style.overflow = "hidden"; + } // dialog element has been appended - - if (options.do_type_filter){ + + if (options.do_type_filter) { var selIn = dialog.querySelector(".slot_in_type_filter"); var selOut = dialog.querySelector(".slot_out_type_filter"); } - + dialog.close = function() { that.search_box = null; - this.blur(); + this.blur(); canvas.focus(); - root_document.body.style.overflow = ""; + root_document.body.style.overflow = ""; setTimeout(function() { that.canvas.focus(); - }, 20); //important, if canvas loses focus keys wont be captured + }, 20); // important, if canvas loses focus keys wont be captured if (dialog.parentNode) { dialog.parentNode.removeChild(dialog); } @@ -11573,7 +11476,7 @@ LGraphNode.prototype.executeAction = function(action) } // hide on mouse leave - if(options.hide_on_mouse_leave){ + if(options.hide_on_mouse_leave) { var prevent_timeout = false; var timeout_close = null; LiteGraph.pointerListenerAdd(dialog,"enter", function(e) { @@ -11583,7 +11486,7 @@ LGraphNode.prototype.executeAction = function(action) } }); LiteGraph.pointerListenerAdd(dialog,"leave", function(e) { - if (prevent_timeout){ + if (prevent_timeout) { return; } timeout_close = setTimeout(function() { @@ -11591,12 +11494,12 @@ LGraphNode.prototype.executeAction = function(action) }, 500); }); // if filtering, check focus changed to comboboxes and prevent closing - if (options.do_type_filter){ + if (options.do_type_filter) { selIn.addEventListener("click", function(e) { prevent_timeout++; }); selIn.addEventListener("blur", function(e) { - prevent_timeout = 0; + prevent_timeout = 0; }); selIn.addEventListener("change", function(e) { prevent_timeout = -1; @@ -11605,7 +11508,7 @@ LGraphNode.prototype.executeAction = function(action) prevent_timeout++; }); selOut.addEventListener("blur", function(e) { - prevent_timeout = 0; + prevent_timeout = 0; }); selOut.addEventListener("change", function(e) { prevent_timeout = -1; @@ -11632,13 +11535,13 @@ LGraphNode.prototype.executeAction = function(action) }); input.addEventListener("keydown", function(e) { if (e.keyCode == 38) { - //UP + // UP changeSelection(false); } else if (e.keyCode == 40) { - //DOWN + // DOWN changeSelection(true); } else if (e.keyCode == 27) { - //ESC + // ESC dialog.close(); } else if (e.keyCode == 13) { refreshHelper(); @@ -11658,67 +11561,67 @@ LGraphNode.prototype.executeAction = function(action) } e.preventDefault(); e.stopPropagation(); - e.stopImmediatePropagation(); - return true; + e.stopImmediatePropagation(); + return true; }); } - + // if should filter on type, load and fill selected and choose elements if passed - if (options.do_type_filter){ - if (selIn){ + if (options.do_type_filter) { + if (selIn) { var aSlots = LiteGraph.slot_types_in; var nSlots = aSlots.length; // this for object :: Object.keys(aSlots).length; - + if (options.type_filter_in == LiteGraph.EVENT || options.type_filter_in == LiteGraph.ACTION) options.type_filter_in = "_event_"; /* this will filter on * .. but better do it manually in case else if(options.type_filter_in === "" || options.type_filter_in === 0) options.type_filter_in = "*";*/ - - for (var iK=0; iK (rect.height - 200)) + // To avoid out of screen problems + if(event.layerY > (rect.height - 200)) helper.style.maxHeight = (rect.height - event.layerY - 20) + "px"; - /* + /* var offsetx = -20; var offsety = -20; if (rect) { @@ -11761,12 +11664,10 @@ LGraphNode.prototype.executeAction = function(action) name = extra.type; } - graphcanvas.graph.beforeChange(); + graphcanvas.graph.beforeChange(); var node = LiteGraph.createNode(name); if (node) { - node.pos = graphcanvas.convertEventToCanvasOffset( - event - ); + node.pos = graphcanvas.convertEventToCanvasOffset(event); graphcanvas.graph.add(node, false); } @@ -11781,7 +11682,7 @@ LGraphNode.prototype.executeAction = function(action) for (var i in extra.data.inputs) { node.addOutput( extra.data.inputs[i][0], - extra.data.inputs[i][1] + extra.data.inputs[i][1], ); } } @@ -11790,7 +11691,7 @@ LGraphNode.prototype.executeAction = function(action) for (var i in extra.data.outputs) { node.addOutput( extra.data.outputs[i][0], - extra.data.outputs[i][1] + extra.data.outputs[i][1], ); } } @@ -11804,56 +11705,56 @@ LGraphNode.prototype.executeAction = function(action) } // join node after inserting - if (options.node_from){ + if (options.node_from) { var iS = false; - switch (typeof options.slot_from){ + switch (typeof options.slot_from) { case "string": - iS = options.node_from.findOutputSlot(options.slot_from); - break; + iS = options.node_from.findOutputSlot(options.slot_from); + break; case "object": - if (options.slot_from.name){ + if (options.slot_from.name) { iS = options.node_from.findOutputSlot(options.slot_from.name); }else{ iS = -1; } if (iS==-1 && typeof options.slot_from.slot_index !== "undefined") iS = options.slot_from.slot_index; - break; + break; case "number": iS = options.slot_from; - break; + break; default: iS = 0; // try with first if no name set } - if (typeof options.node_from.outputs[iS] !== "undefined"){ - if (iS!==false && iS>-1){ + if (typeof options.node_from.outputs[iS] !== "undefined") { + if (iS!==false && iS>-1) { options.node_from.connectByType( iS, node, options.node_from.outputs[iS].type ); } }else{ // console.warn("cant find slot " + options.slot_from); } } - if (options.node_to){ + if (options.node_to) { var iS = false; - switch (typeof options.slot_from){ + switch (typeof options.slot_from) { case "string": - iS = options.node_to.findInputSlot(options.slot_from); - break; + iS = options.node_to.findInputSlot(options.slot_from); + break; case "object": - if (options.slot_from.name){ + if (options.slot_from.name) { iS = options.node_to.findInputSlot(options.slot_from.name); }else{ iS = -1; } if (iS==-1 && typeof options.slot_from.slot_index !== "undefined") iS = options.slot_from.slot_index; - break; + break; case "number": iS = options.slot_from; - break; + break; default: iS = 0; // try with first if no name set } - if (typeof options.node_to.inputs[iS] !== "undefined"){ - if (iS!==false && iS>-1){ + if (typeof options.node_to.inputs[iS] !== "undefined") { + if (iS!==false && iS>-1) { // try connection options.node_to.connectByTypeOutput(iS,node,options.node_to.inputs[iS].type); } @@ -11861,7 +11762,7 @@ LGraphNode.prototype.executeAction = function(action) // console.warn("cant find slot_nodeTO " + options.slot_from); } } - + graphcanvas.graph.afterChange(); } } @@ -11912,26 +11813,26 @@ LGraphNode.prototype.executeAction = function(action) } else { var c = 0; str = str.toLowerCase(); - var filter = graphcanvas.filter || graphcanvas.graph.filter; + var filter = graphcanvas.filter || graphcanvas.graph.filter; // filter by type preprocess - if(options.do_type_filter && that.search_box){ + if(options.do_type_filter && that.search_box) { var sIn = that.search_box.querySelector(".slot_in_type_filter"); var sOut = that.search_box.querySelector(".slot_out_type_filter"); }else{ var sIn = false; var sOut = false; } - - //extras + + // extras for (var i in LiteGraph.searchbox_extras) { var extra = LiteGraph.searchbox_extras[i]; if ((!options.show_all_if_empty || str) && extra.desc.toLowerCase().indexOf(str) === -1) { continue; } - var ctor = LiteGraph.registered_node_types[ extra.type ]; - if( ctor && ctor.filter != filter ) - continue; + var ctor = LiteGraph.registered_node_types[extra.type]; + if( ctor && ctor.filter != filter ) + continue; if( ! inner_test_filter(extra.type) ) continue; addResult( extra.desc, "searchbox_extra" ); @@ -11940,33 +11841,33 @@ LGraphNode.prototype.executeAction = function(action) } } - var filtered = null; - if (Array.prototype.filter) { //filter supported - var keys = Object.keys( LiteGraph.registered_node_types ); //types + var filtered = null; + if (Array.prototype.filter) { // filter supported + var keys = Object.keys( LiteGraph.registered_node_types ); // types var filtered = keys.filter( inner_test_filter ); } else { - filtered = []; + filtered = []; for (var i in LiteGraph.registered_node_types) { - if( inner_test_filter(i) ) - filtered.push(i); + if( inner_test_filter(i) ) + filtered.push(i); + } + } + + for (var i = 0; i < filtered.length; i++) { + addResult(filtered[i]); + if ( LGraphCanvas.search_limit !== -1 && c++ > LGraphCanvas.search_limit ) { + break; } } - for (var i = 0; i < filtered.length; i++) { - addResult(filtered[i]); - if ( LGraphCanvas.search_limit !== -1 && c++ > LGraphCanvas.search_limit ) { - break; - } - } - // add general type if filtering if (options.show_general_after_typefiltered - && (sIn.value || sOut.value) - ){ + && (sIn.value || sOut.value) + ) { filtered_extra = []; for (var i in LiteGraph.registered_node_types) { - if( inner_test_filter(i, {inTypeOverride: sIn&&sIn.value?"*":false, outTypeOverride: sOut&&sOut.value?"*":false}) ) - filtered_extra.push(i); + if( inner_test_filter(i, {inTypeOverride: sIn&&sIn.value?"*":false, outTypeOverride: sOut&&sOut.value?"*":false}) ) + filtered_extra.push(i); } for (var i = 0; i < filtered_extra.length; i++) { addResult(filtered_extra[i], "generic_type"); @@ -11975,15 +11876,15 @@ LGraphNode.prototype.executeAction = function(action) } } } - + // check il filtering gave no results - if ((sIn.value || sOut.value) && + if ((sIn.value || sOut.value) && ( (helper.childNodes.length == 0 && options.show_general_if_none_on_typefilter) ) - ){ + ) { filtered_extra = []; for (var i in LiteGraph.registered_node_types) { - if( inner_test_filter(i, {skipFilter: true}) ) - filtered_extra.push(i); + if( inner_test_filter(i, {skipFilter: true}) ) + filtered_extra.push(i); } for (var i = 0; i < filtered_extra.length; i++) { addResult(filtered_extra[i], "not_in_filter"); @@ -11992,57 +11893,57 @@ LGraphNode.prototype.executeAction = function(action) } } } - - function inner_test_filter( type, optsIn ) - { + + function inner_test_filter( type, optsIn ) { var optsIn = optsIn || {}; - var optsDef = { skipFilter: false - ,inTypeOverride: false - ,outTypeOverride: false - }; + var optsDef = { + skipFilter: false, + inTypeOverride: false, + outTypeOverride: false, + }; var opts = Object.assign(optsDef,optsIn); - var ctor = LiteGraph.registered_node_types[ type ]; - if(filter && ctor.filter != filter ) - return false; + var ctor = LiteGraph.registered_node_types[type]; + if(filter && ctor.filter != filter ) + return false; if ((!options.show_all_if_empty || str) && type.toLowerCase().indexOf(str) === -1) return false; - + // filter by slot IN, OUT types - if(options.do_type_filter && !opts.skipFilter){ + if(options.do_type_filter && !opts.skipFilter) { var sType = type; - + var sV = sIn.value; if (opts.inTypeOverride!==false) sV = opts.inTypeOverride; - //if (sV.toLowerCase() == "_event_") sV = LiteGraph.EVENT; // -1 - - if(sIn && sV){ - //console.log("will check filter against "+sV); - if (LiteGraph.registered_slot_in_types[sV] && LiteGraph.registered_slot_in_types[sV].nodes){ // type is stored - //console.debug("check "+sType+" in "+LiteGraph.registered_slot_in_types[sV].nodes); + // if (sV.toLowerCase() == "_event_") sV = LiteGraph.EVENT; // -1 + + if(sIn && sV) { + // console.log("will check filter against "+sV); + if (LiteGraph.registered_slot_in_types[sV] && LiteGraph.registered_slot_in_types[sV].nodes) { // type is stored + // console.debug("check "+sType+" in "+LiteGraph.registered_slot_in_types[sV].nodes); var doesInc = LiteGraph.registered_slot_in_types[sV].nodes.includes(sType); - if (doesInc!==false){ - //console.log(sType+" HAS "+sV); + if (doesInc!==false) { + // console.log(sType+" HAS "+sV); }else{ - /*console.debug(LiteGraph.registered_slot_in_types[sV]); + /* console.debug(LiteGraph.registered_slot_in_types[sV]); console.log(+" DONT includes "+type);*/ return false; } } } - + var sV = sOut.value; if (opts.outTypeOverride!==false) sV = opts.outTypeOverride; - //if (sV.toLowerCase() == "_event_") sV = LiteGraph.EVENT; // -1 - - if(sOut && sV){ - //console.log("search will check filter against "+sV); - if (LiteGraph.registered_slot_out_types[sV] && LiteGraph.registered_slot_out_types[sV].nodes){ // type is stored - //console.debug("check "+sType+" in "+LiteGraph.registered_slot_out_types[sV].nodes); + // if (sV.toLowerCase() == "_event_") sV = LiteGraph.EVENT; // -1 + + if(sOut && sV) { + // console.log("search will check filter against "+sV); + if (LiteGraph.registered_slot_out_types[sV] && LiteGraph.registered_slot_out_types[sV].nodes) { // type is stored + // console.debug("check "+sType+" in "+LiteGraph.registered_slot_out_types[sV].nodes); var doesInc = LiteGraph.registered_slot_out_types[sV].nodes.includes(sType); - if (doesInc!==false){ - //console.log(sType+" HAS "+sV); + if (doesInc!==false) { + // console.log(sType+" HAS "+sV); }else{ - /*console.debug(LiteGraph.registered_slot_out_types[sV]); + /* console.debug(LiteGraph.registered_slot_out_types[sV]); console.log(+" DONT includes "+type);*/ return false; } @@ -12050,7 +11951,7 @@ LGraphNode.prototype.executeAction = function(action) } } return true; - } + } } function addResult(type, className) { @@ -12083,7 +11984,7 @@ LGraphNode.prototype.executeAction = function(action) var that = this; var info = node.getPropertyInfo(property); - var type = info.type; + var type = info.type; var input_html = ""; @@ -12093,8 +11994,8 @@ LGraphNode.prototype.executeAction = function(action) input_html = ""; - var textarea = panel.alt_content.querySelector("textarea"); - var fDoneWith = function(){ - panel.toggleAltContent(false); //if(node_prop_div) node_prop_div.style.display = "block"; // panel.close(); - panel.toggleFooterVisibility(true); - textarea.parentNode.removeChild(textarea); - panel.classList.add("settings"); - panel.classList.remove("centered"); - inner_refresh(); - } - textarea.value = node.properties[propname]; - textarea.addEventListener("keydown", function(e){ - if(e.code == "Enter" && e.ctrlKey ) - { - node.setProperty(propname, textarea.value); - fDoneWith(); - } - }); - panel.toggleAltContent(true); - panel.toggleFooterVisibility(false); - textarea.style.height = "calc(100% - 40px)"; - /*}*/ - var assign = panel.addButton( "Assign", function(){ - node.setProperty(propname, textarea.value); + panel.alt_content.innerHTML = ""; + var textarea = panel.alt_content.querySelector("textarea"); + var fDoneWith = function() { + panel.toggleAltContent(false); // if(node_prop_div) node_prop_div.style.display = "block"; // panel.close(); + panel.toggleFooterVisibility(true); + textarea.parentNode.removeChild(textarea); + panel.classList.add("settings"); + panel.classList.remove("centered"); + inner_refresh(); + } + textarea.value = node.properties[propname]; + textarea.addEventListener("keydown", function(e) { + if(e.code == "Enter" && e.ctrlKey ) { + node.setProperty(propname, textarea.value); + fDoneWith(); + } + }); + panel.toggleAltContent(true); + panel.toggleFooterVisibility(false); + textarea.style.height = "calc(100% - 40px)"; + /* }*/ + var assign = panel.addButton( "Assign", function() { + node.setProperty(propname, textarea.value); fDoneWith(); - }); - panel.alt_content.appendChild(assign); //panel.content.appendChild(assign); - var button = panel.addButton( "Close", fDoneWith); - button.style.float = "right"; - panel.alt_content.appendChild(button); // panel.content.appendChild(button); - } + }); + panel.alt_content.appendChild(assign); // panel.content.appendChild(assign); + var button = panel.addButton( "Close", fDoneWith); + button.style.float = "right"; + panel.alt_content.appendChild(button); // panel.content.appendChild(button); + } - inner_refresh(); + inner_refresh(); - this.canvas.parentNode.appendChild( panel ); - } - - LGraphCanvas.prototype.showSubgraphPropertiesDialog = function(node) - { - console.log("showing subgraph properties dialog"); + this.canvas.parentNode.appendChild( panel ); + } - var old_panel = this.canvas.parentNode.querySelector(".subgraph_dialog"); - if(old_panel) - old_panel.close(); + LGraphCanvas.prototype.showSubgraphPropertiesDialog = function(node) { + console.log("showing subgraph properties dialog"); - var panel = this.createPanel("Subgraph Inputs",{closable:true, width: 500}); - panel.node = node; - panel.classList.add("subgraph_dialog"); + var old_panel = this.canvas.parentNode.querySelector(".subgraph_dialog"); + if(old_panel) + old_panel.close(); - function inner_refresh() - { - panel.clear(); + var panel = this.createPanel("Subgraph Inputs",{closable: true, width: 500}); + panel.node = node; + panel.classList.add("subgraph_dialog"); - //show currents - if(node.inputs) - for(var i = 0; i < node.inputs.length; ++i) - { - var input = node.inputs[i]; - if(input.not_subgraph_input) - continue; - var html = " "; - var elem = panel.addHTML(html,"subgraph_property"); - elem.dataset["name"] = input.name; - elem.dataset["slot"] = i; - elem.querySelector(".name").innerText = input.name; - elem.querySelector(".type").innerText = input.type; - elem.querySelector("button").addEventListener("click",function(e){ - node.removeInput( Number( this.parentNode.dataset["slot"] ) ); - inner_refresh(); - }); - } - } + function inner_refresh() { + panel.clear(); - //add extra - var html = " + NameType"; - var elem = panel.addHTML(html,"subgraph_property extra", true); - elem.querySelector("button").addEventListener("click", function(e){ - var elem = this.parentNode; - var name = elem.querySelector(".name").value; - var type = elem.querySelector(".type").value; - if(!name || node.findInputSlot(name) != -1) - return; - node.addInput(name,type); - elem.querySelector(".name").value = ""; - elem.querySelector(".type").value = ""; - inner_refresh(); - }); + // show currents + if(node.inputs) + for(var i = 0; i < node.inputs.length; ++i) { + var input = node.inputs[i]; + if(input.not_subgraph_input) + continue; + var html = " "; + var elem = panel.addHTML(html,"subgraph_property"); + elem.dataset["name"] = input.name; + elem.dataset["slot"] = i; + elem.querySelector(".name").innerText = input.name; + elem.querySelector(".type").innerText = input.type; + elem.querySelector("button").addEventListener("click",function(e) { + node.removeInput( Number( this.parentNode.dataset["slot"] ) ); + inner_refresh(); + }); + } + } - inner_refresh(); - this.canvas.parentNode.appendChild(panel); - return panel; - } + // add extra + var html = " + NameType"; + var elem = panel.addHTML(html,"subgraph_property extra", true); + elem.querySelector("button").addEventListener("click", function(e) { + var elem = this.parentNode; + var name = elem.querySelector(".name").value; + var type = elem.querySelector(".type").value; + if(!name || node.findInputSlot(name) != -1) + return; + node.addInput(name,type); + elem.querySelector(".name").value = ""; + elem.querySelector(".type").value = ""; + inner_refresh(); + }); + + inner_refresh(); + this.canvas.parentNode.appendChild(panel); + return panel; + } LGraphCanvas.prototype.showSubgraphPropertiesDialogRight = function (node) { // console.log("showing subgraph properties dialog"); @@ -12847,7 +12729,7 @@ LGraphNode.prototype.executeAction = function(action) function inner_refresh() { panel.clear(); - //show currents + // show currents if (node.outputs) for (var i = 0; i < node.outputs.length; ++i) { var input = node.outputs[i]; @@ -12866,7 +12748,7 @@ LGraphNode.prototype.executeAction = function(action) } } - //add extra + // add extra var html = " + NameType"; var elem = panel.addHTML(html, "subgraph_property extra", true); elem.querySelector(".name").addEventListener("keydown", function (e) { @@ -12893,38 +12775,36 @@ LGraphNode.prototype.executeAction = function(action) this.canvas.parentNode.appendChild(panel); return panel; } - LGraphCanvas.prototype.checkPanels = function() - { - if(!this.canvas) - return; - var panels = this.canvas.parentNode.querySelectorAll(".litegraph.dialog"); - for(var i = 0; i < panels.length; ++i) - { - var panel = panels[i]; - if( !panel.node ) - continue; - if( !panel.node.graph || panel.graph != this.graph ) - panel.close(); - } - } + LGraphCanvas.prototype.checkPanels = function() { + if(!this.canvas) + return; + var panels = this.canvas.parentNode.querySelectorAll(".litegraph.dialog"); + for(var i = 0; i < panels.length; ++i) { + var panel = panels[i]; + if( !panel.node ) + continue; + if( !panel.node.graph || panel.graph != this.graph ) + panel.close(); + } + } LGraphCanvas.onMenuNodeCollapse = function(value, options, e, menu, node) { - node.graph.beforeChange(/*?*/); - - var fApplyMultiNode = function(node){ - node.collapse(); - } - - var graphcanvas = LGraphCanvas.active_canvas; - if (!graphcanvas.selected_nodes || Object.keys(graphcanvas.selected_nodes).length <= 1){ - fApplyMultiNode(node); - }else{ - for (var i in graphcanvas.selected_nodes) { - fApplyMultiNode(graphcanvas.selected_nodes[i]); - } - } - - node.graph.afterChange(/*?*/); + node.graph.beforeChange(/* ?*/); + + var fApplyMultiNode = function(node) { + node.collapse(); + } + + var graphcanvas = LGraphCanvas.active_canvas; + if (!graphcanvas.selected_nodes || Object.keys(graphcanvas.selected_nodes).length <= 1) { + fApplyMultiNode(node); + }else{ + for (var i in graphcanvas.selected_nodes) { + fApplyMultiNode(graphcanvas.selected_nodes[i]); + } + } + + node.graph.afterChange(/* ?*/); }; LGraphCanvas.onMenuNodePin = function(value, options, e, menu, node) { @@ -12934,7 +12814,7 @@ LGraphNode.prototype.executeAction = function(action) LGraphCanvas.onMenuNodeMode = function(value, options, e, menu, node) { new LiteGraph.ContextMenu( LiteGraph.NODE_MODES, - { event: e, callback: inner_clicked, parentMenu: menu, node: node } + { event: e, callback: inner_clicked, parentMenu: menu, node: node }, ); function inner_clicked(v) { @@ -12942,23 +12822,23 @@ LGraphNode.prototype.executeAction = function(action) return; } var kV = Object.values(LiteGraph.NODE_MODES).indexOf(v); - var fApplyMultiNode = function(node){ - if (kV>=0 && LiteGraph.NODE_MODES[kV]) - node.changeMode(kV); - else{ - console.warn("unexpected mode: "+v); - node.changeMode(LiteGraph.ALWAYS); - } - } - - var graphcanvas = LGraphCanvas.active_canvas; - if (!graphcanvas.selected_nodes || Object.keys(graphcanvas.selected_nodes).length <= 1){ - fApplyMultiNode(node); - }else{ - for (var i in graphcanvas.selected_nodes) { - fApplyMultiNode(graphcanvas.selected_nodes[i]); - } - } + var fApplyMultiNode = function(node) { + if (kV>=0 && LiteGraph.NODE_MODES[kV]) + node.changeMode(kV); + else{ + console.warn("unexpected mode: "+v); + node.changeMode(LiteGraph.ALWAYS); + } + } + + var graphcanvas = LGraphCanvas.active_canvas; + if (!graphcanvas.selected_nodes || Object.keys(graphcanvas.selected_nodes).length <= 1) { + fApplyMultiNode(node); + }else{ + for (var i in graphcanvas.selected_nodes) { + fApplyMultiNode(graphcanvas.selected_nodes[i]); + } + } } return false; @@ -12973,7 +12853,7 @@ LGraphNode.prototype.executeAction = function(action) values.push({ value: null, content: - "No color" + "No color", }); for (var i in LGraphCanvas.node_colors) { @@ -12987,7 +12867,7 @@ LGraphNode.prototype.executeAction = function(action) color.bgcolor + "'>" + i + - "" + "", }; values.push(value); } @@ -12995,7 +12875,7 @@ LGraphNode.prototype.executeAction = function(action) event: e, callback: inner_clicked, parentMenu: menu, - node: node + node: node, }); function inner_clicked(v) { @@ -13004,29 +12884,29 @@ LGraphNode.prototype.executeAction = function(action) } var color = v.value ? LGraphCanvas.node_colors[v.value] : null; - - var fApplyColor = function(node){ - if (color) { - if (node.constructor === LiteGraph.LGraphGroup) { - node.color = color.groupcolor; - } else { - node.color = color.color; - node.bgcolor = color.bgcolor; - } - } else { - delete node.color; - delete node.bgcolor; - } - } - - var graphcanvas = LGraphCanvas.active_canvas; - if (!graphcanvas.selected_nodes || Object.keys(graphcanvas.selected_nodes).length <= 1){ - fApplyColor(node); - }else{ - for (var i in graphcanvas.selected_nodes) { - fApplyColor(graphcanvas.selected_nodes[i]); - } - } + + var fApplyColor = function(node) { + if (color) { + if (node.constructor === LiteGraph.LGraphGroup) { + node.color = color.groupcolor; + } else { + node.color = color.color; + node.bgcolor = color.bgcolor; + } + } else { + delete node.color; + delete node.bgcolor; + } + } + + var graphcanvas = LGraphCanvas.active_canvas; + if (!graphcanvas.selected_nodes || Object.keys(graphcanvas.selected_nodes).length <= 1) { + fApplyColor(node); + }else{ + for (var i in graphcanvas.selected_nodes) { + fApplyColor(graphcanvas.selected_nodes[i]); + } + } node.setDirtyCanvas(true, true); } @@ -13042,29 +12922,29 @@ LGraphNode.prototype.executeAction = function(action) event: e, callback: inner_clicked, parentMenu: menu, - node: node + node: node, }); function inner_clicked(v) { if (!node) { return; } - node.graph.beforeChange(/*?*/); //node - - var fApplyMultiNode = function(node){ - node.shape = v; - } + node.graph.beforeChange(/* ?*/); // node - var graphcanvas = LGraphCanvas.active_canvas; - if (!graphcanvas.selected_nodes || Object.keys(graphcanvas.selected_nodes).length <= 1){ - fApplyMultiNode(node); - }else{ - for (var i in graphcanvas.selected_nodes) { - fApplyMultiNode(graphcanvas.selected_nodes[i]); - } - } - - node.graph.afterChange(/*?*/); //node + var fApplyMultiNode = function(node) { + node.shape = v; + } + + var graphcanvas = LGraphCanvas.active_canvas; + if (!graphcanvas.selected_nodes || Object.keys(graphcanvas.selected_nodes).length <= 1) { + fApplyMultiNode(node); + }else{ + for (var i in graphcanvas.selected_nodes) { + fApplyMultiNode(graphcanvas.selected_nodes[i]); + } + } + + node.graph.afterChange(/* ?*/); // node node.setDirtyCanvas(true); } @@ -13076,83 +12956,83 @@ LGraphNode.prototype.executeAction = function(action) throw "no node passed"; } - var graph = node.graph; - graph.beforeChange(); - - - var fApplyMultiNode = function(node){ - if (node.removable === false) { - return; - } - graph.remove(node); - } + var graph = node.graph; + graph.beforeChange(); - var graphcanvas = LGraphCanvas.active_canvas; - if (!graphcanvas.selected_nodes || Object.keys(graphcanvas.selected_nodes).length <= 1){ - fApplyMultiNode(node); - }else{ - for (var i in graphcanvas.selected_nodes) { - fApplyMultiNode(graphcanvas.selected_nodes[i]); - } - } - - graph.afterChange(); + + var fApplyMultiNode = function(node) { + if (node.removable === false) { + return; + } + graph.remove(node); + } + + var graphcanvas = LGraphCanvas.active_canvas; + if (!graphcanvas.selected_nodes || Object.keys(graphcanvas.selected_nodes).length <= 1) { + fApplyMultiNode(node); + }else{ + for (var i in graphcanvas.selected_nodes) { + fApplyMultiNode(graphcanvas.selected_nodes[i]); + } + } + + graph.afterChange(); node.setDirtyCanvas(true, true); }; LGraphCanvas.onMenuNodeToSubgraph = function(value, options, e, menu, node) { - var graph = node.graph; - var graphcanvas = LGraphCanvas.active_canvas; - if(!graphcanvas) //?? - return; + var graph = node.graph; + var graphcanvas = LGraphCanvas.active_canvas; + if(!graphcanvas) // ?? + return; - var nodes_list = Object.values( graphcanvas.selected_nodes || {} ); - if( !nodes_list.length ) - nodes_list = [ node ]; + var nodes_list = Object.values( graphcanvas.selected_nodes || {} ); + if( !nodes_list.length ) + nodes_list = [ node ]; - var subgraph_node = LiteGraph.createNode("graph/subgraph"); - subgraph_node.pos = node.pos.concat(); - graph.add(subgraph_node); + var subgraph_node = LiteGraph.createNode("graph/subgraph"); + subgraph_node.pos = node.pos.concat(); + graph.add(subgraph_node); - subgraph_node.buildFromNodes( nodes_list ); + subgraph_node.buildFromNodes( nodes_list ); - graphcanvas.deselectAllNodes(); + graphcanvas.deselectAllNodes(); node.setDirtyCanvas(true, true); }; LGraphCanvas.onMenuNodeClone = function(value, options, e, menu, node) { - - node.graph.beforeChange(); - - var newSelected = {}; - - var fApplyMultiNode = function(node){ - if (node.clonable === false) { - return; - } - var newnode = node.clone(); - if (!newnode) { - return; - } - newnode.pos = [node.pos[0] + 5, node.pos[1] + 5]; - node.graph.add(newnode); - newSelected[newnode.id] = newnode; - } - var graphcanvas = LGraphCanvas.active_canvas; - if (!graphcanvas.selected_nodes || Object.keys(graphcanvas.selected_nodes).length <= 1){ - fApplyMultiNode(node); - }else{ - for (var i in graphcanvas.selected_nodes) { - fApplyMultiNode(graphcanvas.selected_nodes[i]); - } - } - - if(Object.keys(newSelected).length){ - graphcanvas.selectNodes(newSelected); - } - - node.graph.afterChange(); + node.graph.beforeChange(); + + var newSelected = {}; + + var fApplyMultiNode = function(node) { + if (node.clonable === false) { + return; + } + var newnode = node.clone(); + if (!newnode) { + return; + } + newnode.pos = [node.pos[0] + 5, node.pos[1] + 5]; + node.graph.add(newnode); + newSelected[newnode.id] = newnode; + } + + var graphcanvas = LGraphCanvas.active_canvas; + if (!graphcanvas.selected_nodes || Object.keys(graphcanvas.selected_nodes).length <= 1) { + fApplyMultiNode(node); + }else{ + for (var i in graphcanvas.selected_nodes) { + fApplyMultiNode(graphcanvas.selected_nodes[i]); + } + } + + if(Object.keys(newSelected).length) { + graphcanvas.selectNodes(newSelected); + } + + node.graph.afterChange(); node.setDirtyCanvas(true, true); }; @@ -13165,17 +13045,17 @@ LGraphNode.prototype.executeAction = function(action) pale_blue: { color: "#2a363b", bgcolor: "#3f5159", - groupcolor: "#3f789e" + groupcolor: "#3f789e", }, cyan: { color: "#233", bgcolor: "#355", groupcolor: "#8AA" }, purple: { color: "#323", bgcolor: "#535", groupcolor: "#a1309b" }, yellow: { color: "#432", bgcolor: "#653", groupcolor: "#b58b2a" }, - black: { color: "#222", bgcolor: "#000", groupcolor: "#444" } + black: { color: "#222", bgcolor: "#000", groupcolor: "#444" }, }; LGraphCanvas.prototype.getCanvasMenuOptions = function() { var options = null; - var that = this; + var that = this; if (this.getMenuOptions) { options = this.getMenuOptions(); } else { @@ -13183,13 +13063,13 @@ LGraphNode.prototype.executeAction = function(action) { content: "Add Node", has_submenu: true, - callback: LGraphCanvas.onMenuAdd + callback: LGraphCanvas.onMenuAdd, }, { content: "Add Group", callback: LGraphCanvas.onGroupAdd }, - //{ content: "Arrange", callback: that.graph.arrange }, - //{content:"Collapse All", callback: LGraphCanvas.onMenuCollapseAll } + // { content: "Arrange", callback: that.graph.arrange }, + // {content:"Collapse All", callback: LGraphCanvas.onMenuCollapseAll } ]; - /*if (LiteGraph.showCanvasOptions){ + /* if (LiteGraph.showCanvasOptions){ options.push({ content: "Options", callback: that.showShowGraphOptionsPanel }); }*/ @@ -13204,7 +13084,7 @@ LGraphNode.prototype.executeAction = function(action) if (this._graph_stack && this._graph_stack.length > 0) { options.push(null, { content: "Close subgraph", - callback: this.closeSubgraph.bind(this) + callback: this.closeSubgraph.bind(this), }); } } @@ -13219,7 +13099,7 @@ LGraphNode.prototype.executeAction = function(action) return options; }; - //called by processContextMenu to extract the menu list + // called by processContextMenu to extract the menu list LGraphCanvas.prototype.getNodeMenuOptions = function(node) { var options = null; @@ -13231,52 +13111,50 @@ LGraphNode.prototype.executeAction = function(action) content: "Inputs", has_submenu: true, disabled: true, - callback: LGraphCanvas.showMenuNodeOptionalInputs + callback: LGraphCanvas.showMenuNodeOptionalInputs, }, { content: "Outputs", has_submenu: true, disabled: true, - callback: LGraphCanvas.showMenuNodeOptionalOutputs + callback: LGraphCanvas.showMenuNodeOptionalOutputs, }, null, { content: "Properties", has_submenu: true, - callback: LGraphCanvas.onShowMenuNodeProperties + callback: LGraphCanvas.onShowMenuNodeProperties, }, null, { content: "Title", - callback: LGraphCanvas.onShowPropertyEditor + callback: LGraphCanvas.onShowPropertyEditor, }, { content: "Mode", has_submenu: true, - callback: LGraphCanvas.onMenuNodeMode + callback: LGraphCanvas.onMenuNodeMode, }]; - if(node.resizable !== false){ - options.push({ - content: "Resize", callback: LGraphCanvas.onMenuResizeNode - }); + if(node.resizable !== false) { + options.push({content: "Resize", callback: LGraphCanvas.onMenuResizeNode}); } options.push( { content: "Collapse", - callback: LGraphCanvas.onMenuNodeCollapse + callback: LGraphCanvas.onMenuNodeCollapse, }, { content: "Pin", callback: LGraphCanvas.onMenuNodePin }, { content: "Colors", has_submenu: true, - callback: LGraphCanvas.onMenuNodeColors + callback: LGraphCanvas.onMenuNodeColors, }, { content: "Shapes", has_submenu: true, - callback: LGraphCanvas.onMenuNodeShapes + callback: LGraphCanvas.onMenuNodeShapes, }, - null + null, ); } @@ -13305,16 +13183,16 @@ LGraphNode.prototype.executeAction = function(action) if (node.clonable !== false) { options.push({ content: "Clone", - callback: LGraphCanvas.onMenuNodeClone + callback: LGraphCanvas.onMenuNodeClone, }); } - - if(0) //TODO - options.push({ - content: "To Subgraph", - callback: LGraphCanvas.onMenuNodeToSubgraph - }); - + /* + if(0) // TODO + options.push({ + content: "To Subgraph", + callback: LGraphCanvas.onMenuNodeToSubgraph, + }); + */ if (Object.keys(this.selected_nodes).length > 1) { options.push({ content: "Align Selected To", @@ -13323,11 +13201,11 @@ LGraphNode.prototype.executeAction = function(action) }) } - options.push(null, { - content: "Remove", - disabled: !(node.removable !== false && !node.block_delete ), - callback: LGraphCanvas.onMenuNodeRemove - }); + options.push(null, { + content: "Remove", + disabled: !(node.removable !== false && !node.block_delete ), + callback: LGraphCanvas.onMenuNodeRemove, + }); if (node.graph && node.graph.onGetNodeMenuOptions) { node.graph.onGetNodeMenuOptions(options, node); @@ -13342,16 +13220,16 @@ LGraphNode.prototype.executeAction = function(action) { content: "Color", has_submenu: true, - callback: LGraphCanvas.onMenuNodeColors + callback: LGraphCanvas.onMenuNodeColors, }, { content: "Font size", property: "font_size", type: "Number", - callback: LGraphCanvas.onShowPropertyEditor + callback: LGraphCanvas.onShowPropertyEditor, }, null, - { content: "Remove", callback: LGraphCanvas.onMenuNodeRemove } + { content: "Remove", callback: LGraphCanvas.onMenuNodeRemove }, ]; return o; @@ -13366,13 +13244,13 @@ LGraphNode.prototype.executeAction = function(action) var options = { event: event, callback: inner_option_clicked, - extra: node + extra: node, }; - if(node) - options.title = node.type; + if(node) + options.title = node.type; - //check if mouse is in input + // check if mouse is in input var slot = null; if (node) { slot = node.getSlotInPosition(event.canvasX, event.canvasY); @@ -13380,7 +13258,7 @@ LGraphNode.prototype.executeAction = function(action) } if (slot) { - //on slot + // on slot menu_info = []; if (node.getSlotMenuOptions) { menu_info = node.getSlotMenuOptions(slot); @@ -13394,17 +13272,15 @@ LGraphNode.prototype.executeAction = function(action) menu_info.push({ content: "Disconnect Links", slot: slot }); } var _slot = slot.input || slot.output; - if (_slot.removable){ - menu_info.push( - _slot.locked - ? "Cannot remove" - : { content: "Remove Slot", slot: slot } - ); - } - if (!_slot.nameLocked){ - menu_info.push({ content: "Rename Slot", slot: slot }); - } - + if (_slot.removable) { + menu_info.push(_slot.locked + ? "Cannot remove" + : { content: "Remove Slot", slot: slot }); + } + if (!_slot.nameLocked) { + menu_info.push({ content: "Rename Slot", slot: slot }); + } + } options.title = (slot.input ? slot.input.type : slot.output.type) || "*"; @@ -13416,30 +13292,30 @@ LGraphNode.prototype.executeAction = function(action) } } else { if (node) { - //on node + // on node menu_info = this.getNodeMenuOptions(node); } else { menu_info = this.getCanvasMenuOptions(); var group = this.graph.getGroupOnPos( event.canvasX, - event.canvasY + event.canvasY, ); if (group) { - //on group + // on group menu_info.push(null, { content: "Edit Group", has_submenu: true, submenu: { title: "Group", extra: group, - options: this.getGroupMenuOptions(group) - } + options: this.getGroupMenuOptions(group), + }, }); } } } - //show menu + // show menu if (!menu_info) { return; } @@ -13478,14 +13354,14 @@ LGraphNode.prototype.executeAction = function(action) : node.getOutputInfo(info.slot); var dialog = that.createDialog( "Name", - options + options, ); var input = dialog.querySelector("input"); if (input && slot_info) { input.value = slot_info.label || ""; } - var inner = function(){ - node.graph.beforeChange(); + var inner = function() { + node.graph.beforeChange(); if (input.value) { if (slot_info) { slot_info.label = input.value; @@ -13499,7 +13375,7 @@ LGraphNode.prototype.executeAction = function(action) input.addEventListener("keydown", function(e) { dialog.is_modified = true; if (e.keyCode == 27) { - //ESC + // ESC dialog.close(); } else if (e.keyCode == 13) { inner(); // save @@ -13512,12 +13388,12 @@ LGraphNode.prototype.executeAction = function(action) input.focus(); } - //if(v.callback) + // if(v.callback) // return v.callback.call(that, node, options, e, menu, that, event ); } }; - //API ************************************************* + // API ************************************************* function compareObjects(a, b) { for (var i in a) { if (a[i] != b[i]) { @@ -13529,9 +13405,7 @@ LGraphNode.prototype.executeAction = function(action) LiteGraph.compareObjects = compareObjects; function distance(a, b) { - return Math.sqrt( - (b[0] - a[0]) * (b[0] - a[0]) + (b[1] - a[1]) * (b[1] - a[1]) - ); + return Math.sqrt((b[0] - a[0]) * (b[0] - a[0]) + (b[1] - a[1]) * (b[1] - a[1])); } LiteGraph.distance = distance; @@ -13558,7 +13432,7 @@ LGraphNode.prototype.executeAction = function(action) } LiteGraph.isInsideRectangle = isInsideRectangle; - //[minx,miny,maxx,maxy] + // [minx,miny,maxx,maxy] function growBounding(bounding, x, y) { if (x < bounding[0]) { bounding[0] = x; @@ -13574,7 +13448,7 @@ LGraphNode.prototype.executeAction = function(action) } LiteGraph.growBounding = growBounding; - //point inside bounding box + // point inside bounding box function isInsideBounding(p, bb) { if ( p[0] < bb[0][0] || @@ -13588,7 +13462,7 @@ LGraphNode.prototype.executeAction = function(action) } LiteGraph.isInsideBounding = isInsideBounding; - //bounding overlap, format: [ startx, starty, width, height ] + // bounding overlap, format: [ startx, starty, width, height ] function overlapBounding(a, b) { var A_end_x = a[0] + a[2]; var A_end_y = a[1] + a[3]; @@ -13607,13 +13481,13 @@ LGraphNode.prototype.executeAction = function(action) } LiteGraph.overlapBounding = overlapBounding; - //Convert a hex value to its decimal value - the inputted hex must be in the + // Convert a hex value to its decimal value - the inputted hex must be in the // format of a hex triplet - the kind we use for HTML colours. The function // will return an array with three values. function hex2num(hex) { if (hex.charAt(0) == "#") { hex = hex.slice(1); - } //Remove the '#' char - if there is one. + } // Remove the '#' char - if there is one. hex = hex.toUpperCase(); var hex_alphabets = "0123456789ABCDEF"; var value = new Array(3); @@ -13630,7 +13504,7 @@ LGraphNode.prototype.executeAction = function(action) LiteGraph.hex2num = hex2num; - //Give a array with three values as the argument and the function will return + // Give a array with three values as the argument and the function will return // the corresponding hex triplet. function num2hex(triplet) { var hex_alphabets = "0123456789ABCDEF"; @@ -13666,12 +13540,10 @@ LGraphNode.prototype.executeAction = function(action) this.options = options; var that = this; - //to link a menu with its parent + // to link a menu with its parent if (options.parentMenu) { if (options.parentMenu.constructor !== this.constructor) { - console.error( - "parentMenu must be of class ContextMenu, ignoring it" - ); + console.error("parentMenu must be of class ContextMenu, ignoring it"); options.parentMenu = null; } else { this.parentMenu = options.parentMenu; @@ -13680,16 +13552,14 @@ LGraphNode.prototype.executeAction = function(action) } } - var eventClass = null; - if(options.event) //use strings because comparing classes between windows doesnt work - eventClass = options.event.constructor.name; + var eventClass = null; + if(options.event) // use strings because comparing classes between windows doesnt work + eventClass = options.event.constructor.name; if ( eventClass !== "MouseEvent" && eventClass !== "CustomEvent" && eventClass !== "PointerEvent" ) { - console.error( - "Event passed to ContextMenu is not of type MouseEvent or CustomEvent. Ignoring it. ("+eventClass+")" - ); + console.error("Event passed to ContextMenu is not of type MouseEvent or CustomEvent. Ignoring it. ("+eventClass+")"); options.event = null; } @@ -13703,40 +13573,42 @@ LGraphNode.prototype.executeAction = function(action) root.style.pointerEvents = "none"; setTimeout(function() { root.style.pointerEvents = "auto"; - }, 100); //delay so the mouse up event is not caught by this element + }, 100); // delay so the mouse up event is not caught by this element - //this prevents the default context browser menu to open in case this menu was created when pressing right button - LiteGraph.pointerListenerAdd(root,"up", + // this prevents the default context browser menu to open in case this menu was created when pressing right button + LiteGraph.pointerListenerAdd( + root,"up", function(e) { - //console.log("pointerevents: ContextMenu up root prevent"); + // console.log("pointerevents: ContextMenu up root prevent"); e.preventDefault(); return true; }, - true + true, ); root.addEventListener( "contextmenu", function(e) { if (e.button != 2) { - //right button + // right button return false; } e.preventDefault(); return false; }, - true + true, ); - LiteGraph.pointerListenerAdd(root,"down", + LiteGraph.pointerListenerAdd( + root,"down", function(e) { - //console.log("pointerevents: ContextMenu down"); + // console.log("pointerevents: ContextMenu down"); if (e.button == 2) { that.close(); e.preventDefault(); return true; } }, - true + true, ); function on_mouse_wheel(e) { @@ -13756,7 +13628,7 @@ LGraphNode.prototype.executeAction = function(action) this.root = root; - //title + // title if (options.title) { var element = document.createElement("div"); element.className = "litemenu-title"; @@ -13764,7 +13636,7 @@ LGraphNode.prototype.executeAction = function(action) root.appendChild(element); } - //entries + // entries var num = 0; for (var i=0; i < values.length; i++) { var name = values.constructor == Array ? values[i] : i; @@ -13776,8 +13648,8 @@ LGraphNode.prototype.executeAction = function(action) num++; } - //close on leave? touch enabled devices won't work TODO use a global device detector and condition on that - /*LiteGraph.pointerListenerAdd(root,"leave", function(e) { + // close on leave? touch enabled devices won't work TODO use a global device detector and condition on that + /* LiteGraph.pointerListenerAdd(root,"leave", function(e) { console.log("pointerevents: ContextMenu leave"); if (that.lock) { return; @@ -13789,14 +13661,14 @@ LGraphNode.prototype.executeAction = function(action) //that.close(e); });*/ - LiteGraph.pointerListenerAdd(root,"enter", function(e) { - //console.log("pointerevents: ContextMenu enter"); + LiteGraph.pointerListenerAdd(root,"enter", function(e) { + // console.log("pointerevents: ContextMenu enter"); if (root.closing_timer) { clearTimeout(root.closing_timer); } }); - //insert before checking position + // insert before checking position var root_document = document; if (options.event) { root_document = options.event.target.ownerDocument; @@ -13806,12 +13678,12 @@ LGraphNode.prototype.executeAction = function(action) root_document = document; } - if( root_document.fullscreenElement ) - root_document.fullscreenElement.appendChild(root); - else - root_document.body.appendChild(root); + if( root_document.fullscreenElement ) + root_document.fullscreenElement.appendChild(root); + else + root_document.body.appendChild(root); - //compute best position + // compute best position var left = options.left || 0; var top = options.top || 0; if (options.event) { @@ -13828,8 +13700,8 @@ LGraphNode.prototype.executeAction = function(action) var body_rect = document.body.getBoundingClientRect(); var root_rect = root.getBoundingClientRect(); - if(body_rect.height == 0) - console.error("document.body height is 0. That is dangerous, set html,body { height: 100%; }"); + if(body_rect.height == 0) + console.error("document.body height is 0. That is dangerous, set html,body { height: 100%; }"); if (body_rect.width && left > body_rect.width - root_rect.width - 10) { left = body_rect.width - root_rect.width - 10; @@ -13858,8 +13730,8 @@ LGraphNode.prototype.executeAction = function(action) if (value === null) { element.classList.add("separator"); - //element.innerHTML = "
" - //continue; + // element.innerHTML = "
" + // continue; } else { element.innerHTML = value && value.title ? value.title : name; element.value = value; @@ -13891,7 +13763,7 @@ LGraphNode.prototype.executeAction = function(action) element.addEventListener("click", inner_onclick); } if (!disabled && options.autoopen) { - LiteGraph.pointerListenerAdd(element,"enter",inner_over); + LiteGraph.pointerListenerAdd(element,"enter",inner_over); } function inner_over(e) { @@ -13899,11 +13771,11 @@ LGraphNode.prototype.executeAction = function(action) if (!value || !value.has_submenu) { return; } - //if it is a submenu, autoopen like the item was clicked + // if it is a submenu, autoopen like the item was clicked inner_onclick.call(this, e); } - //menu option clicked + // menu option clicked function inner_onclick(e) { var value = this.value; var close_parent = true; @@ -13912,7 +13784,7 @@ LGraphNode.prototype.executeAction = function(action) that.current_submenu.close(e); } - //global callback + // global callback if (options.callback) { var r = options.callback.call( this, @@ -13920,28 +13792,28 @@ LGraphNode.prototype.executeAction = function(action) options, e, that, - options.node + options.node, ); if (r === true) { close_parent = false; } } - //special cases + // special cases if (value) { if ( value.callback && !options.ignore_item_callbacks && value.disabled !== true ) { - //item callback + // item callback var r = value.callback.call( this, value, options, e, that, - options.extra + options.extra, ); if (r === true) { close_parent = false; @@ -13959,7 +13831,7 @@ LGraphNode.prototype.executeAction = function(action) value.submenu.ignore_item_callbacks, title: value.submenu.title, extra: value.submenu.extra, - autoopen: options.autoopen + autoopen: options.autoopen, }); close_parent = false; } @@ -13996,26 +13868,26 @@ LGraphNode.prototype.executeAction = function(action) if (this.root.closing_timer) { clearTimeout(this.root.closing_timer); } - + // TODO implement : LiteGraph.contextMenuClosed(); :: keep track of opened / closed / current ContextMenu // on key press, allow filtering/selecting the context menu elements }; - //this code is used to trigger events easily (used in the context menu mouseleave + // this code is used to trigger events easily (used in the context menu mouseleave ContextMenu.trigger = function(element, event_name, params, origin) { var evt = document.createEvent("CustomEvent"); - evt.initCustomEvent(event_name, true, true, params); //canBubble, cancelable, detail + evt.initCustomEvent(event_name, true, true, params); // canBubble, cancelable, detail evt.srcElement = origin; if (element.dispatchEvent) { element.dispatchEvent(evt); } else if (element.__events) { element.__events.dispatchEvent(evt); } - //else nothing seems binded here so nothing to do + // else nothing seems binded here so nothing to do return evt; }; - //returns the top most menu + // returns the top most menu ContextMenu.prototype.getTopMenu = function() { if (this.options.parentMenu) { return this.options.parentMenu.getTopMenu(); @@ -14074,7 +13946,7 @@ LGraphNode.prototype.executeAction = function(action) LiteGraph.extendClass = function(target, origin) { for (var i in origin) { - //copy class properties + // copy class properties if (target.hasOwnProperty(i)) { continue; } @@ -14082,218 +13954,207 @@ LGraphNode.prototype.executeAction = function(action) } if (origin.prototype) { - //copy prototype properties + // copy prototype properties for (var i in origin.prototype) { - //only enumerable + // only enumerable if (!origin.prototype.hasOwnProperty(i)) { continue; } if (target.prototype.hasOwnProperty(i)) { - //avoid overwriting existing ones + // avoid overwriting existing ones continue; } - //copy getters + // copy getters if (origin.prototype.__lookupGetter__(i)) { target.prototype.__defineGetter__( i, - origin.prototype.__lookupGetter__(i) + origin.prototype.__lookupGetter__(i), ); } else { target.prototype[i] = origin.prototype[i]; } - //and setters + // and setters if (origin.prototype.__lookupSetter__(i)) { target.prototype.__defineSetter__( i, - origin.prototype.__lookupSetter__(i) + origin.prototype.__lookupSetter__(i), ); } } } }; - //used by some widgets to render a curve editor - function CurveEditor( points ) - { - this.points = points; - this.selected = -1; - this.nearest = -1; - this.size = null; //stores last size used - this.must_update = true; - this.margin = 5; - } + // used by some widgets to render a curve editor + function CurveEditor( points ) { + this.points = points; + this.selected = -1; + this.nearest = -1; + this.size = null; // stores last size used + this.must_update = true; + this.margin = 5; + } - CurveEditor.sampleCurve = function(f,points) - { - if(!points) - return; - for(var i = 0; i < points.length - 1; ++i) - { - var p = points[i]; - var pn = points[i+1]; - if(pn[0] < f) - continue; - var r = (pn[0] - p[0]); - if( Math.abs(r) < 0.00001 ) - return p[1]; - var local_f = (f - p[0]) / r; - return p[1] * (1.0 - local_f) + pn[1] * local_f; - } - return 0; - } + CurveEditor.sampleCurve = function(f,points) { + if(!points) + return; + for(var i = 0; i < points.length - 1; ++i) { + var p = points[i]; + var pn = points[i+1]; + if(pn[0] < f) + continue; + var r = (pn[0] - p[0]); + if( Math.abs(r) < 0.00001 ) + return p[1]; + var local_f = (f - p[0]) / r; + return p[1] * (1.0 - local_f) + pn[1] * local_f; + } + return 0; + } - CurveEditor.prototype.draw = function( ctx, size, graphcanvas, background_color, line_color, inactive ) - { - var points = this.points; - if(!points) - return; - this.size = size; - var w = size[0] - this.margin * 2; - var h = size[1] - this.margin * 2; + CurveEditor.prototype.draw = function( ctx, size, graphcanvas, background_color, line_color, inactive ) { + var points = this.points; + if(!points) + return; + this.size = size; + var w = size[0] - this.margin * 2; + var h = size[1] - this.margin * 2; - line_color = line_color || "#666"; + line_color = line_color || "#666"; - ctx.save(); - ctx.translate(this.margin,this.margin); + ctx.save(); + ctx.translate(this.margin,this.margin); - if(background_color) - { - ctx.fillStyle = "#111"; - ctx.fillRect(0,0,w,h); - ctx.fillStyle = "#222"; - ctx.fillRect(w*0.5,0,1,h); - ctx.strokeStyle = "#333"; - ctx.strokeRect(0,0,w,h); - } - ctx.strokeStyle = line_color; - if(inactive) - ctx.globalAlpha = 0.5; - ctx.beginPath(); - for(var i = 0; i < points.length; ++i) - { - var p = points[i]; - ctx.lineTo( p[0] * w, (1.0 - p[1]) * h ); - } - ctx.stroke(); - ctx.globalAlpha = 1; - if(!inactive) - for(var i = 0; i < points.length; ++i) - { - var p = points[i]; - ctx.fillStyle = this.selected == i ? "#FFF" : (this.nearest == i ? "#DDD" : "#AAA"); - ctx.beginPath(); - ctx.arc( p[0] * w, (1.0 - p[1]) * h, 2, 0, Math.PI * 2 ); - ctx.fill(); - } - ctx.restore(); - } + if(background_color) { + ctx.fillStyle = "#111"; + ctx.fillRect(0,0,w,h); + ctx.fillStyle = "#222"; + ctx.fillRect(w*0.5,0,1,h); + ctx.strokeStyle = "#333"; + ctx.strokeRect(0,0,w,h); + } + ctx.strokeStyle = line_color; + if(inactive) + ctx.globalAlpha = 0.5; + ctx.beginPath(); + for(var i = 0; i < points.length; ++i) { + var p = points[i]; + ctx.lineTo( p[0] * w, (1.0 - p[1]) * h ); + } + ctx.stroke(); + ctx.globalAlpha = 1; + if(!inactive) + for(var i = 0; i < points.length; ++i) { + var p = points[i]; + ctx.fillStyle = this.selected == i ? "#FFF" : (this.nearest == i ? "#DDD" : "#AAA"); + ctx.beginPath(); + ctx.arc( p[0] * w, (1.0 - p[1]) * h, 2, 0, Math.PI * 2 ); + ctx.fill(); + } + ctx.restore(); + } - //localpos is mouse in curve editor space - CurveEditor.prototype.onMouseDown = function( localpos, graphcanvas ) - { - var points = this.points; - if(!points) - return; - if( localpos[1] < 0 ) - return; + // localpos is mouse in curve editor space + CurveEditor.prototype.onMouseDown = function( localpos, graphcanvas ) { + var points = this.points; + if(!points) + return; + if( localpos[1] < 0 ) + return; - //this.captureInput(true); - var w = this.size[0] - this.margin * 2; - var h = this.size[1] - this.margin * 2; - var x = localpos[0] - this.margin; - var y = localpos[1] - this.margin; - var pos = [x,y]; - var max_dist = 30 / graphcanvas.ds.scale; - //search closer one - this.selected = this.getCloserPoint(pos, max_dist); - //create one - if(this.selected == -1) - { - var point = [x / w, 1 - y / h]; - points.push(point); - points.sort(function(a,b){ return a[0] - b[0]; }); - this.selected = points.indexOf(point); - this.must_update = true; - } - if(this.selected != -1) - return true; - } + // this.captureInput(true); + var w = this.size[0] - this.margin * 2; + var h = this.size[1] - this.margin * 2; + var x = localpos[0] - this.margin; + var y = localpos[1] - this.margin; + var pos = [x,y]; + var max_dist = 30 / graphcanvas.ds.scale; + // search closer one + this.selected = this.getCloserPoint(pos, max_dist); + // create one + if(this.selected == -1) { + var point = [x / w, 1 - y / h]; + points.push(point); + points.sort(function(a,b) { + return a[0] - b[0]; + }); + this.selected = points.indexOf(point); + this.must_update = true; + } + if(this.selected != -1) + return true; + } - CurveEditor.prototype.onMouseMove = function( localpos, graphcanvas ) - { - var points = this.points; - if(!points) - return; - var s = this.selected; - if(s < 0) - return; - var x = (localpos[0] - this.margin) / (this.size[0] - this.margin * 2 ); - var y = (localpos[1] - this.margin) / (this.size[1] - this.margin * 2 ); - var curvepos = [(localpos[0] - this.margin),(localpos[1] - this.margin)]; - var max_dist = 30 / graphcanvas.ds.scale; - this._nearest = this.getCloserPoint(curvepos, max_dist); - var point = points[s]; - if(point) - { - var is_edge_point = s == 0 || s == points.length - 1; - if( !is_edge_point && (localpos[0] < -10 || localpos[0] > this.size[0] + 10 || localpos[1] < -10 || localpos[1] > this.size[1] + 10) ) - { - points.splice(s,1); - this.selected = -1; - return; - } - if( !is_edge_point ) //not edges - point[0] = clamp(x, 0, 1); - else - point[0] = s == 0 ? 0 : 1; - point[1] = 1.0 - clamp(y, 0, 1); - points.sort(function(a,b){ return a[0] - b[0]; }); - this.selected = points.indexOf(point); - this.must_update = true; - } - } + CurveEditor.prototype.onMouseMove = function( localpos, graphcanvas ) { + var points = this.points; + if(!points) + return; + var s = this.selected; + if(s < 0) + return; + var x = (localpos[0] - this.margin) / (this.size[0] - this.margin * 2 ); + var y = (localpos[1] - this.margin) / (this.size[1] - this.margin * 2 ); + var curvepos = [(localpos[0] - this.margin),(localpos[1] - this.margin)]; + var max_dist = 30 / graphcanvas.ds.scale; + this._nearest = this.getCloserPoint(curvepos, max_dist); + var point = points[s]; + if(point) { + var is_edge_point = s == 0 || s == points.length - 1; + if( !is_edge_point && (localpos[0] < -10 || localpos[0] > this.size[0] + 10 || localpos[1] < -10 || localpos[1] > this.size[1] + 10) ) { + points.splice(s,1); + this.selected = -1; + return; + } + if( !is_edge_point ) // not edges + point[0] = clamp(x, 0, 1); + else + point[0] = s == 0 ? 0 : 1; + point[1] = 1.0 - clamp(y, 0, 1); + points.sort(function(a,b) { + return a[0] - b[0]; + }); + this.selected = points.indexOf(point); + this.must_update = true; + } + } - CurveEditor.prototype.onMouseUp = function( localpos, graphcanvas ) - { - this.selected = -1; - return false; - } + CurveEditor.prototype.onMouseUp = function( localpos, graphcanvas ) { + this.selected = -1; + return false; + } - CurveEditor.prototype.getCloserPoint = function(pos, max_dist) - { - var points = this.points; - if(!points) - return -1; - max_dist = max_dist || 30; - var w = (this.size[0] - this.margin * 2); - var h = (this.size[1] - this.margin * 2); - var num = points.length; - var p2 = [0,0]; - var min_dist = 1000000; - var closest = -1; - var last_valid = -1; - for(var i = 0; i < num; ++i) - { - var p = points[i]; - p2[0] = p[0] * w; - p2[1] = (1.0 - p[1]) * h; - if(p2[0] < pos[0]) - last_valid = i; - var dist = vec2.distance(pos,p2); - if(dist > min_dist || dist > max_dist) - continue; - closest = i; - min_dist = dist; - } - return closest; - } + CurveEditor.prototype.getCloserPoint = function(pos, max_dist) { + var points = this.points; + if(!points) + return -1; + max_dist = max_dist || 30; + var w = (this.size[0] - this.margin * 2); + var h = (this.size[1] - this.margin * 2); + var num = points.length; + var p2 = [0,0]; + var min_dist = 1000000; + var closest = -1; + var last_valid = -1; + for(var i = 0; i < num; ++i) { + var p = points[i]; + p2[0] = p[0] * w; + p2[1] = (1.0 - p[1]) * h; + if(p2[0] < pos[0]) + last_valid = i; + var dist = vec2.distance(pos,p2); + if(dist > min_dist || dist > max_dist) + continue; + closest = i; + min_dist = dist; + } + return closest; + } - LiteGraph.CurveEditor = CurveEditor; + LiteGraph.CurveEditor = CurveEditor; - //used to create nodes from wrapping functions + // used to create nodes from wrapping functions LiteGraph.getParameterNames = function(func) { return (func + "") .replace(/[/][/].*$/gm, "") // strip single-line comments @@ -14306,101 +14167,105 @@ LGraphNode.prototype.executeAction = function(action) .filter(Boolean); // split & filter [""] }; - /* helper for interaction: pointer, touch, mouse Listeners + /* helper for interaction: pointer, touch, mouse Listeners used by LGraphCanvas DragAndScale ContextMenu*/ - LiteGraph.pointerListenerAdd = function(oDOM, sEvIn, fCall, capture=false) { - if (!oDOM || !oDOM.addEventListener || !sEvIn || typeof fCall!=="function"){ - //console.log("cant pointerListenerAdd "+oDOM+", "+sEvent+", "+fCall); - return; // -- break -- - } - - var sMethod = LiteGraph.pointerevents_method; - var sEvent = sEvIn; - - // UNDER CONSTRUCTION - // convert pointerevents to touch event when not available - if (sMethod=="pointer" && !window.PointerEvent){ - console.warn("sMethod=='pointer' && !window.PointerEvent"); - console.log("Converting pointer["+sEvent+"] : down move up cancel enter TO touchstart touchmove touchend, etc .."); - switch(sEvent){ - case "down":{ - sMethod = "touch"; - sEvent = "start"; - break; - } - case "move":{ - sMethod = "touch"; - //sEvent = "move"; - break; - } - case "up":{ - sMethod = "touch"; - sEvent = "end"; - break; - } - case "cancel":{ - sMethod = "touch"; - //sEvent = "cancel"; - break; - } - case "enter":{ - console.log("debug: Should I send a move event?"); // ??? - break; - } - // case "over": case "out": not used at now - default:{ - console.warn("PointerEvent not available in this browser ? The event "+sEvent+" would not be called"); - } - } - } + LiteGraph.pointerListenerAdd = function(oDOM, sEvIn, fCall, capture=false) { + if (!oDOM || !oDOM.addEventListener || !sEvIn || typeof fCall!=="function") { + // console.log("cant pointerListenerAdd "+oDOM+", "+sEvent+", "+fCall); + return; // -- break -- + } + + var sMethod = LiteGraph.pointerevents_method; + var sEvent = sEvIn; + + // UNDER CONSTRUCTION + // convert pointerevents to touch event when not available + if (sMethod=="pointer" && !window.PointerEvent) { + console.warn("sMethod=='pointer' && !window.PointerEvent"); + console.log("Converting pointer["+sEvent+"] : down move up cancel enter TO touchstart touchmove touchend, etc .."); + switch(sEvent) { + case "down":{ + sMethod = "touch"; + sEvent = "start"; + break; + } + case "move":{ + sMethod = "touch"; + // sEvent = "move"; + break; + } + case "up":{ + sMethod = "touch"; + sEvent = "end"; + break; + } + case "cancel":{ + sMethod = "touch"; + // sEvent = "cancel"; + break; + } + case "enter":{ + console.log("debug: Should I send a move event?"); // ??? + break; + } + // case "over": case "out": not used at now + default:{ + console.warn("PointerEvent not available in this browser ? The event "+sEvent+" would not be called"); + } + } + } - switch(sEvent){ - //both pointer and move events - case "down": case "up": case "move": case "over": case "out": case "enter": - { - oDOM.addEventListener(sMethod+sEvent, fCall, capture); - } - // only pointerevents - case "leave": case "cancel": case "gotpointercapture": case "lostpointercapture": - { - if (sMethod!="mouse"){ - return oDOM.addEventListener(sMethod+sEvent, fCall, capture); - } - } - // not "pointer" || "mouse" - default: - return oDOM.addEventListener(sEvent, fCall, capture); - } - } - LiteGraph.pointerListenerRemove = function(oDOM, sEvent, fCall, capture=false) { - if (!oDOM || !oDOM.removeEventListener || !sEvent || typeof fCall!=="function"){ - //console.log("cant pointerListenerRemove "+oDOM+", "+sEvent+", "+fCall); - return; // -- break -- - } - switch(sEvent){ - //both pointer and move events - case "down": case "up": case "move": case "over": case "out": case "enter": - { - if (LiteGraph.pointerevents_method=="pointer" || LiteGraph.pointerevents_method=="mouse"){ - oDOM.removeEventListener(LiteGraph.pointerevents_method+sEvent, fCall, capture); - } - } - // only pointerevents - case "leave": case "cancel": case "gotpointercapture": case "lostpointercapture": - { - if (LiteGraph.pointerevents_method=="pointer"){ - return oDOM.removeEventListener(LiteGraph.pointerevents_method+sEvent, fCall, capture); - } - } - // not "pointer" || "mouse" - default: - return oDOM.removeEventListener(sEvent, fCall, capture); - } - } + switch(sEvent) { + // both pointer and move events + case "down": case "up": case "move": case "over": case "out": case "enter": + { + oDOM.addEventListener(sMethod+sEvent, fCall, capture); + } + break; + // only pointerevents + case "leave": case "cancel": case "gotpointercapture": case "lostpointercapture": + { + if (sMethod!="mouse") { + return oDOM.addEventListener(sMethod+sEvent, fCall, capture); + } + } + break; + // not "pointer" || "mouse" + default: + return oDOM.addEventListener(sEvent, fCall, capture); + } + } + LiteGraph.pointerListenerRemove = function(oDOM, sEvent, fCall, capture=false) { + if (!oDOM || !oDOM.removeEventListener || !sEvent || typeof fCall!=="function") { + // console.log("cant pointerListenerRemove "+oDOM+", "+sEvent+", "+fCall); + return; // -- break -- + } + switch(sEvent) { + // both pointer and move events + case "down": case "up": case "move": case "over": case "out": case "enter": + { + if (LiteGraph.pointerevents_method=="pointer" || LiteGraph.pointerevents_method=="mouse") { + oDOM.removeEventListener(LiteGraph.pointerevents_method+sEvent, fCall, capture); + } + } + break; + // only pointerevents + case "leave": case "cancel": case "gotpointercapture": case "lostpointercapture": + { + if (LiteGraph.pointerevents_method=="pointer") { + return oDOM.removeEventListener(LiteGraph.pointerevents_method+sEvent, fCall, capture); + } + } + break; + // not "pointer" || "mouse" + default: + return oDOM.removeEventListener(sEvent, fCall, capture); + } + } function clamp(v, a, b) { return a > v ? a : b < v ? b : v; - }; + } global.clamp = clamp; if (typeof window != "undefined" && !window["requestAnimationFrame"]) { @@ -14425,20149 +14290,19646 @@ if (typeof exports != "undefined") { } -//basic nodes -(function(global) { - var LiteGraph = global.LiteGraph; - - //Constant - function Time() { - this.addOutput("in ms", "number"); - this.addOutput("in sec", "number"); - } - - Time.title = "Time"; - Time.desc = "Time"; - - Time.prototype.onExecute = function() { - this.setOutputData(0, this.graph.globaltime * 1000); - this.setOutputData(1, this.graph.globaltime); - }; - - LiteGraph.registerNodeType("basic/time", Time); - - //Subgraph: a node that contains a graph - function Subgraph() { - var that = this; - this.size = [140, 80]; - this.properties = { enabled: true }; - this.enabled = true; - - //create inner graph - this.subgraph = new LiteGraph.LGraph(); - this.subgraph._subgraph_node = this; - this.subgraph._is_subgraph = true; - - this.subgraph.onTrigger = this.onSubgraphTrigger.bind(this); - - //nodes input node added inside - this.subgraph.onInputAdded = this.onSubgraphNewInput.bind(this); - this.subgraph.onInputRenamed = this.onSubgraphRenamedInput.bind(this); - this.subgraph.onInputTypeChanged = this.onSubgraphTypeChangeInput.bind(this); - this.subgraph.onInputRemoved = this.onSubgraphRemovedInput.bind(this); - - this.subgraph.onOutputAdded = this.onSubgraphNewOutput.bind(this); - this.subgraph.onOutputRenamed = this.onSubgraphRenamedOutput.bind(this); - this.subgraph.onOutputTypeChanged = this.onSubgraphTypeChangeOutput.bind(this); - this.subgraph.onOutputRemoved = this.onSubgraphRemovedOutput.bind(this); - } - - Subgraph.title = "Subgraph"; - Subgraph.desc = "Graph inside a node"; - Subgraph.title_color = "#334"; - - Subgraph.prototype.onGetInputs = function() { - return [["enabled", "boolean"]]; - }; - - /* - Subgraph.prototype.onDrawTitle = function(ctx) { - if (this.flags.collapsed) { - return; - } - - ctx.fillStyle = "#555"; - var w = LiteGraph.NODE_TITLE_HEIGHT; - var x = this.size[0] - w; - ctx.fillRect(x, -w, w, w); - ctx.fillStyle = "#333"; - ctx.beginPath(); - ctx.moveTo(x + w * 0.2, -w * 0.6); - ctx.lineTo(x + w * 0.8, -w * 0.6); - ctx.lineTo(x + w * 0.5, -w * 0.3); - ctx.fill(); - }; - */ - - Subgraph.prototype.onDblClick = function(e, pos, graphcanvas) { - var that = this; - setTimeout(function() { - graphcanvas.openSubgraph(that.subgraph); - }, 10); - }; - - /* - Subgraph.prototype.onMouseDown = function(e, pos, graphcanvas) { - if ( - !this.flags.collapsed && - pos[0] > this.size[0] - LiteGraph.NODE_TITLE_HEIGHT && - pos[1] < 0 - ) { - var that = this; - setTimeout(function() { - graphcanvas.openSubgraph(that.subgraph); - }, 10); - } - }; - */ - - Subgraph.prototype.onAction = function(action, param) { - this.subgraph.onAction(action, param); - }; - - Subgraph.prototype.onExecute = function() { - this.enabled = this.getInputOrProperty("enabled"); - if (!this.enabled) { - return; - } - - //send inputs to subgraph global inputs - if (this.inputs) { - for (var i = 0; i < this.inputs.length; i++) { - var input = this.inputs[i]; - var value = this.getInputData(i); - this.subgraph.setInputData(input.name, value); - } - } - - //execute - this.subgraph.runStep(); - - //send subgraph global outputs to outputs - if (this.outputs) { - for (var i = 0; i < this.outputs.length; i++) { - var output = this.outputs[i]; - var value = this.subgraph.getOutputData(output.name); - this.setOutputData(i, value); - } - } - }; - - Subgraph.prototype.sendEventToAllNodes = function(eventname, param, mode) { - if (this.enabled) { - this.subgraph.sendEventToAllNodes(eventname, param, mode); - } - }; - - Subgraph.prototype.onDrawBackground = function (ctx, graphcanvas, canvas, pos) { - if (this.flags.collapsed) - return; - var y = this.size[1] - LiteGraph.NODE_TITLE_HEIGHT + 0.5; - // button - var over = LiteGraph.isInsideRectangle(pos[0], pos[1], this.pos[0], this.pos[1] + y, this.size[0], LiteGraph.NODE_TITLE_HEIGHT); - let overleft = LiteGraph.isInsideRectangle(pos[0], pos[1], this.pos[0], this.pos[1] + y, this.size[0] / 2, LiteGraph.NODE_TITLE_HEIGHT) - ctx.fillStyle = over ? "#555" : "#222"; - ctx.beginPath(); - if (this._shape == LiteGraph.BOX_SHAPE) { - if (overleft) { - ctx.rect(0, y, this.size[0] / 2 + 1, LiteGraph.NODE_TITLE_HEIGHT); - } else { - ctx.rect(this.size[0] / 2, y, this.size[0] / 2 + 1, LiteGraph.NODE_TITLE_HEIGHT); - } - } - else { - if (overleft) { - ctx.roundRect(0, y, this.size[0] / 2 + 1, LiteGraph.NODE_TITLE_HEIGHT, [0,0, 8,8]); - } else { - ctx.roundRect(this.size[0] / 2, y, this.size[0] / 2 + 1, LiteGraph.NODE_TITLE_HEIGHT, [0,0, 8,8]); - } - } - if (over) { - ctx.fill(); - } else { - ctx.fillRect(0, y, this.size[0] + 1, LiteGraph.NODE_TITLE_HEIGHT); - } - // button - ctx.textAlign = "center"; - ctx.font = "24px Arial"; - ctx.fillStyle = over ? "#DDD" : "#999"; - ctx.fillText("+", this.size[0] * 0.25, y + 24); - ctx.fillText("+", this.size[0] * 0.75, y + 24); - } - - // Subgraph.prototype.onMouseDown = function(e, localpos, graphcanvas) - // { - // var y = this.size[1] - LiteGraph.NODE_TITLE_HEIGHT + 0.5; - // if(localpos[1] > y) - // { - // graphcanvas.showSubgraphPropertiesDialog(this); - // } - // } - Subgraph.prototype.onMouseDown = function (e, localpos, graphcanvas) { - var y = this.size[1] - LiteGraph.NODE_TITLE_HEIGHT + 0.5; - console.log(0) - if (localpos[1] > y) { - if (localpos[0] < this.size[0] / 2) { - console.log(1) - graphcanvas.showSubgraphPropertiesDialog(this); - } else { - console.log(2) - graphcanvas.showSubgraphPropertiesDialogRight(this); - } - } - } - Subgraph.prototype.computeSize = function() - { - var num_inputs = this.inputs ? this.inputs.length : 0; - var num_outputs = this.outputs ? this.outputs.length : 0; - return [ 200, Math.max(num_inputs,num_outputs) * LiteGraph.NODE_SLOT_HEIGHT + LiteGraph.NODE_TITLE_HEIGHT ]; - } - - //**** INPUTS *********************************** - Subgraph.prototype.onSubgraphTrigger = function(event, param) { - var slot = this.findOutputSlot(event); - if (slot != -1) { - this.triggerSlot(slot); - } - }; - - Subgraph.prototype.onSubgraphNewInput = function(name, type) { - var slot = this.findInputSlot(name); - if (slot == -1) { - //add input to the node - this.addInput(name, type); - } - }; - - Subgraph.prototype.onSubgraphRenamedInput = function(oldname, name) { - var slot = this.findInputSlot(oldname); - if (slot == -1) { - return; - } - var info = this.getInputInfo(slot); - info.name = name; - }; - - Subgraph.prototype.onSubgraphTypeChangeInput = function(name, type) { - var slot = this.findInputSlot(name); - if (slot == -1) { - return; - } - var info = this.getInputInfo(slot); - info.type = type; - }; - - Subgraph.prototype.onSubgraphRemovedInput = function(name) { - var slot = this.findInputSlot(name); - if (slot == -1) { - return; - } - this.removeInput(slot); - }; - - //**** OUTPUTS *********************************** - Subgraph.prototype.onSubgraphNewOutput = function(name, type) { - var slot = this.findOutputSlot(name); - if (slot == -1) { - this.addOutput(name, type); - } - }; - - Subgraph.prototype.onSubgraphRenamedOutput = function(oldname, name) { - var slot = this.findOutputSlot(oldname); - if (slot == -1) { - return; - } - var info = this.getOutputInfo(slot); - info.name = name; - }; - - Subgraph.prototype.onSubgraphTypeChangeOutput = function(name, type) { - var slot = this.findOutputSlot(name); - if (slot == -1) { - return; - } - var info = this.getOutputInfo(slot); - info.type = type; - }; - - Subgraph.prototype.onSubgraphRemovedOutput = function(name) { - var slot = this.findOutputSlot(name); - if (slot == -1) { - return; - } - this.removeOutput(slot); - }; - // ***************************************************** - - Subgraph.prototype.getExtraMenuOptions = function(graphcanvas) { - var that = this; - return [ - { - content: "Open", - callback: function() { - graphcanvas.openSubgraph(that.subgraph); - } - } - ]; - }; - - Subgraph.prototype.onResize = function(size) { - size[1] += 20; - }; - - Subgraph.prototype.serialize = function() { - var data = LiteGraph.LGraphNode.prototype.serialize.call(this); - data.subgraph = this.subgraph.serialize(); - return data; - }; - //no need to define node.configure, the default method detects node.subgraph and passes the object to node.subgraph.configure() - - Subgraph.prototype.reassignSubgraphUUIDs = function(graph) { - const idMap = { nodeIDs: {}, linkIDs: {} } - - for (const node of graph.nodes) { - const oldID = node.id - const newID = LiteGraph.uuidv4() - node.id = newID - - if (idMap.nodeIDs[oldID] || idMap.nodeIDs[newID]) { - throw new Error(`New/old node UUID wasn't unique in changed map! ${oldID} ${newID}`) - } - - idMap.nodeIDs[oldID] = newID - idMap.nodeIDs[newID] = oldID - } - - for (const link of graph.links) { - const oldID = link[0] - const newID = LiteGraph.uuidv4(); - link[0] = newID - - if (idMap.linkIDs[oldID] || idMap.linkIDs[newID]) { - throw new Error(`New/old link UUID wasn't unique in changed map! ${oldID} ${newID}`) - } - - idMap.linkIDs[oldID] = newID - idMap.linkIDs[newID] = oldID - - const nodeFrom = link[1] - const nodeTo = link[3] - - if (!idMap.nodeIDs[nodeFrom]) { - throw new Error(`Old node UUID not found in mapping! ${nodeFrom}`) - } - - link[1] = idMap.nodeIDs[nodeFrom] - - if (!idMap.nodeIDs[nodeTo]) { - throw new Error(`Old node UUID not found in mapping! ${nodeTo}`) - } - - link[3] = idMap.nodeIDs[nodeTo] - } - - // Reconnect links - for (const node of graph.nodes) { - if (node.inputs) { - for (const input of node.inputs) { - if (input.link) { - input.link = idMap.linkIDs[input.link] - } - } - } - if (node.outputs) { - for (const output of node.outputs) { - if (output.links) { - output.links = output.links.map(l => idMap.linkIDs[l]); - } - } - } - } - - // Recurse! - for (const node of graph.nodes) { - if (node.type === "graph/subgraph") { - const merge = reassignGraphUUIDs(node.subgraph); - idMap.nodeIDs.assign(merge.nodeIDs) - idMap.linkIDs.assign(merge.linkIDs) - } - } - }; - - Subgraph.prototype.clone = function() { - var node = LiteGraph.createNode(this.type); - var data = this.serialize(); - - if (LiteGraph.use_uuids) { - // LGraph.serialize() seems to reuse objects in the original graph. But we - // need to change node IDs here, so clone it first. - const subgraph = LiteGraph.cloneObject(data.subgraph) - - this.reassignSubgraphUUIDs(subgraph); - - data.subgraph = subgraph; - } - - delete data["id"]; - delete data["inputs"]; - delete data["outputs"]; - node.configure(data); - return node; - }; - - Subgraph.prototype.buildFromNodes = function(nodes) - { - //clear all? - //TODO - - //nodes that connect data between parent graph and subgraph - var subgraph_inputs = []; - var subgraph_outputs = []; - - //mark inner nodes - var ids = {}; - var min_x = 0; - var max_x = 0; - for(var i = 0; i < nodes.length; ++i) - { - var node = nodes[i]; - ids[ node.id ] = node; - min_x = Math.min( node.pos[0], min_x ); - max_x = Math.max( node.pos[0], min_x ); - } - - var last_input_y = 0; - var last_output_y = 0; - - for(var i = 0; i < nodes.length; ++i) - { - var node = nodes[i]; - //check inputs - if( node.inputs ) - for(var j = 0; j < node.inputs.length; ++j) - { - var input = node.inputs[j]; - if( !input || !input.link ) - continue; - var link = node.graph.links[ input.link ]; - if(!link) - continue; - if( ids[ link.origin_id ] ) - continue; - //this.addInput(input.name,link.type); - this.subgraph.addInput(input.name,link.type); - /* - var input_node = LiteGraph.createNode("graph/input"); - this.subgraph.add( input_node ); - input_node.pos = [min_x - 200, last_input_y ]; - last_input_y += 100; - */ - } - - //check outputs - if( node.outputs ) - for(var j = 0; j < node.outputs.length; ++j) - { - var output = node.outputs[j]; - if( !output || !output.links || !output.links.length ) - continue; - var is_external = false; - for(var k = 0; k < output.links.length; ++k) - { - var link = node.graph.links[ output.links[k] ]; - if(!link) - continue; - if( ids[ link.target_id ] ) - continue; - is_external = true; - break; - } - if(!is_external) - continue; - //this.addOutput(output.name,output.type); - /* - var output_node = LiteGraph.createNode("graph/output"); - this.subgraph.add( output_node ); - output_node.pos = [max_x + 50, last_output_y ]; - last_output_y += 100; - */ - } - } - - //detect inputs and outputs - //split every connection in two data_connection nodes - //keep track of internal connections - //connect external connections - - //clone nodes inside subgraph and try to reconnect them - - //connect edge subgraph nodes to extarnal connections nodes - } - - LiteGraph.Subgraph = Subgraph; - LiteGraph.registerNodeType("graph/subgraph", Subgraph); - - //Input for a subgraph - function GraphInput() { - this.addOutput("", "number"); - - this.name_in_graph = ""; - this.properties = { - name: "", - type: "number", - value: 0 - }; - - var that = this; - - this.name_widget = this.addWidget( - "text", - "Name", - this.properties.name, - function(v) { - if (!v) { - return; - } - that.setProperty("name",v); - } - ); - this.type_widget = this.addWidget( - "text", - "Type", - this.properties.type, - function(v) { - that.setProperty("type",v); - } - ); - - this.value_widget = this.addWidget( - "number", - "Value", - this.properties.value, - function(v) { - that.setProperty("value",v); - } - ); - - this.widgets_up = true; - this.size = [180, 90]; - } - - GraphInput.title = "Input"; - GraphInput.desc = "Input of the graph"; - - GraphInput.prototype.onConfigure = function() - - { - this.updateType(); - } - - //ensures the type in the node output and the type in the associated graph input are the same - GraphInput.prototype.updateType = function() - { - var type = this.properties.type; - this.type_widget.value = type; - - //update output - if(this.outputs[0].type != type) - { - if (!LiteGraph.isValidConnection(this.outputs[0].type,type)) - this.disconnectOutput(0); - this.outputs[0].type = type; - } - - //update widget - if(type == "number") - { - this.value_widget.type = "number"; - this.value_widget.value = 0; - } - else if(type == "boolean") - { - this.value_widget.type = "toggle"; - this.value_widget.value = true; - } - else if(type == "string") - { - this.value_widget.type = "text"; - this.value_widget.value = ""; - } - else - { - this.value_widget.type = null; - this.value_widget.value = null; - } - this.properties.value = this.value_widget.value; - - //update graph - if (this.graph && this.name_in_graph) { - this.graph.changeInputType(this.name_in_graph, type); - } - } - - //this is executed AFTER the property has changed - GraphInput.prototype.onPropertyChanged = function(name,v) - { - if( name == "name" ) - { - if (v == "" || v == this.name_in_graph || v == "enabled") { - return false; - } - if(this.graph) - { - if (this.name_in_graph) { - //already added - this.graph.renameInput( this.name_in_graph, v ); - } else { - this.graph.addInput( v, this.properties.type ); - } - } //what if not?! - this.name_widget.value = v; - this.name_in_graph = v; - } - else if( name == "type" ) - { - this.updateType(); - } - else if( name == "value" ) - { - } - } - - GraphInput.prototype.getTitle = function() { - if (this.flags.collapsed) { - return this.properties.name; - } - return this.title; - }; - - GraphInput.prototype.onAction = function(action, param) { - if (this.properties.type == LiteGraph.EVENT) { - this.triggerSlot(0, param); - } - }; - - GraphInput.prototype.onExecute = function() { - var name = this.properties.name; - //read from global input - var data = this.graph.inputs[name]; - if (!data) { - this.setOutputData(0, this.properties.value ); - return; - } - - this.setOutputData(0, data.value !== undefined ? data.value : this.properties.value ); - }; - - GraphInput.prototype.onRemoved = function() { - if (this.name_in_graph) { - this.graph.removeInput(this.name_in_graph); - } - }; - - LiteGraph.GraphInput = GraphInput; - LiteGraph.registerNodeType("graph/input", GraphInput); - - //Output for a subgraph - function GraphOutput() { - this.addInput("", ""); - - this.name_in_graph = ""; - this.properties = { name: "", type: "" }; - var that = this; - - // Object.defineProperty(this.properties, "name", { - // get: function() { - // return that.name_in_graph; - // }, - // set: function(v) { - // if (v == "" || v == that.name_in_graph) { - // return; - // } - // if (that.name_in_graph) { - // //already added - // that.graph.renameOutput(that.name_in_graph, v); - // } else { - // that.graph.addOutput(v, that.properties.type); - // } - // that.name_widget.value = v; - // that.name_in_graph = v; - // }, - // enumerable: true - // }); - - // Object.defineProperty(this.properties, "type", { - // get: function() { - // return that.inputs[0].type; - // }, - // set: function(v) { - // if (v == "action" || v == "event") { - // v = LiteGraph.ACTION; - // } - // if (!LiteGraph.isValidConnection(that.inputs[0].type,v)) - // that.disconnectInput(0); - // that.inputs[0].type = v; - // if (that.name_in_graph) { - // //already added - // that.graph.changeOutputType( - // that.name_in_graph, - // that.inputs[0].type - // ); - // } - // that.type_widget.value = v || ""; - // }, - // enumerable: true - // }); - - this.name_widget = this.addWidget("text","Name",this.properties.name,"name"); - this.type_widget = this.addWidget("text","Type",this.properties.type,"type"); - this.widgets_up = true; - this.size = [180, 60]; - } - - GraphOutput.title = "Output"; - GraphOutput.desc = "Output of the graph"; - - GraphOutput.prototype.onPropertyChanged = function (name, v) { - if (name == "name") { - if (v == "" || v == this.name_in_graph || v == "enabled") { - return false; - } - if (this.graph) { - if (this.name_in_graph) { - //already added - this.graph.renameOutput(this.name_in_graph, v); - } else { - this.graph.addOutput(v, this.properties.type); - } - } //what if not?! - this.name_widget.value = v; - this.name_in_graph = v; - } - else if (name == "type") { - this.updateType(); - } - else if (name == "value") { - } - } - - GraphOutput.prototype.updateType = function () { - var type = this.properties.type; - if (this.type_widget) - this.type_widget.value = type; - - //update output - if (this.inputs[0].type != type) { - - if ( type == "action" || type == "event") - type = LiteGraph.EVENT; - if (!LiteGraph.isValidConnection(this.inputs[0].type, type)) - this.disconnectInput(0); - this.inputs[0].type = type; - } - - //update graph - if (this.graph && this.name_in_graph) { - this.graph.changeOutputType(this.name_in_graph, type); - } - } - - - - GraphOutput.prototype.onExecute = function() { - this._value = this.getInputData(0); - this.graph.setOutputData(this.properties.name, this._value); - }; - - GraphOutput.prototype.onAction = function(action, param) { - if (this.properties.type == LiteGraph.ACTION) { - this.graph.trigger( this.properties.name, param ); - } - }; - - GraphOutput.prototype.onRemoved = function() { - if (this.name_in_graph) { - this.graph.removeOutput(this.name_in_graph); - } - }; - - GraphOutput.prototype.getTitle = function() { - if (this.flags.collapsed) { - return this.properties.name; - } - return this.title; - }; - - LiteGraph.GraphOutput = GraphOutput; - LiteGraph.registerNodeType("graph/output", GraphOutput); - - //Constant - function ConstantNumber() { - this.addOutput("value", "number"); - this.addProperty("value", 1.0); - this.widget = this.addWidget("number","value",1,"value"); - this.widgets_up = true; - this.size = [180, 30]; - } - - ConstantNumber.title = "Const Number"; - ConstantNumber.desc = "Constant number"; - - ConstantNumber.prototype.onExecute = function() { - this.setOutputData(0, parseFloat(this.properties["value"])); - }; - - ConstantNumber.prototype.getTitle = function() { - if (this.flags.collapsed) { - return this.properties.value; - } - return this.title; - }; - - ConstantNumber.prototype.setValue = function(v) - { - this.setProperty("value",v); - } - - ConstantNumber.prototype.onDrawBackground = function(ctx) { - //show the current value - this.outputs[0].label = this.properties["value"].toFixed(3); - }; - - LiteGraph.registerNodeType("basic/const", ConstantNumber); - - function ConstantBoolean() { - this.addOutput("bool", "boolean"); - this.addProperty("value", true); - this.widget = this.addWidget("toggle","value",true,"value"); - this.serialize_widgets = true; - this.widgets_up = true; - this.size = [140, 30]; - } - - ConstantBoolean.title = "Const Boolean"; - ConstantBoolean.desc = "Constant boolean"; - ConstantBoolean.prototype.getTitle = ConstantNumber.prototype.getTitle; - - ConstantBoolean.prototype.onExecute = function() { - this.setOutputData(0, this.properties["value"]); - }; - - ConstantBoolean.prototype.setValue = ConstantNumber.prototype.setValue; - - ConstantBoolean.prototype.onGetInputs = function() { - return [["toggle", LiteGraph.ACTION]]; - }; - - ConstantBoolean.prototype.onAction = function(action) - { - this.setValue( !this.properties.value ); - } - - LiteGraph.registerNodeType("basic/boolean", ConstantBoolean); - - function ConstantString() { - this.addOutput("string", "string"); - this.addProperty("value", ""); - this.widget = this.addWidget("text","value","","value"); //link to property value - this.widgets_up = true; - this.size = [180, 30]; - } - - ConstantString.title = "Const String"; - ConstantString.desc = "Constant string"; - - ConstantString.prototype.getTitle = ConstantNumber.prototype.getTitle; - - ConstantString.prototype.onExecute = function() { - this.setOutputData(0, this.properties["value"]); - }; - - ConstantString.prototype.setValue = ConstantNumber.prototype.setValue; - - ConstantString.prototype.onDropFile = function(file) - { - var that = this; - var reader = new FileReader(); - reader.onload = function(e) - { - that.setProperty("value",e.target.result); - } - reader.readAsText(file); - } - - LiteGraph.registerNodeType("basic/string", ConstantString); - - function ConstantObject() { - this.addOutput("obj", "object"); - this.size = [120, 30]; - this._object = {}; - } - - ConstantObject.title = "Const Object"; - ConstantObject.desc = "Constant Object"; - - ConstantObject.prototype.onExecute = function() { - this.setOutputData(0, this._object); - }; - - LiteGraph.registerNodeType( "basic/object", ConstantObject ); - - function ConstantFile() { - this.addInput("url", "string"); - this.addOutput("file", "string"); - this.addProperty("url", ""); - this.addProperty("type", "text"); - this.widget = this.addWidget("text","url","","url"); - this._data = null; - } - - ConstantFile.title = "Const File"; - ConstantFile.desc = "Fetches a file from an url"; - ConstantFile["@type"] = { type: "enum", values: ["text","arraybuffer","blob","json"] }; - - ConstantFile.prototype.onPropertyChanged = function(name, value) { - if (name == "url") - { - if( value == null || value == "") - this._data = null; - else - { - this.fetchFile(value); - } - } - } - - ConstantFile.prototype.onExecute = function() { - var url = this.getInputData(0) || this.properties.url; - if(url && (url != this._url || this._type != this.properties.type)) - this.fetchFile(url); - this.setOutputData(0, this._data ); - }; - - ConstantFile.prototype.setValue = ConstantNumber.prototype.setValue; - - ConstantFile.prototype.fetchFile = function(url) { - var that = this; - if(!url || url.constructor !== String) - { - that._data = null; - that.boxcolor = null; - return; - } - - this._url = url; - this._type = this.properties.type; - if (url.substr(0, 4) == "http" && LiteGraph.proxy) { - url = LiteGraph.proxy + url.substr(url.indexOf(":") + 3); - } - fetch(url) - .then(function(response) { - if(!response.ok) - throw new Error("File not found"); - - if(that.properties.type == "arraybuffer") - return response.arrayBuffer(); - else if(that.properties.type == "text") - return response.text(); - else if(that.properties.type == "json") - return response.json(); - else if(that.properties.type == "blob") - return response.blob(); - }) - .then(function(data) { - that._data = data; - that.boxcolor = "#AEA"; - }) - .catch(function(error) { - that._data = null; - that.boxcolor = "red"; - console.error("error fetching file:",url); - }); - }; - - ConstantFile.prototype.onDropFile = function(file) - { - var that = this; - this._url = file.name; - this._type = this.properties.type; - this.properties.url = file.name; - var reader = new FileReader(); - reader.onload = function(e) - { - that.boxcolor = "#AEA"; - var v = e.target.result; - if( that.properties.type == "json" ) - v = JSON.parse(v); - that._data = v; - } - if(that.properties.type == "arraybuffer") - reader.readAsArrayBuffer(file); - else if(that.properties.type == "text" || that.properties.type == "json") - reader.readAsText(file); - else if(that.properties.type == "blob") - return reader.readAsBinaryString(file); - } - - LiteGraph.registerNodeType("basic/file", ConstantFile); - - -//to store json objects -function JSONParse() { - this.addInput("parse", LiteGraph.ACTION); - this.addInput("json", "string"); - this.addOutput("done", LiteGraph.EVENT); - this.addOutput("object", "object"); - this.widget = this.addWidget("button","parse","",this.parse.bind(this)); - this._str = null; - this._obj = null; -} - -JSONParse.title = "JSON Parse"; -JSONParse.desc = "Parses JSON String into object"; - -JSONParse.prototype.parse = function() -{ - if(!this._str) - return; - - try { - this._str = this.getInputData(1); - this._obj = JSON.parse(this._str); - this.boxcolor = "#AEA"; - this.triggerSlot(0); - } catch (err) { - this.boxcolor = "red"; - } -} - -JSONParse.prototype.onExecute = function() { - this._str = this.getInputData(1); - this.setOutputData(1, this._obj); -}; - -JSONParse.prototype.onAction = function(name) { - if(name == "parse") - this.parse(); -} - -LiteGraph.registerNodeType("basic/jsonparse", JSONParse); - - //to store json objects - function ConstantData() { - this.addOutput("data", "object"); - this.addProperty("value", ""); - this.widget = this.addWidget("text","json","","value"); - this.widgets_up = true; - this.size = [140, 30]; - this._value = null; - } - - ConstantData.title = "Const Data"; - ConstantData.desc = "Constant Data"; - - ConstantData.prototype.onPropertyChanged = function(name, value) { - this.widget.value = value; - if (value == null || value == "") { - return; - } - - try { - this._value = JSON.parse(value); - this.boxcolor = "#AEA"; - } catch (err) { - this.boxcolor = "red"; - } - }; - - ConstantData.prototype.onExecute = function() { - this.setOutputData(0, this._value); - }; - - ConstantData.prototype.setValue = ConstantNumber.prototype.setValue; - - LiteGraph.registerNodeType("basic/data", ConstantData); - - //to store json objects - function ConstantArray() { - this._value = []; - this.addInput("json", ""); - this.addOutput("arrayOut", "array"); - this.addOutput("length", "number"); - this.addProperty("value", "[]"); - this.widget = this.addWidget("text","array",this.properties.value,"value"); - this.widgets_up = true; - this.size = [140, 50]; - } - - ConstantArray.title = "Const Array"; - ConstantArray.desc = "Constant Array"; - - ConstantArray.prototype.onPropertyChanged = function(name, value) { - this.widget.value = value; - if (value == null || value == "") { - return; - } - - try { - if(value[0] != "[") - this._value = JSON.parse("[" + value + "]"); - else - this._value = JSON.parse(value); - this.boxcolor = "#AEA"; - } catch (err) { - this.boxcolor = "red"; - } - }; - - ConstantArray.prototype.onExecute = function() { - var v = this.getInputData(0); - if(v && v.length) //clone - { - if(!this._value) - this._value = new Array(); - this._value.length = v.length; - for(var i = 0; i < v.length; ++i) - this._value[i] = v[i]; - } - this.setOutputData(0, this._value); - this.setOutputData(1, this._value ? ( this._value.length || 0) : 0 ); - }; - - ConstantArray.prototype.setValue = ConstantNumber.prototype.setValue; - - LiteGraph.registerNodeType("basic/array", ConstantArray); - - function SetArray() - { - this.addInput("arr", "array"); - this.addInput("value", ""); - this.addOutput("arr", "array"); - this.properties = { index: 0 }; - this.widget = this.addWidget("number","i",this.properties.index,"index",{precision: 0, step: 10, min: 0}); - } - - SetArray.title = "Set Array"; - SetArray.desc = "Sets index of array"; - - SetArray.prototype.onExecute = function() { - var arr = this.getInputData(0); - if(!arr) - return; - var v = this.getInputData(1); - if(v === undefined ) - return; - if(this.properties.index) - arr[ Math.floor(this.properties.index) ] = v; - this.setOutputData(0,arr); - }; - - LiteGraph.registerNodeType("basic/set_array", SetArray ); - - function ArrayElement() { - this.addInput("array", "array,table,string"); - this.addInput("index", "number"); - this.addOutput("value", ""); - this.addProperty("index",0); - } - - ArrayElement.title = "Array[i]"; - ArrayElement.desc = "Returns an element from an array"; - - ArrayElement.prototype.onExecute = function() { - var array = this.getInputData(0); - var index = this.getInputData(1); - if(index == null) - index = this.properties.index; - if(array == null || index == null ) - return; - this.setOutputData(0, array[Math.floor(Number(index))] ); - }; - - LiteGraph.registerNodeType("basic/array[]", ArrayElement); - - function TableElement() { - this.addInput("table", "table"); - this.addInput("row", "number"); - this.addInput("col", "number"); - this.addOutput("value", ""); - this.addProperty("row",0); - this.addProperty("column",0); - } - - TableElement.title = "Table[row][col]"; - TableElement.desc = "Returns an element from a table"; - - TableElement.prototype.onExecute = function() { - var table = this.getInputData(0); - var row = this.getInputData(1); - var col = this.getInputData(2); - if(row == null) - row = this.properties.row; - if(col == null) - col = this.properties.column; - if(table == null || row == null || col == null) - return; - var row = table[Math.floor(Number(row))]; - if(row) - this.setOutputData(0, row[Math.floor(Number(col))] ); - else - this.setOutputData(0, null ); - }; - - LiteGraph.registerNodeType("basic/table[][]", TableElement); - - function ObjectProperty() { - this.addInput("obj", "object"); - this.addOutput("property", 0); - this.addProperty("value", 0); - this.widget = this.addWidget("text","prop.","",this.setValue.bind(this) ); - this.widgets_up = true; - this.size = [140, 30]; - this._value = null; - } - - ObjectProperty.title = "Object property"; - ObjectProperty.desc = "Outputs the property of an object"; - - ObjectProperty.prototype.setValue = function(v) { - this.properties.value = v; - this.widget.value = v; - }; - - ObjectProperty.prototype.getTitle = function() { - if (this.flags.collapsed) { - return "in." + this.properties.value; - } - return this.title; - }; - - ObjectProperty.prototype.onPropertyChanged = function(name, value) { - this.widget.value = value; - }; - - ObjectProperty.prototype.onExecute = function() { - var data = this.getInputData(0); - if (data != null) { - this.setOutputData(0, data[this.properties.value]); - } - }; - - LiteGraph.registerNodeType("basic/object_property", ObjectProperty); - - function ObjectKeys() { - this.addInput("obj", ""); - this.addOutput("keys", "array"); - this.size = [140, 30]; - } - - ObjectKeys.title = "Object keys"; - ObjectKeys.desc = "Outputs an array with the keys of an object"; - - ObjectKeys.prototype.onExecute = function() { - var data = this.getInputData(0); - if (data != null) { - this.setOutputData(0, Object.keys(data) ); - } - }; - - LiteGraph.registerNodeType("basic/object_keys", ObjectKeys); - - - function SetObject() - { - this.addInput("obj", ""); - this.addInput("value", ""); - this.addOutput("obj", ""); - this.properties = { property: "" }; - this.name_widget = this.addWidget("text","prop.",this.properties.property,"property"); - } - - SetObject.title = "Set Object"; - SetObject.desc = "Adds propertiesrty to object"; - - SetObject.prototype.onExecute = function() { - var obj = this.getInputData(0); - if(!obj) - return; - var v = this.getInputData(1); - if(v === undefined ) - return; - if(this.properties.property) - obj[ this.properties.property ] = v; - this.setOutputData(0,obj); - }; - - LiteGraph.registerNodeType("basic/set_object", SetObject ); - - - function MergeObjects() { - this.addInput("A", "object"); - this.addInput("B", "object"); - this.addOutput("out", "object"); - this._result = {}; - var that = this; - this.addWidget("button","clear","",function(){ - that._result = {}; - }); - this.size = this.computeSize(); - } - - MergeObjects.title = "Merge Objects"; - MergeObjects.desc = "Creates an object copying properties from others"; - - MergeObjects.prototype.onExecute = function() { - var A = this.getInputData(0); - var B = this.getInputData(1); - var C = this._result; - if(A) - for(var i in A) - C[i] = A[i]; - if(B) - for(var i in B) - C[i] = B[i]; - this.setOutputData(0,C); - }; - - LiteGraph.registerNodeType("basic/merge_objects", MergeObjects ); - - //Store as variable - function Variable() { - this.size = [60, 30]; - this.addInput("in"); - this.addOutput("out"); - this.properties = { varname: "myname", container: Variable.LITEGRAPH }; - this.value = null; - } - - Variable.title = "Variable"; - Variable.desc = "store/read variable value"; - - Variable.LITEGRAPH = 0; //between all graphs - Variable.GRAPH = 1; //only inside this graph - Variable.GLOBALSCOPE = 2; //attached to Window - - Variable["@container"] = { type: "enum", values: {"litegraph":Variable.LITEGRAPH, "graph":Variable.GRAPH,"global": Variable.GLOBALSCOPE} }; - - Variable.prototype.onExecute = function() { - var container = this.getContainer(); - - if(this.isInputConnected(0)) - { - this.value = this.getInputData(0); - container[ this.properties.varname ] = this.value; - this.setOutputData(0, this.value ); - return; - } - - this.setOutputData( 0, container[ this.properties.varname ] ); - }; - - Variable.prototype.getContainer = function() - { - switch(this.properties.container) - { - case Variable.GRAPH: - if(this.graph) - return this.graph.vars; - return {}; - break; - case Variable.GLOBALSCOPE: - return global; - break; - case Variable.LITEGRAPH: - default: - return LiteGraph.Globals; - break; - } - } - - Variable.prototype.getTitle = function() { - return this.properties.varname; - }; - - LiteGraph.registerNodeType("basic/variable", Variable); - - function length(v) { - if(v && v.length != null) - return Number(v.length); - return 0; - } - - LiteGraph.wrapFunctionAsNode( - "basic/length", - length, - [""], - "number" - ); - - function length(v) { - if(v && v.length != null) - return Number(v.length); - return 0; - } - - LiteGraph.wrapFunctionAsNode( - "basic/not", - function(a){ return !a; }, - [""], - "boolean" - ); - - function DownloadData() { - this.size = [60, 30]; - this.addInput("data", 0 ); - this.addInput("download", LiteGraph.ACTION ); - this.properties = { filename: "data.json" }; - this.value = null; - var that = this; - this.addWidget("button","Download","", function(v){ - if(!that.value) - return; - that.downloadAsFile(); - }); - } - - DownloadData.title = "Download"; - DownloadData.desc = "Download some data"; - - DownloadData.prototype.downloadAsFile = function() - { - if(this.value == null) - return; - - var str = null; - if(this.value.constructor === String) - str = this.value; - else - str = JSON.stringify(this.value); - - var file = new Blob([str]); - var url = URL.createObjectURL( file ); - var element = document.createElement("a"); - element.setAttribute('href', url); - element.setAttribute('download', this.properties.filename ); - element.style.display = 'none'; - document.body.appendChild(element); - element.click(); - document.body.removeChild(element); - setTimeout( function(){ URL.revokeObjectURL( url ); }, 1000*60 ); //wait one minute to revoke url - } - - DownloadData.prototype.onAction = function(action, param) { - var that = this; - setTimeout( function(){ that.downloadAsFile(); }, 100); //deferred to avoid blocking the renderer with the popup - } - - DownloadData.prototype.onExecute = function() { - if (this.inputs[0]) { - this.value = this.getInputData(0); - } - }; - - DownloadData.prototype.getTitle = function() { - if (this.flags.collapsed) { - return this.properties.filename; - } - return this.title; - }; - - LiteGraph.registerNodeType("basic/download", DownloadData); - - - - //Watch a value in the editor - function Watch() { - this.size = [60, 30]; - this.addInput("value", 0, { label: "" }); - this.value = 0; - } - - Watch.title = "Watch"; - Watch.desc = "Show value of input"; - - Watch.prototype.onExecute = function() { - if (this.inputs[0]) { - this.value = this.getInputData(0); - } - }; - - Watch.prototype.getTitle = function() { - if (this.flags.collapsed) { - return this.inputs[0].label; - } - return this.title; - }; - - Watch.toString = function(o) { - if (o == null) { - return "null"; - } else if (o.constructor === Number) { - return o.toFixed(3); - } else if (o.constructor === Array) { - var str = "["; - for (var i = 0; i < o.length; ++i) { - str += Watch.toString(o[i]) + (i + 1 != o.length ? "," : ""); - } - str += "]"; - return str; - } else { - return String(o); - } - }; - - Watch.prototype.onDrawBackground = function(ctx) { - //show the current value - this.inputs[0].label = Watch.toString(this.value); - }; - - LiteGraph.registerNodeType("basic/watch", Watch); - - //in case one type doesnt match other type but you want to connect them anyway - function Cast() { - this.addInput("in", 0); - this.addOutput("out", 0); - this.size = [40, 30]; - } - - Cast.title = "Cast"; - Cast.desc = "Allows to connect different types"; - - Cast.prototype.onExecute = function() { - this.setOutputData(0, this.getInputData(0)); - }; - - LiteGraph.registerNodeType("basic/cast", Cast); - - //Show value inside the debug console - function Console() { - this.mode = LiteGraph.ON_EVENT; - this.size = [80, 30]; - this.addProperty("msg", ""); - this.addInput("log", LiteGraph.EVENT); - this.addInput("msg", 0); - } - - Console.title = "Console"; - Console.desc = "Show value inside the console"; - - Console.prototype.onAction = function(action, param) { - // param is the action - var msg = this.getInputData(1); //getInputDataByName("msg"); - //if (msg == null || typeof msg == "undefined") return; - if (!msg) msg = this.properties.msg; - if (!msg) msg = "Event: "+param; // msg is undefined if the slot is lost? - if (action == "log") { - console.log(msg); - } else if (action == "warn") { - console.warn(msg); - } else if (action == "error") { - console.error(msg); - } - }; - - Console.prototype.onExecute = function() { - var msg = this.getInputData(1); //getInputDataByName("msg"); - if (!msg) msg = this.properties.msg; - if (msg != null && typeof msg != "undefined") { - this.properties.msg = msg; - console.log(msg); - } - }; - - Console.prototype.onGetInputs = function() { - return [ - ["log", LiteGraph.ACTION], - ["warn", LiteGraph.ACTION], - ["error", LiteGraph.ACTION] - ]; - }; - - LiteGraph.registerNodeType("basic/console", Console); - - //Show value inside the debug console - function Alert() { - this.mode = LiteGraph.ON_EVENT; - this.addProperty("msg", ""); - this.addInput("", LiteGraph.EVENT); - var that = this; - this.widget = this.addWidget("text", "Text", "", "msg"); - this.widgets_up = true; - this.size = [200, 30]; - } - - Alert.title = "Alert"; - Alert.desc = "Show an alert window"; - Alert.color = "#510"; - - Alert.prototype.onConfigure = function(o) { - this.widget.value = o.properties.msg; - }; - - Alert.prototype.onAction = function(action, param) { - var msg = this.properties.msg; - setTimeout(function() { - alert(msg); - }, 10); - }; - - LiteGraph.registerNodeType("basic/alert", Alert); - - //Execites simple code - function NodeScript() { - this.size = [60, 30]; - this.addProperty("onExecute", "return A;"); - this.addInput("A", 0); - this.addInput("B", 0); - this.addOutput("out", 0); - - this._func = null; - this.data = {}; - } - - NodeScript.prototype.onConfigure = function(o) { - if (o.properties.onExecute && LiteGraph.allow_scripts) - this.compileCode(o.properties.onExecute); - else - console.warn("Script not compiled, LiteGraph.allow_scripts is false"); - }; - - NodeScript.title = "Script"; - NodeScript.desc = "executes a code (max 256 characters)"; - - NodeScript.widgets_info = { - onExecute: { type: "code" } - }; - - NodeScript.prototype.onPropertyChanged = function(name, value) { - if (name == "onExecute" && LiteGraph.allow_scripts) - this.compileCode(value); - else - console.warn("Script not compiled, LiteGraph.allow_scripts is false"); - }; - - NodeScript.prototype.compileCode = function(code) { - this._func = null; - if (code.length > 256) { - console.warn("Script too long, max 256 chars"); - } else { - var code_low = code.toLowerCase(); - var forbidden_words = [ - "script", - "body", - "document", - "eval", - "nodescript", - "function" - ]; //bad security solution - for (var i = 0; i < forbidden_words.length; ++i) { - if (code_low.indexOf(forbidden_words[i]) != -1) { - console.warn("invalid script"); - return; - } - } - try { - this._func = new Function("A", "B", "C", "DATA", "node", code); - } catch (err) { - console.error("Error parsing script"); - console.error(err); - } - } - }; - - NodeScript.prototype.onExecute = function() { - if (!this._func) { - return; - } - - try { - var A = this.getInputData(0); - var B = this.getInputData(1); - var C = this.getInputData(2); - this.setOutputData(0, this._func(A, B, C, this.data, this)); - } catch (err) { - console.error("Error in script"); - console.error(err); - } - }; - - NodeScript.prototype.onGetOutputs = function() { - return [["C", ""]]; - }; - - LiteGraph.registerNodeType("basic/script", NodeScript); - - - function GenericCompare() { - this.addInput("A", 0); - this.addInput("B", 0); - this.addOutput("true", "boolean"); - this.addOutput("false", "boolean"); - this.addProperty("A", 1); - this.addProperty("B", 1); - this.addProperty("OP", "==", "enum", { values: GenericCompare.values }); - this.addWidget("combo","Op.",this.properties.OP,{ property: "OP", values: GenericCompare.values } ); - - this.size = [80, 60]; - } - - GenericCompare.values = ["==", "!="]; //[">", "<", "==", "!=", "<=", ">=", "||", "&&" ]; - GenericCompare["@OP"] = { - type: "enum", - title: "operation", - values: GenericCompare.values - }; - - GenericCompare.title = "Compare *"; - GenericCompare.desc = "evaluates condition between A and B"; - - GenericCompare.prototype.getTitle = function() { - return "*A " + this.properties.OP + " *B"; - }; - - GenericCompare.prototype.onExecute = function() { - var A = this.getInputData(0); - if (A === undefined) { - A = this.properties.A; - } else { - this.properties.A = A; - } - - var B = this.getInputData(1); - if (B === undefined) { - B = this.properties.B; - } else { - this.properties.B = B; - } - - var result = false; - if (typeof A == typeof B){ - switch (this.properties.OP) { - case "==": - case "!=": - // traverse both objects.. consider that this is not a true deep check! consider underscore or other library for thath :: _isEqual() - result = true; - switch(typeof A){ - case "object": - var aProps = Object.getOwnPropertyNames(A); - var bProps = Object.getOwnPropertyNames(B); - if (aProps.length != bProps.length){ - result = false; - break; - } - for (var i = 0; i < aProps.length; i++) { - var propName = aProps[i]; - if (A[propName] !== B[propName]) { - result = false; - break; - } - } - break; - default: - result = A == B; - } - if (this.properties.OP == "!=") result = !result; - break; - /*case ">": - result = A > B; - break; - case "<": - result = A < B; - break; - case "<=": - result = A <= B; - break; - case ">=": - result = A >= B; - break; - case "||": - result = A || B; - break; - case "&&": - result = A && B; - break;*/ - } - } - this.setOutputData(0, result); - this.setOutputData(1, !result); - }; - - LiteGraph.registerNodeType("basic/CompareValues", GenericCompare); - -})(this); - -//event related nodes -(function(global) { - var LiteGraph = global.LiteGraph; - - //Show value inside the debug console - function LogEvent() { - this.size = [60, 30]; - this.addInput("event", LiteGraph.ACTION); - } - - LogEvent.title = "Log Event"; - LogEvent.desc = "Log event in console"; - - LogEvent.prototype.onAction = function(action, param, options) { - console.log(action, param); - }; - - LiteGraph.registerNodeType("events/log", LogEvent); - - //convert to Event if the value is true - function TriggerEvent() { - this.size = [60, 30]; - this.addInput("if", ""); - this.addOutput("true", LiteGraph.EVENT); - this.addOutput("change", LiteGraph.EVENT); - this.addOutput("false", LiteGraph.EVENT); - this.properties = { only_on_change: true }; - this.prev = 0; - } - - TriggerEvent.title = "TriggerEvent"; - TriggerEvent.desc = "Triggers event if input evaluates to true"; - - TriggerEvent.prototype.onExecute = function( param, options) { - var v = this.getInputData(0); - var changed = (v != this.prev); - if(this.prev === 0) - changed = false; - var must_resend = (changed && this.properties.only_on_change) || (!changed && !this.properties.only_on_change); - if(v && must_resend ) - this.triggerSlot(0, param, null, options); - if(!v && must_resend) - this.triggerSlot(2, param, null, options); - if(changed) - this.triggerSlot(1, param, null, options); - this.prev = v; - }; - - LiteGraph.registerNodeType("events/trigger", TriggerEvent); - - //Sequence of events - function Sequence() { - var that = this; - this.addInput("", LiteGraph.ACTION); - this.addInput("", LiteGraph.ACTION); - this.addInput("", LiteGraph.ACTION); - this.addOutput("", LiteGraph.EVENT); - this.addOutput("", LiteGraph.EVENT); - this.addOutput("", LiteGraph.EVENT); - this.addWidget("button","+",null,function(){ - that.addInput("", LiteGraph.ACTION); - that.addOutput("", LiteGraph.EVENT); - }); - this.size = [90, 70]; - this.flags = { horizontal: true, render_box: false }; - } - - Sequence.title = "Sequence"; - Sequence.desc = "Triggers a sequence of events when an event arrives"; - - Sequence.prototype.getTitle = function() { - return ""; - }; - - Sequence.prototype.onAction = function(action, param, options) { - if (this.outputs) { - options = options || {}; - for (var i = 0; i < this.outputs.length; ++i) { - var output = this.outputs[i]; - //needs more info about this... - if( options.action_call ) // CREATE A NEW ID FOR THE ACTION - options.action_call = options.action_call + "_seq_" + i; - else - options.action_call = this.id + "_" + (action ? action : "action")+"_seq_"+i+"_"+Math.floor(Math.random()*9999); - this.triggerSlot(i, param, null, options); - } - } - }; - - LiteGraph.registerNodeType("events/sequence", Sequence); - - - //Sequence of events - function WaitAll() { - var that = this; - this.addInput("", LiteGraph.ACTION); - this.addInput("", LiteGraph.ACTION); - this.addOutput("", LiteGraph.EVENT); - this.addWidget("button","+",null,function(){ - that.addInput("", LiteGraph.ACTION); - that.size[0] = 90; - }); - this.size = [90, 70]; - this.ready = []; -} - -WaitAll.title = "WaitAll"; -WaitAll.desc = "Wait until all input events arrive then triggers output"; - -WaitAll.prototype.getTitle = function() { - return ""; -}; - -WaitAll.prototype.onDrawBackground = function(ctx) -{ - if (this.flags.collapsed) { - return; - } - for(var i = 0; i < this.inputs.length; ++i) - { - var y = i * LiteGraph.NODE_SLOT_HEIGHT + 10; - ctx.fillStyle = this.ready[i] ? "#AFB" : "#000"; - ctx.fillRect(20, y, 10, 10); - } -} - -WaitAll.prototype.onAction = function(action, param, options, slot_index) { - if(slot_index == null) - return; - - //check all - this.ready.length = this.outputs.length; - this.ready[slot_index] = true; - for(var i = 0; i < this.ready.length;++i) - if(!this.ready[i]) - return; - //pass - this.reset(); - this.triggerSlot(0); -}; - -WaitAll.prototype.reset = function() -{ - this.ready.length = 0; -} - -LiteGraph.registerNodeType("events/waitAll", WaitAll); - - - //Sequencer for events - function Stepper() { - var that = this; - this.properties = { index: 0 }; - this.addInput("index", "number"); - this.addInput("step", LiteGraph.ACTION); - this.addInput("reset", LiteGraph.ACTION); - this.addOutput("index", "number"); - this.addOutput("", LiteGraph.EVENT); - this.addOutput("", LiteGraph.EVENT); - this.addOutput("", LiteGraph.EVENT,{removable:true}); - this.addWidget("button","+",null,function(){ - that.addOutput("", LiteGraph.EVENT, {removable:true}); - }); - this.size = [120, 120]; - this.flags = { render_box: false }; - } - - Stepper.title = "Stepper"; - Stepper.desc = "Trigger events sequentially when an tick arrives"; - - Stepper.prototype.onDrawBackground = function(ctx) - { - if (this.flags.collapsed) { - return; - } - var index = this.properties.index || 0; - ctx.fillStyle = "#AFB"; - var w = this.size[0]; - var y = (index + 1)* LiteGraph.NODE_SLOT_HEIGHT + 4; - ctx.beginPath(); - ctx.moveTo(w - 30, y); - ctx.lineTo(w - 30, y + LiteGraph.NODE_SLOT_HEIGHT); - ctx.lineTo(w - 15, y + LiteGraph.NODE_SLOT_HEIGHT * 0.5); - ctx.fill(); - } - - Stepper.prototype.onExecute = function() - { - var index = this.getInputData(0); - if(index != null) - { - index = Math.floor(index); - index = clamp( index, 0, this.outputs ? (this.outputs.length - 2) : 0 ); - if( index != this.properties.index ) - { - this.properties.index = index; - this.triggerSlot( index+1 ); - } - } - - this.setOutputData(0, this.properties.index ); - } - - Stepper.prototype.onAction = function(action, param) { - if(action == "reset") - this.properties.index = 0; - else if(action == "step") - { - this.triggerSlot(this.properties.index+1, param); - var n = this.outputs ? this.outputs.length - 1 : 0; - this.properties.index = (this.properties.index + 1) % n; - } - }; - - LiteGraph.registerNodeType("events/stepper", Stepper); - - //Filter events - function FilterEvent() { - this.size = [60, 30]; - this.addInput("event", LiteGraph.ACTION); - this.addOutput("event", LiteGraph.EVENT); - this.properties = { - equal_to: "", - has_property: "", - property_equal_to: "" - }; - } - - FilterEvent.title = "Filter Event"; - FilterEvent.desc = "Blocks events that do not match the filter"; - - FilterEvent.prototype.onAction = function(action, param, options) { - if (param == null) { - return; - } - - if (this.properties.equal_to && this.properties.equal_to != param) { - return; - } - - if (this.properties.has_property) { - var prop = param[this.properties.has_property]; - if (prop == null) { - return; - } - - if ( - this.properties.property_equal_to && - this.properties.property_equal_to != prop - ) { - return; - } - } - - this.triggerSlot(0, param, null, options); - }; - - LiteGraph.registerNodeType("events/filter", FilterEvent); - - - function EventBranch() { - this.addInput("in", LiteGraph.ACTION); - this.addInput("cond", "boolean"); - this.addOutput("true", LiteGraph.EVENT); - this.addOutput("false", LiteGraph.EVENT); - this.size = [120, 60]; - this._value = false; - } - - EventBranch.title = "Branch"; - EventBranch.desc = "If condition is true, outputs triggers true, otherwise false"; - - EventBranch.prototype.onExecute = function() { - this._value = this.getInputData(1); - } - - EventBranch.prototype.onAction = function(action, param, options) { - this._value = this.getInputData(1); - this.triggerSlot(this._value ? 0 : 1, param, null, options); - } - - LiteGraph.registerNodeType("events/branch", EventBranch); - - //Show value inside the debug console - function EventCounter() { - this.addInput("inc", LiteGraph.ACTION); - this.addInput("dec", LiteGraph.ACTION); - this.addInput("reset", LiteGraph.ACTION); - this.addOutput("change", LiteGraph.EVENT); - this.addOutput("num", "number"); - this.addProperty("doCountExecution", false, "boolean", {name: "Count Executions"}); - this.addWidget("toggle","Count Exec.",this.properties.doCountExecution,"doCountExecution"); - this.num = 0; - } - - EventCounter.title = "Counter"; - EventCounter.desc = "Counts events"; - - EventCounter.prototype.getTitle = function() { - if (this.flags.collapsed) { - return String(this.num); - } - return this.title; - }; - - EventCounter.prototype.onAction = function(action, param, options) { - var v = this.num; - if (action == "inc") { - this.num += 1; - } else if (action == "dec") { - this.num -= 1; - } else if (action == "reset") { - this.num = 0; - } - if (this.num != v) { - this.trigger("change", this.num); - } - }; - - EventCounter.prototype.onDrawBackground = function(ctx) { - if (this.flags.collapsed) { - return; - } - ctx.fillStyle = "#AAA"; - ctx.font = "20px Arial"; - ctx.textAlign = "center"; - ctx.fillText(this.num, this.size[0] * 0.5, this.size[1] * 0.5); - }; - - EventCounter.prototype.onExecute = function() { - if(this.properties.doCountExecution){ - this.num += 1; - } - this.setOutputData(1, this.num); - }; - - LiteGraph.registerNodeType("events/counter", EventCounter); - - //Show value inside the debug console - function DelayEvent() { - this.size = [60, 30]; - this.addProperty("time_in_ms", 1000); - this.addInput("event", LiteGraph.ACTION); - this.addOutput("on_time", LiteGraph.EVENT); - - this._pending = []; - } - - DelayEvent.title = "Delay"; - DelayEvent.desc = "Delays one event"; - - DelayEvent.prototype.onAction = function(action, param, options) { - var time = this.properties.time_in_ms; - if (time <= 0) { - this.trigger(null, param, options); - } else { - this._pending.push([time, param]); - } - }; - - DelayEvent.prototype.onExecute = function(param, options) { - var dt = this.graph.elapsed_time * 1000; //in ms - - if (this.isInputConnected(1)) { - this.properties.time_in_ms = this.getInputData(1); - } - - for (var i = 0; i < this._pending.length; ++i) { - var actionPass = this._pending[i]; - actionPass[0] -= dt; - if (actionPass[0] > 0) { - continue; - } - - //remove - this._pending.splice(i, 1); - --i; - - //trigger - this.trigger(null, actionPass[1], options); - } - }; - - DelayEvent.prototype.onGetInputs = function() { - return [["event", LiteGraph.ACTION], ["time_in_ms", "number"]]; - }; - - LiteGraph.registerNodeType("events/delay", DelayEvent); - - //Show value inside the debug console - function TimerEvent() { - this.addProperty("interval", 1000); - this.addProperty("event", "tick"); - this.addOutput("on_tick", LiteGraph.EVENT); - this.time = 0; - this.last_interval = 1000; - this.triggered = false; - } - - TimerEvent.title = "Timer"; - TimerEvent.desc = "Sends an event every N milliseconds"; - - TimerEvent.prototype.onStart = function() { - this.time = 0; - }; - - TimerEvent.prototype.getTitle = function() { - return "Timer: " + this.last_interval.toString() + "ms"; - }; - - TimerEvent.on_color = "#AAA"; - TimerEvent.off_color = "#222"; - - TimerEvent.prototype.onDrawBackground = function() { - this.boxcolor = this.triggered - ? TimerEvent.on_color - : TimerEvent.off_color; - this.triggered = false; - }; - - TimerEvent.prototype.onExecute = function() { - var dt = this.graph.elapsed_time * 1000; //in ms - - var trigger = this.time == 0; - - this.time += dt; - this.last_interval = Math.max( - 1, - this.getInputOrProperty("interval") | 0 - ); - - if ( - !trigger && - (this.time < this.last_interval || isNaN(this.last_interval)) - ) { - if (this.inputs && this.inputs.length > 1 && this.inputs[1]) { - this.setOutputData(1, false); - } - return; - } - - this.triggered = true; - this.time = this.time % this.last_interval; - this.trigger("on_tick", this.properties.event); - if (this.inputs && this.inputs.length > 1 && this.inputs[1]) { - this.setOutputData(1, true); - } - }; - - TimerEvent.prototype.onGetInputs = function() { - return [["interval", "number"]]; - }; - - TimerEvent.prototype.onGetOutputs = function() { - return [["tick", "boolean"]]; - }; - - LiteGraph.registerNodeType("events/timer", TimerEvent); - - - - function SemaphoreEvent() { - this.addInput("go", LiteGraph.ACTION ); - this.addInput("green", LiteGraph.ACTION ); - this.addInput("red", LiteGraph.ACTION ); - this.addOutput("continue", LiteGraph.EVENT ); - this.addOutput("blocked", LiteGraph.EVENT ); - this.addOutput("is_green", "boolean" ); - this._ready = false; - this.properties = {}; - var that = this; - this.addWidget("button","reset","",function(){ - that._ready = false; - }); - } - - SemaphoreEvent.title = "Semaphore Event"; - SemaphoreEvent.desc = "Until both events are not triggered, it doesnt continue."; - - SemaphoreEvent.prototype.onExecute = function() - { - this.setOutputData(1,this._ready); - this.boxcolor = this._ready ? "#9F9" : "#FA5"; - } - - SemaphoreEvent.prototype.onAction = function(action, param) { - if( action == "go" ) - this.triggerSlot( this._ready ? 0 : 1 ); - else if( action == "green" ) - this._ready = true; - else if( action == "red" ) - this._ready = false; - }; - - LiteGraph.registerNodeType("events/semaphore", SemaphoreEvent); - - function OnceEvent() { - this.addInput("in", LiteGraph.ACTION ); - this.addInput("reset", LiteGraph.ACTION ); - this.addOutput("out", LiteGraph.EVENT ); - this._once = false; - this.properties = {}; - var that = this; - this.addWidget("button","reset","",function(){ - that._once = false; - }); - } - - OnceEvent.title = "Once"; - OnceEvent.desc = "Only passes an event once, then gets locked"; - - OnceEvent.prototype.onAction = function(action, param) { - if( action == "in" && !this._once ) - { - this._once = true; - this.triggerSlot( 0, param ); - } - else if( action == "reset" ) - this._once = false; - }; - - LiteGraph.registerNodeType("events/once", OnceEvent); - - function DataStore() { - this.addInput("data", 0); - this.addInput("assign", LiteGraph.ACTION); - this.addOutput("data", 0); - this._last_value = null; - this.properties = { data: null, serialize: true }; - var that = this; - this.addWidget("button","store","",function(){ - that.properties.data = that._last_value; - }); - } - - DataStore.title = "Data Store"; - DataStore.desc = "Stores data and only changes when event is received"; - - DataStore.prototype.onExecute = function() - { - this._last_value = this.getInputData(0); - this.setOutputData(0, this.properties.data ); - } - - DataStore.prototype.onAction = function(action, param, options) { - this.properties.data = this._last_value; - }; - - DataStore.prototype.onSerialize = function(o) - { - if(o.data == null) - return; - if(this.properties.serialize == false || (o.data.constructor !== String && o.data.constructor !== Number && o.data.constructor !== Boolean && o.data.constructor !== Array && o.data.constructor !== Object )) - o.data = null; - } - - LiteGraph.registerNodeType("basic/data_store", DataStore); - - - -})(this); - -//widgets +// basic nodes (function(global) { var LiteGraph = global.LiteGraph; - /* Button ****************/ + // Constant + function Time() { + this.addOutput("in ms", "number"); + this.addOutput("in sec", "number"); + } - function WidgetButton() { - this.addOutput("", LiteGraph.EVENT); - this.addOutput("", "boolean"); - this.addProperty("text", "click me"); - this.addProperty("font_size", 30); - this.addProperty("message", ""); - this.size = [164, 84]; - this.clicked = false; + Time.title = "Time"; + Time.desc = "Time"; + + Time.prototype.onExecute = function() { + this.setOutputData(0, this.graph.globaltime * 1000); + this.setOutputData(1, this.graph.globaltime); + }; + + LiteGraph.registerNodeType("basic/time", Time); + + // Subgraph: a node that contains a graph + function Subgraph() { + var that = this; + this.size = [140, 80]; + this.properties = { enabled: true }; + this.enabled = true; + + // create inner graph + this.subgraph = new LiteGraph.LGraph(); + this.subgraph._subgraph_node = this; + this.subgraph._is_subgraph = true; + + this.subgraph.onTrigger = this.onSubgraphTrigger.bind(this); + + // nodes input node added inside + this.subgraph.onInputAdded = this.onSubgraphNewInput.bind(this); + this.subgraph.onInputRenamed = this.onSubgraphRenamedInput.bind(this); + this.subgraph.onInputTypeChanged = this.onSubgraphTypeChangeInput.bind(this); + this.subgraph.onInputRemoved = this.onSubgraphRemovedInput.bind(this); + + this.subgraph.onOutputAdded = this.onSubgraphNewOutput.bind(this); + this.subgraph.onOutputRenamed = this.onSubgraphRenamedOutput.bind(this); + this.subgraph.onOutputTypeChanged = this.onSubgraphTypeChangeOutput.bind(this); + this.subgraph.onOutputRemoved = this.onSubgraphRemovedOutput.bind(this); } - WidgetButton.title = "Button"; - WidgetButton.desc = "Triggers an event"; + Subgraph.title = "Subgraph"; + Subgraph.desc = "Graph inside a node"; + Subgraph.title_color = "#334"; - WidgetButton.font = "Arial"; - WidgetButton.prototype.onDrawForeground = function(ctx) { + Subgraph.prototype.onGetInputs = function() { + return [["enabled", "boolean"]]; + }; + + /* + Subgraph.prototype.onDrawTitle = function(ctx) { if (this.flags.collapsed) { return; } - var margin = 10; - ctx.fillStyle = "black"; - ctx.fillRect( - margin + 1, - margin + 1, - this.size[0] - margin * 2, - this.size[1] - margin * 2 - ); - ctx.fillStyle = "#AAF"; - ctx.fillRect( - margin - 1, - margin - 1, - this.size[0] - margin * 2, - this.size[1] - margin * 2 - ); - ctx.fillStyle = this.clicked - ? "white" - : this.mouseOver - ? "#668" - : "#334"; - ctx.fillRect( - margin, - margin, - this.size[0] - margin * 2, - this.size[1] - margin * 2 - ); - if (this.properties.text || this.properties.text === 0) { - var font_size = this.properties.font_size || 30; - ctx.textAlign = "center"; - ctx.fillStyle = this.clicked ? "black" : "white"; - ctx.font = font_size + "px " + WidgetButton.font; - ctx.fillText( - this.properties.text, - this.size[0] * 0.5, - this.size[1] * 0.5 + font_size * 0.3 - ); - ctx.textAlign = "left"; - } + ctx.fillStyle = "#555"; + var w = LiteGraph.NODE_TITLE_HEIGHT; + var x = this.size[0] - w; + ctx.fillRect(x, -w, w, w); + ctx.fillStyle = "#333"; + ctx.beginPath(); + ctx.moveTo(x + w * 0.2, -w * 0.6); + ctx.lineTo(x + w * 0.8, -w * 0.6); + ctx.lineTo(x + w * 0.5, -w * 0.3); + ctx.fill(); }; + */ - WidgetButton.prototype.onMouseDown = function(e, local_pos) { + Subgraph.prototype.onDblClick = function(e, pos, graphcanvas) { + var that = this; + setTimeout(function() { + graphcanvas.openSubgraph(that.subgraph); + }, 10); + }; + + /* + Subgraph.prototype.onMouseDown = function(e, pos, graphcanvas) { if ( - local_pos[0] > 1 && - local_pos[1] > 1 && - local_pos[0] < this.size[0] - 2 && - local_pos[1] < this.size[1] - 2 + !this.flags.collapsed && + pos[0] > this.size[0] - LiteGraph.NODE_TITLE_HEIGHT && + pos[1] < 0 ) { - this.clicked = true; - this.setOutputData(1, this.clicked); - this.triggerSlot(0, this.properties.message); - return true; + var that = this; + setTimeout(function() { + graphcanvas.openSubgraph(that.subgraph); + }, 10); } }; + */ - WidgetButton.prototype.onExecute = function() { - this.setOutputData(1, this.clicked); + Subgraph.prototype.onAction = function(action, param) { + this.subgraph.onAction(action, param); }; - WidgetButton.prototype.onMouseUp = function(e) { - this.clicked = false; - }; + Subgraph.prototype.onExecute = function() { + this.enabled = this.getInputOrProperty("enabled"); + if (!this.enabled) { + return; + } - LiteGraph.registerNodeType("widget/button", WidgetButton); + // send inputs to subgraph global inputs + if (this.inputs) { + for (var i = 0; i < this.inputs.length; i++) { + var input = this.inputs[i]; + var value = this.getInputData(i); + this.subgraph.setInputData(input.name, value); + } + } - function WidgetToggle() { - this.addInput("", "boolean"); - this.addInput("e", LiteGraph.ACTION); - this.addOutput("v", "boolean"); - this.addOutput("e", LiteGraph.EVENT); - this.properties = { font: "", value: false }; - this.size = [160, 44]; - } + // execute + this.subgraph.runStep(); - WidgetToggle.title = "Toggle"; - WidgetToggle.desc = "Toggles between true or false"; + // send subgraph global outputs to outputs + if (this.outputs) { + for (var i = 0; i < this.outputs.length; i++) { + var output = this.outputs[i]; + var value = this.subgraph.getOutputData(output.name); + this.setOutputData(i, value); + } + } + }; - WidgetToggle.prototype.onDrawForeground = function(ctx) { - if (this.flags.collapsed) { - return; + Subgraph.prototype.sendEventToAllNodes = function(eventname, param, mode) { + if (this.enabled) { + this.subgraph.sendEventToAllNodes(eventname, param, mode); } + }; - var size = this.size[1] * 0.5; - var margin = 0.25; - var h = this.size[1] * 0.8; - ctx.font = this.properties.font || (size * 0.8).toFixed(0) + "px Arial"; - var w = ctx.measureText(this.title).width; - var x = (this.size[0] - (w + size)) * 0.5; - - ctx.fillStyle = "#AAA"; - ctx.fillRect(x, h - size, size, size); + Subgraph.prototype.onDrawBackground = function (ctx, graphcanvas, canvas, pos) { + if (this.flags.collapsed) + return; + var y = this.size[1] - LiteGraph.NODE_TITLE_HEIGHT + 0.5; + // button + var over = LiteGraph.isInsideRectangle(pos[0], pos[1], this.pos[0], this.pos[1] + y, this.size[0], LiteGraph.NODE_TITLE_HEIGHT); + let overleft = LiteGraph.isInsideRectangle(pos[0], pos[1], this.pos[0], this.pos[1] + y, this.size[0] / 2, LiteGraph.NODE_TITLE_HEIGHT) + ctx.fillStyle = over ? "#555" : "#222"; + ctx.beginPath(); + if (this._shape == LiteGraph.BOX_SHAPE) { + if (overleft) { + ctx.rect(0, y, this.size[0] / 2 + 1, LiteGraph.NODE_TITLE_HEIGHT); + } else { + ctx.rect(this.size[0] / 2, y, this.size[0] / 2 + 1, LiteGraph.NODE_TITLE_HEIGHT); + } + } else { + if (overleft) { + ctx.roundRect(0, y, this.size[0] / 2 + 1, LiteGraph.NODE_TITLE_HEIGHT, [0,0, 8,8]); + } else { + ctx.roundRect(this.size[0] / 2, y, this.size[0] / 2 + 1, LiteGraph.NODE_TITLE_HEIGHT, [0,0, 8,8]); + } + } + if (over) { + ctx.fill(); + } else { + ctx.fillRect(0, y, this.size[0] + 1, LiteGraph.NODE_TITLE_HEIGHT); + } + // button + ctx.textAlign = "center"; + ctx.font = "24px Arial"; + ctx.fillStyle = over ? "#DDD" : "#999"; + ctx.fillText("+", this.size[0] * 0.25, y + 24); + ctx.fillText("+", this.size[0] * 0.75, y + 24); + } - ctx.fillStyle = this.properties.value ? "#AEF" : "#000"; - ctx.fillRect( - x + size * margin, - h - size + size * margin, - size * (1 - margin * 2), - size * (1 - margin * 2) - ); + // Subgraph.prototype.onMouseDown = function(e, localpos, graphcanvas) + // { + // var y = this.size[1] - LiteGraph.NODE_TITLE_HEIGHT + 0.5; + // if(localpos[1] > y) + // { + // graphcanvas.showSubgraphPropertiesDialog(this); + // } + // } + Subgraph.prototype.onMouseDown = function (e, localpos, graphcanvas) { + var y = this.size[1] - LiteGraph.NODE_TITLE_HEIGHT + 0.5; + console.log(0) + if (localpos[1] > y) { + if (localpos[0] < this.size[0] / 2) { + console.log(1) + graphcanvas.showSubgraphPropertiesDialog(this); + } else { + console.log(2) + graphcanvas.showSubgraphPropertiesDialogRight(this); + } + } + } + Subgraph.prototype.computeSize = function() { + var num_inputs = this.inputs ? this.inputs.length : 0; + var num_outputs = this.outputs ? this.outputs.length : 0; + return [ 200, Math.max(num_inputs,num_outputs) * LiteGraph.NODE_SLOT_HEIGHT + LiteGraph.NODE_TITLE_HEIGHT ]; + } - ctx.textAlign = "left"; - ctx.fillStyle = "#AAA"; - ctx.fillText(this.title, size * 1.2 + x, h * 0.85); - ctx.textAlign = "left"; + //* *** INPUTS *********************************** + Subgraph.prototype.onSubgraphTrigger = function(event, param) { + var slot = this.findOutputSlot(event); + if (slot != -1) { + this.triggerSlot(slot); + } }; - WidgetToggle.prototype.onAction = function(action) { - this.properties.value = !this.properties.value; - this.trigger("e", this.properties.value); + Subgraph.prototype.onSubgraphNewInput = function(name, type) { + var slot = this.findInputSlot(name); + if (slot == -1) { + // add input to the node + this.addInput(name, type); + } }; - WidgetToggle.prototype.onExecute = function() { - var v = this.getInputData(0); - if (v != null) { - this.properties.value = v; + Subgraph.prototype.onSubgraphRenamedInput = function(oldname, name) { + var slot = this.findInputSlot(oldname); + if (slot == -1) { + return; } - this.setOutputData(0, this.properties.value); + var info = this.getInputInfo(slot); + info.name = name; }; - WidgetToggle.prototype.onMouseDown = function(e, local_pos) { - if ( - local_pos[0] > 1 && - local_pos[1] > 1 && - local_pos[0] < this.size[0] - 2 && - local_pos[1] < this.size[1] - 2 - ) { - this.properties.value = !this.properties.value; - this.graph._version++; - this.trigger("e", this.properties.value); - return true; + Subgraph.prototype.onSubgraphTypeChangeInput = function(name, type) { + var slot = this.findInputSlot(name); + if (slot == -1) { + return; } + var info = this.getInputInfo(slot); + info.type = type; }; - LiteGraph.registerNodeType("widget/toggle", WidgetToggle); - - /* Number ****************/ + Subgraph.prototype.onSubgraphRemovedInput = function(name) { + var slot = this.findInputSlot(name); + if (slot == -1) { + return; + } + this.removeInput(slot); + }; - function WidgetNumber() { - this.addOutput("", "number"); - this.size = [80, 60]; - this.properties = { min: -1000, max: 1000, value: 1, step: 1 }; - this.old_y = -1; - this._remainder = 0; - this._precision = 0; - this.mouse_captured = false; - } + //* *** OUTPUTS *********************************** + Subgraph.prototype.onSubgraphNewOutput = function(name, type) { + var slot = this.findOutputSlot(name); + if (slot == -1) { + this.addOutput(name, type); + } + }; - WidgetNumber.title = "Number"; - WidgetNumber.desc = "Widget to select number value"; + Subgraph.prototype.onSubgraphRenamedOutput = function(oldname, name) { + var slot = this.findOutputSlot(oldname); + if (slot == -1) { + return; + } + var info = this.getOutputInfo(slot); + info.name = name; + }; - WidgetNumber.pixels_threshold = 10; - WidgetNumber.markers_color = "#666"; + Subgraph.prototype.onSubgraphTypeChangeOutput = function(name, type) { + var slot = this.findOutputSlot(name); + if (slot == -1) { + return; + } + var info = this.getOutputInfo(slot); + info.type = type; + }; - WidgetNumber.prototype.onDrawForeground = function(ctx) { - var x = this.size[0] * 0.5; - var h = this.size[1]; - if (h > 30) { - ctx.fillStyle = WidgetNumber.markers_color; - ctx.beginPath(); - ctx.moveTo(x, h * 0.1); - ctx.lineTo(x + h * 0.1, h * 0.2); - ctx.lineTo(x + h * -0.1, h * 0.2); - ctx.fill(); - ctx.beginPath(); - ctx.moveTo(x, h * 0.9); - ctx.lineTo(x + h * 0.1, h * 0.8); - ctx.lineTo(x + h * -0.1, h * 0.8); - ctx.fill(); - ctx.font = (h * 0.7).toFixed(1) + "px Arial"; - } else { - ctx.font = (h * 0.8).toFixed(1) + "px Arial"; + Subgraph.prototype.onSubgraphRemovedOutput = function(name) { + var slot = this.findOutputSlot(name); + if (slot == -1) { + return; } + this.removeOutput(slot); + }; + // ***************************************************** - ctx.textAlign = "center"; - ctx.font = (h * 0.7).toFixed(1) + "px Arial"; - ctx.fillStyle = "#EEE"; - ctx.fillText( - this.properties.value.toFixed(this._precision), - x, - h * 0.75 - ); + Subgraph.prototype.getExtraMenuOptions = function(graphcanvas) { + var that = this; + return [ + { + content: "Open", + callback: function() { + graphcanvas.openSubgraph(that.subgraph); + }, + }, + ]; }; - WidgetNumber.prototype.onExecute = function() { - this.setOutputData(0, this.properties.value); + Subgraph.prototype.onResize = function(size) { + size[1] += 20; }; - WidgetNumber.prototype.onPropertyChanged = function(name, value) { - var t = (this.properties.step + "").split("."); - this._precision = t.length > 1 ? t[1].length : 0; + Subgraph.prototype.serialize = function() { + var data = LiteGraph.LGraphNode.prototype.serialize.call(this); + data.subgraph = this.subgraph.serialize(); + return data; }; + // no need to define node.configure, the default method detects node.subgraph and passes the object to node.subgraph.configure() - WidgetNumber.prototype.onMouseDown = function(e, pos) { - if (pos[1] < 0) { - return; + Subgraph.prototype.reassignSubgraphUUIDs = function(graph) { + const idMap = { nodeIDs: {}, linkIDs: {} } + + for (const node of graph.nodes) { + const oldID = node.id + const newID = LiteGraph.uuidv4() + node.id = newID + + if (idMap.nodeIDs[oldID] || idMap.nodeIDs[newID]) { + throw new Error(`New/old node UUID wasn't unique in changed map! ${oldID} ${newID}`) + } + + idMap.nodeIDs[oldID] = newID + idMap.nodeIDs[newID] = oldID } - this.old_y = e.canvasY; - this.captureInput(true); - this.mouse_captured = true; + for (const link of graph.links) { + const oldID = link[0] + const newID = LiteGraph.uuidv4(); + link[0] = newID - return true; - }; + if (idMap.linkIDs[oldID] || idMap.linkIDs[newID]) { + throw new Error(`New/old link UUID wasn't unique in changed map! ${oldID} ${newID}`) + } - WidgetNumber.prototype.onMouseMove = function(e) { - if (!this.mouse_captured) { - return; + idMap.linkIDs[oldID] = newID + idMap.linkIDs[newID] = oldID + + const nodeFrom = link[1] + const nodeTo = link[3] + + if (!idMap.nodeIDs[nodeFrom]) { + throw new Error(`Old node UUID not found in mapping! ${nodeFrom}`) + } + + link[1] = idMap.nodeIDs[nodeFrom] + + if (!idMap.nodeIDs[nodeTo]) { + throw new Error(`Old node UUID not found in mapping! ${nodeTo}`) + } + + link[3] = idMap.nodeIDs[nodeTo] } - var delta = this.old_y - e.canvasY; - if (e.shiftKey) { - delta *= 10; + // Reconnect links + for (const node of graph.nodes) { + if (node.inputs) { + for (const input of node.inputs) { + if (input.link) { + input.link = idMap.linkIDs[input.link] + } + } + } + if (node.outputs) { + for (const output of node.outputs) { + if (output.links) { + output.links = output.links.map((l) => idMap.linkIDs[l]); + } + } + } } - if (e.metaKey || e.altKey) { - delta *= 0.1; + + // Recurse! + for (const node of graph.nodes) { + if (node.type === "graph/subgraph") { + const merge = reassignGraphUUIDs(node.subgraph); + idMap.nodeIDs.assign(merge.nodeIDs) + idMap.linkIDs.assign(merge.linkIDs) + } } - this.old_y = e.canvasY; + }; - var steps = this._remainder + delta / WidgetNumber.pixels_threshold; - this._remainder = steps % 1; - steps = steps | 0; + Subgraph.prototype.clone = function() { + var node = LiteGraph.createNode(this.type); + var data = this.serialize(); - var v = clamp( - this.properties.value + steps * this.properties.step, - this.properties.min, - this.properties.max - ); - this.properties.value = v; - this.graph._version++; - this.setDirtyCanvas(true); - }; + if (LiteGraph.use_uuids) { + // LGraph.serialize() seems to reuse objects in the original graph. But we + // need to change node IDs here, so clone it first. + const subgraph = LiteGraph.cloneObject(data.subgraph) - WidgetNumber.prototype.onMouseUp = function(e, pos) { - if (e.click_time < 200) { - var steps = pos[1] > this.size[1] * 0.5 ? -1 : 1; - this.properties.value = clamp( - this.properties.value + steps * this.properties.step, - this.properties.min, - this.properties.max - ); - this.graph._version++; - this.setDirtyCanvas(true); - } + this.reassignSubgraphUUIDs(subgraph); - if (this.mouse_captured) { - this.mouse_captured = false; - this.captureInput(false); + data.subgraph = subgraph; } + + delete data["id"]; + delete data["inputs"]; + delete data["outputs"]; + node.configure(data); + return node; }; - LiteGraph.registerNodeType("widget/number", WidgetNumber); + Subgraph.prototype.buildFromNodes = function(nodes) { + // clear all? + // TODO + // nodes that connect data between parent graph and subgraph + var subgraph_inputs = []; + var subgraph_outputs = []; - /* Combo ****************/ + // mark inner nodes + var ids = {}; + var min_x = 0; + var max_x = 0; + for(var i = 0; i < nodes.length; ++i) { + var node = nodes[i]; + ids[node.id] = node; + min_x = Math.min( node.pos[0], min_x ); + max_x = Math.max( node.pos[0], min_x ); + } - function WidgetCombo() { - this.addOutput("", "string"); - this.addOutput("change", LiteGraph.EVENT); - this.size = [80, 60]; - this.properties = { value: "A", values:"A;B;C" }; - this.old_y = -1; - this.mouse_captured = false; - this._values = this.properties.values.split(";"); - var that = this; - this.widgets_up = true; - this.widget = this.addWidget("combo","", this.properties.value, function(v){ - that.properties.value = v; - that.triggerSlot(1, v); - }, { property: "value", values: this._values } ); - } + var last_input_y = 0; + var last_output_y = 0; - WidgetCombo.title = "Combo"; - WidgetCombo.desc = "Widget to select from a list"; + for(var i = 0; i < nodes.length; ++i) { + var node = nodes[i]; + // check inputs + if( node.inputs ) + for(var j = 0; j < node.inputs.length; ++j) { + var input = node.inputs[j]; + if( !input || !input.link ) + continue; + var link = node.graph.links[input.link]; + if(!link) + continue; + if( ids[link.origin_id] ) + continue; + // this.addInput(input.name,link.type); + this.subgraph.addInput(input.name,link.type); + /* + var input_node = LiteGraph.createNode("graph/input"); + this.subgraph.add( input_node ); + input_node.pos = [min_x - 200, last_input_y ]; + last_input_y += 100; + */ + } - WidgetCombo.prototype.onExecute = function() { - this.setOutputData( 0, this.properties.value ); - }; + // check outputs + if( node.outputs ) + for(var j = 0; j < node.outputs.length; ++j) { + var output = node.outputs[j]; + if( !output || !output.links || !output.links.length ) + continue; + var is_external = false; + for(var k = 0; k < output.links.length; ++k) { + var link = node.graph.links[output.links[k]]; + if(!link) + continue; + if( ids[link.target_id] ) + continue; + is_external = true; + break; + } + if(!is_external) + continue; + // this.addOutput(output.name,output.type); + /* + var output_node = LiteGraph.createNode("graph/output"); + this.subgraph.add( output_node ); + output_node.pos = [max_x + 50, last_output_y ]; + last_output_y += 100; + */ + } + } - WidgetCombo.prototype.onPropertyChanged = function(name, value) { - if(name == "values") - { - this._values = value.split(";"); - this.widget.options.values = this._values; - } - else if(name == "value") - { - this.widget.value = value; - } - }; + // detect inputs and outputs + // split every connection in two data_connection nodes + // keep track of internal connections + // connect external connections - LiteGraph.registerNodeType("widget/combo", WidgetCombo); + // clone nodes inside subgraph and try to reconnect them + // connect edge subgraph nodes to extarnal connections nodes + } - /* Knob ****************/ + LiteGraph.Subgraph = Subgraph; + LiteGraph.registerNodeType("graph/subgraph", Subgraph); - function WidgetKnob() { + // Input for a subgraph + function GraphInput() { this.addOutput("", "number"); - this.size = [64, 84]; + + this.name_in_graph = ""; this.properties = { - min: 0, - max: 1, - value: 0.5, - color: "#7AF", - precision: 2 + name: "", + type: "number", + value: 0, }; - this.value = -1; - } - WidgetKnob.title = "Knob"; - WidgetKnob.desc = "Circular controller"; - WidgetKnob.size = [80, 100]; - - WidgetKnob.prototype.onDrawForeground = function(ctx) { - if (this.flags.collapsed) { - return; - } + var that = this; - if (this.value == -1) { - this.value = - (this.properties.value - this.properties.min) / - (this.properties.max - this.properties.min); - } + this.name_widget = this.addWidget( + "text", + "Name", + this.properties.name, + function(v) { + if (!v) { + return; + } + that.setProperty("name",v); + }, + ); + this.type_widget = this.addWidget( + "text", + "Type", + this.properties.type, + function(v) { + that.setProperty("type",v); + }, + ); - var center_x = this.size[0] * 0.5; - var center_y = this.size[1] * 0.5; - var radius = Math.min(this.size[0], this.size[1]) * 0.5 - 5; - var w = Math.floor(radius * 0.05); + this.value_widget = this.addWidget( + "number", + "Value", + this.properties.value, + function(v) { + that.setProperty("value",v); + }, + ); - ctx.globalAlpha = 1; - ctx.save(); - ctx.translate(center_x, center_y); - ctx.rotate(Math.PI * 0.75); + this.widgets_up = true; + this.size = [180, 90]; + } - //bg - ctx.fillStyle = "rgba(0,0,0,0.5)"; - ctx.beginPath(); - ctx.moveTo(0, 0); - ctx.arc(0, 0, radius, 0, Math.PI * 1.5); - ctx.fill(); + GraphInput.title = "Input"; + GraphInput.desc = "Input of the graph"; - //value - ctx.strokeStyle = "black"; - ctx.fillStyle = this.properties.color; - ctx.lineWidth = 2; - ctx.beginPath(); - ctx.moveTo(0, 0); - ctx.arc( - 0, - 0, - radius - 4, - 0, - Math.PI * 1.5 * Math.max(0.01, this.value) - ); - ctx.closePath(); - ctx.fill(); - //ctx.stroke(); - ctx.lineWidth = 1; - ctx.globalAlpha = 1; - ctx.restore(); + GraphInput.prototype.onConfigure = function() { + this.updateType(); + } - //inner - ctx.fillStyle = "black"; - ctx.beginPath(); - ctx.arc(center_x, center_y, radius * 0.75, 0, Math.PI * 2, true); - ctx.fill(); + // ensures the type in the node output and the type in the associated graph input are the same + GraphInput.prototype.updateType = function() { + var type = this.properties.type; + this.type_widget.value = type; + + // update output + if(this.outputs[0].type != type) { + if (!LiteGraph.isValidConnection(this.outputs[0].type,type)) + this.disconnectOutput(0); + this.outputs[0].type = type; + } + + // update widget + if(type == "number") { + this.value_widget.type = "number"; + this.value_widget.value = 0; + } else if(type == "boolean") { + this.value_widget.type = "toggle"; + this.value_widget.value = true; + } else if(type == "string") { + this.value_widget.type = "text"; + this.value_widget.value = ""; + } else { + this.value_widget.type = null; + this.value_widget.value = null; + } + this.properties.value = this.value_widget.value; - //miniball - ctx.fillStyle = this.mouseOver ? "white" : this.properties.color; - ctx.beginPath(); - var angle = this.value * Math.PI * 1.5 + Math.PI * 0.75; - ctx.arc( - center_x + Math.cos(angle) * radius * 0.65, - center_y + Math.sin(angle) * radius * 0.65, - radius * 0.05, - 0, - Math.PI * 2, - true - ); - ctx.fill(); + // update graph + if (this.graph && this.name_in_graph) { + this.graph.changeInputType(this.name_in_graph, type); + } + } - //text - ctx.fillStyle = this.mouseOver ? "white" : "#AAA"; - ctx.font = Math.floor(radius * 0.5) + "px Arial"; - ctx.textAlign = "center"; - ctx.fillText( - this.properties.value.toFixed(this.properties.precision), - center_x, - center_y + radius * 0.15 - ); - }; + // this is executed AFTER the property has changed + GraphInput.prototype.onPropertyChanged = function(name,v) { + if( name == "name" ) { + if (v == "" || v == this.name_in_graph || v == "enabled") { + return false; + } + if(this.graph) { + if (this.name_in_graph) { + // already added + this.graph.renameInput( this.name_in_graph, v ); + } else { + this.graph.addInput( v, this.properties.type ); + } + } // what if not?! + this.name_widget.value = v; + this.name_in_graph = v; + } else if( name == "type" ) { + this.updateType(); + } + } - WidgetKnob.prototype.onExecute = function() { - this.setOutputData(0, this.properties.value); - this.boxcolor = LiteGraph.colorToString([ - this.value, - this.value, - this.value - ]); + GraphInput.prototype.getTitle = function() { + if (this.flags.collapsed) { + return this.properties.name; + } + return this.title; }; - WidgetKnob.prototype.onMouseDown = function(e) { - 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 || - LiteGraph.distance( - [e.canvasX, e.canvasY], - [this.pos[0] + this.center[0], this.pos[1] + this.center[1]] - ) > this.radius - ) { - return false; + GraphInput.prototype.onAction = function(action, param) { + if (this.properties.type == LiteGraph.EVENT) { + this.triggerSlot(0, param); } - this.oldmouse = [e.canvasX - this.pos[0], e.canvasY - this.pos[1]]; - this.captureInput(true); - return true; }; - WidgetKnob.prototype.onMouseMove = function(e) { - if (!this.oldmouse) { + GraphInput.prototype.onExecute = function() { + var name = this.properties.name; + // read from global input + var data = this.graph.inputs[name]; + if (!data) { + this.setOutputData(0, this.properties.value ); 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); + this.setOutputData(0, data.value !== undefined ? data.value : this.properties.value ); }; - WidgetKnob.prototype.onMouseUp = function(e) { - if (this.oldmouse) { - this.oldmouse = null; - this.captureInput(false); + GraphInput.prototype.onRemoved = function() { + if (this.name_in_graph) { + this.graph.removeInput(this.name_in_graph); } }; - WidgetKnob.prototype.onPropertyChanged = function(name, value) { - if (name == "min" || name == "max" || name == "value") { - this.properties[name] = parseFloat(value); - return true; //block - } - }; + LiteGraph.GraphInput = GraphInput; + LiteGraph.registerNodeType("graph/input", GraphInput); - LiteGraph.registerNodeType("widget/knob", WidgetKnob); + // Output for a subgraph + function GraphOutput() { + this.addInput("", ""); - //Show value inside the debug console - function WidgetSliderGUI() { - this.addOutput("", "number"); - this.properties = { - value: 0.5, - min: 0, - max: 1, - text: "V" - }; + this.name_in_graph = ""; + this.properties = { name: "", type: "" }; var that = this; - this.size = [140, 40]; - this.slider = this.addWidget( - "slider", - "V", - this.properties.value, - function(v) { - that.properties.value = v; - }, - this.properties - ); + + // Object.defineProperty(this.properties, "name", { + // get: function() { + // return that.name_in_graph; + // }, + // set: function(v) { + // if (v == "" || v == that.name_in_graph) { + // return; + // } + // if (that.name_in_graph) { + // //already added + // that.graph.renameOutput(that.name_in_graph, v); + // } else { + // that.graph.addOutput(v, that.properties.type); + // } + // that.name_widget.value = v; + // that.name_in_graph = v; + // }, + // enumerable: true + // }); + + // Object.defineProperty(this.properties, "type", { + // get: function() { + // return that.inputs[0].type; + // }, + // set: function(v) { + // if (v == "action" || v == "event") { + // v = LiteGraph.ACTION; + // } + // if (!LiteGraph.isValidConnection(that.inputs[0].type,v)) + // that.disconnectInput(0); + // that.inputs[0].type = v; + // if (that.name_in_graph) { + // //already added + // that.graph.changeOutputType( + // that.name_in_graph, + // that.inputs[0].type + // ); + // } + // that.type_widget.value = v || ""; + // }, + // enumerable: true + // }); + + this.name_widget = this.addWidget("text","Name",this.properties.name,"name"); + this.type_widget = this.addWidget("text","Type",this.properties.type,"type"); this.widgets_up = true; + this.size = [180, 60]; } - WidgetSliderGUI.title = "Inner Slider"; + GraphOutput.title = "Output"; + GraphOutput.desc = "Output of the graph"; - WidgetSliderGUI.prototype.onPropertyChanged = function(name, value) { - if (name == "value") { - this.slider.value = value; + GraphOutput.prototype.onPropertyChanged = function (name, v) { + if (name == "name") { + if (v == "" || v == this.name_in_graph || v == "enabled") { + return false; + } + if (this.graph) { + if (this.name_in_graph) { + // already added + this.graph.renameOutput(this.name_in_graph, v); + } else { + this.graph.addOutput(v, this.properties.type); + } + } // what if not?! + this.name_widget.value = v; + this.name_in_graph = v; + } else if (name == "type") { + this.updateType(); } - }; - - WidgetSliderGUI.prototype.onExecute = function() { - this.setOutputData(0, this.properties.value); - }; + } - LiteGraph.registerNodeType("widget/internal_slider", WidgetSliderGUI); + GraphOutput.prototype.updateType = function () { + var type = this.properties.type; + if (this.type_widget) + this.type_widget.value = type; - //Widget H SLIDER - function WidgetHSlider() { - this.size = [160, 26]; - this.addOutput("", "number"); - this.properties = { color: "#7AF", min: 0, max: 1, value: 0.5 }; - this.value = -1; - } + // update output + if (this.inputs[0].type != type) { - WidgetHSlider.title = "H.Slider"; - WidgetHSlider.desc = "Linear slider controller"; + if ( type == "action" || type == "event") + type = LiteGraph.EVENT; + if (!LiteGraph.isValidConnection(this.inputs[0].type, type)) + this.disconnectInput(0); + this.inputs[0].type = type; + } - WidgetHSlider.prototype.onDrawForeground = function(ctx) { - if (this.value == -1) { - this.value = - (this.properties.value - this.properties.min) / - (this.properties.max - this.properties.min); + // update graph + if (this.graph && this.name_in_graph) { + this.graph.changeOutputType(this.name_in_graph, type); } + } - //border - ctx.globalAlpha = 1; - ctx.lineWidth = 1; - ctx.fillStyle = "#000"; - ctx.fillRect(2, 2, this.size[0] - 4, this.size[1] - 4); - ctx.fillStyle = this.properties.color; - ctx.beginPath(); - ctx.rect(4, 4, (this.size[0] - 8) * this.value, this.size[1] - 8); - ctx.fill(); - }; - 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 = LiteGraph.colorToString([ - this.value, - this.value, - this.value - ]); + GraphOutput.prototype.onExecute = function() { + this._value = this.getInputData(0); + this.graph.setOutputData(this.properties.name, this._value); }; - WidgetHSlider.prototype.onMouseDown = function(e) { - if (e.canvasY - this.pos[1] < 0) { - return false; + GraphOutput.prototype.onAction = function(action, param) { + if (this.properties.type == LiteGraph.ACTION) { + this.graph.trigger( this.properties.name, param ); } + }; - this.oldmouse = [e.canvasX - this.pos[0], e.canvasY - this.pos[1]]; - this.captureInput(true); - return true; + GraphOutput.prototype.onRemoved = function() { + if (this.name_in_graph) { + this.graph.removeOutput(this.name_in_graph); + } }; - WidgetHSlider.prototype.onMouseMove = function(e) { - if (!this.oldmouse) { - return; + GraphOutput.prototype.getTitle = function() { + if (this.flags.collapsed) { + return this.properties.name; } + return this.title; + }; - var m = [e.canvasX - this.pos[0], e.canvasY - this.pos[1]]; + LiteGraph.GraphOutput = GraphOutput; + LiteGraph.registerNodeType("graph/output", GraphOutput); - 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; - } + // Constant + function ConstantNumber() { + this.addOutput("value", "number"); + this.addProperty("value", 1.0); + this.widget = this.addWidget("number","value",1,"value"); + this.widgets_up = true; + this.size = [180, 30]; + } - this.value = v; + ConstantNumber.title = "Const Number"; + ConstantNumber.desc = "Constant number"; - this.oldmouse = m; - this.setDirtyCanvas(true); + ConstantNumber.prototype.onExecute = function() { + this.setOutputData(0, parseFloat(this.properties["value"])); }; - WidgetHSlider.prototype.onMouseUp = function(e) { - this.oldmouse = null; - this.captureInput(false); + ConstantNumber.prototype.getTitle = function() { + if (this.flags.collapsed) { + return this.properties.value; + } + return this.title; }; - WidgetHSlider.prototype.onMouseLeave = function(e) { - //this.oldmouse = null; + ConstantNumber.prototype.setValue = function(v) { + this.setProperty("value",v); + } + + ConstantNumber.prototype.onDrawBackground = function(ctx) { + // show the current value + this.outputs[0].label = this.properties["value"].toFixed(3); }; - LiteGraph.registerNodeType("widget/hslider", WidgetHSlider); + LiteGraph.registerNodeType("basic/const", ConstantNumber); - function WidgetProgress() { - this.size = [160, 26]; - this.addInput("", "number"); - this.properties = { min: 0, max: 1, value: 0, color: "#AAF" }; + function ConstantBoolean() { + this.addOutput("bool", "boolean"); + this.addProperty("value", true); + this.widget = this.addWidget("toggle","value",true,"value"); + this.serialize_widgets = true; + this.widgets_up = true; + this.size = [140, 30]; } - WidgetProgress.title = "Progress"; - WidgetProgress.desc = "Shows data in linear progress"; + ConstantBoolean.title = "Const Boolean"; + ConstantBoolean.desc = "Constant boolean"; + ConstantBoolean.prototype.getTitle = ConstantNumber.prototype.getTitle; - WidgetProgress.prototype.onExecute = function() { - var v = this.getInputData(0); - if (v != undefined) { - this.properties["value"] = v; - } + ConstantBoolean.prototype.onExecute = function() { + this.setOutputData(0, this.properties["value"]); }; - WidgetProgress.prototype.onDrawForeground = function(ctx) { - //border - ctx.lineWidth = 1; - ctx.fillStyle = this.properties.color; - 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); + ConstantBoolean.prototype.setValue = ConstantNumber.prototype.setValue; + + ConstantBoolean.prototype.onGetInputs = function() { + return [["toggle", LiteGraph.ACTION]]; }; - LiteGraph.registerNodeType("widget/progress", WidgetProgress); + ConstantBoolean.prototype.onAction = function(action) { + this.setValue( !this.properties.value ); + } - function WidgetText() { - this.addInputs("", 0); - this.properties = { - value: "...", - font: "Arial", - fontsize: 18, - color: "#AAA", - align: "left", - glowSize: 0, - decimals: 1 - }; + LiteGraph.registerNodeType("basic/boolean", ConstantBoolean); + + function ConstantString() { + this.addOutput("string", "string"); + this.addProperty("value", ""); + this.widget = this.addWidget("text","value","","value"); // link to property value + this.widgets_up = true; + this.size = [180, 30]; } - 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" } - ]; + ConstantString.title = "Const String"; + ConstantString.desc = "Constant string"; - WidgetText.prototype.onDrawForeground = function(ctx) { - //ctx.fillStyle="#000"; - //ctx.fillRect(0,0,100,60); - ctx.fillStyle = this.properties["color"]; - var v = this.properties["value"]; + ConstantString.prototype.getTitle = ConstantNumber.prototype.getTitle; - if (this.properties["glowSize"]) { - ctx.shadowColor = this.properties.color; - ctx.shadowOffsetX = 0; - ctx.shadowOffsetY = 0; - ctx.shadowBlur = this.properties["glowSize"]; - } else { - ctx.shadowColor = "transparent"; + ConstantString.prototype.onExecute = function() { + this.setOutputData(0, this.properties["value"]); + }; + + ConstantString.prototype.setValue = ConstantNumber.prototype.setValue; + + ConstantString.prototype.onDropFile = function(file) { + var that = this; + var reader = new FileReader(); + reader.onload = function(e) { + that.setProperty("value",e.target.result); } + reader.readAsText(file); + } - var fontsize = this.properties["fontsize"]; + LiteGraph.registerNodeType("basic/string", ConstantString); - ctx.textAlign = this.properties["align"]; - ctx.font = fontsize.toString() + "px " + this.properties["font"]; - this.str = - typeof v == "number" ? v.toFixed(this.properties["decimals"]) : v; + function ConstantObject() { + this.addOutput("obj", "object"); + this.size = [120, 30]; + this._object = {}; + } - if (typeof this.str == "string") { - var lines = this.str.replace(/[\r\n]/g, "\\n").split("\\n"); - for (var i=0; i < lines.length; i++) { - ctx.fillText( - lines[i], - this.properties["align"] == "left" ? 15 : this.size[0] - 15, - fontsize * -0.15 + fontsize * (parseInt(i) + 1) - ); - } - } + ConstantObject.title = "Const Object"; + ConstantObject.desc = "Constant Object"; - ctx.shadowColor = "transparent"; - this.last_ctx = ctx; - ctx.textAlign = "left"; + ConstantObject.prototype.onExecute = function() { + this.setOutputData(0, this._object); }; - WidgetText.prototype.onExecute = function() { - var v = this.getInputData(0); - if (v != null) { - this.properties["value"] = v; + LiteGraph.registerNodeType( "basic/object", ConstantObject ); + + function ConstantFile() { + this.addInput("url", "string"); + this.addOutput("file", "string"); + this.addProperty("url", ""); + this.addProperty("type", "text"); + this.widget = this.addWidget("text","url","","url"); + this._data = null; + } + + ConstantFile.title = "Const File"; + ConstantFile.desc = "Fetches a file from an url"; + ConstantFile["@type"] = { type: "enum", values: ["text","arraybuffer","blob","json"] }; + + ConstantFile.prototype.onPropertyChanged = function(name, value) { + if (name == "url") { + if( value == null || value == "") + this._data = null; + else { + this.fetchFile(value); + } } - //this.setDirtyCanvas(true); + } + + ConstantFile.prototype.onExecute = function() { + var url = this.getInputData(0) || this.properties.url; + if(url && (url != this._url || this._type != this.properties.type)) + this.fetchFile(url); + this.setOutputData(0, this._data ); }; - WidgetText.prototype.resize = function() { - if (!this.last_ctx) { + ConstantFile.prototype.setValue = ConstantNumber.prototype.setValue; + + ConstantFile.prototype.fetchFile = function(url) { + var that = this; + if(!url || url.constructor !== String) { + that._data = null; + that.boxcolor = null; return; } - var lines = this.str.split("\\n"); - this.last_ctx.font = - this.properties["fontsize"] + "px " + this.properties["font"]; - var max = 0; - for (var i=0; i < lines.length; i++) { - var w = this.last_ctx.measureText(lines[i]).width; - if (max < w) { - max = w; - } + this._url = url; + this._type = this.properties.type; + if (url.substr(0, 4) == "http" && LiteGraph.proxy) { + url = LiteGraph.proxy + url.substr(url.indexOf(":") + 3); } - this.size[0] = max + 20; - this.size[1] = 4 + lines.length * this.properties["fontsize"]; - - this.setDirtyCanvas(true); + fetch(url) + .then(function(response) { + if(!response.ok) + throw new Error("File not found"); + + if(that.properties.type == "arraybuffer") + return response.arrayBuffer(); + else if(that.properties.type == "text") + return response.text(); + else if(that.properties.type == "json") + return response.json(); + else if(that.properties.type == "blob") + return response.blob(); + }) + .then(function(data) { + that._data = data; + that.boxcolor = "#AEA"; + }) + .catch(function(error) { + that._data = null; + that.boxcolor = "red"; + console.error("error fetching file:",url); + }); }; - WidgetText.prototype.onPropertyChanged = function(name, value) { - this.properties[name] = value; - this.str = typeof value == "number" ? value.toFixed(3) : value; - //this.resize(); - return true; - }; + ConstantFile.prototype.onDropFile = function(file) { + var that = this; + this._url = file.name; + this._type = this.properties.type; + this.properties.url = file.name; + var reader = new FileReader(); + reader.onload = function(e) { + that.boxcolor = "#AEA"; + var v = e.target.result; + if( that.properties.type == "json" ) + v = JSON.parse(v); + that._data = v; + } + if(that.properties.type == "arraybuffer") + reader.readAsArrayBuffer(file); + else if(that.properties.type == "text" || that.properties.type == "json") + reader.readAsText(file); + else if(that.properties.type == "blob") + return reader.readAsBinaryString(file); + } - LiteGraph.registerNodeType("widget/text", WidgetText); + LiteGraph.registerNodeType("basic/file", ConstantFile); - function WidgetPanel() { - this.size = [200, 100]; - this.properties = { - borderColor: "#ffffff", - bgcolorTop: "#f0f0f0", - bgcolorBottom: "#e0e0e0", - shadowSize: 2, - borderRadius: 3 - }; + + // to store json objects + function JSONParse() { + this.addInput("parse", LiteGraph.ACTION); + this.addInput("json", "string"); + this.addOutput("done", LiteGraph.EVENT); + this.addOutput("object", "object"); + this.widget = this.addWidget("button","parse","",this.parse.bind(this)); + this._str = null; + this._obj = null; } - WidgetPanel.title = "Panel"; - WidgetPanel.desc = "Non interactive panel"; - WidgetPanel.widgets = [{ name: "update", text: "Update", type: "button" }]; + JSONParse.title = "JSON Parse"; + JSONParse.desc = "Parses JSON String into object"; - WidgetPanel.prototype.createGradient = function(ctx) { - if ( - this.properties["bgcolorTop"] == "" || - this.properties["bgcolorBottom"] == "" - ) { - this.lineargradient = 0; + JSONParse.prototype.parse = function() { + if(!this._str) return; + + try { + this._str = this.getInputData(1); + this._obj = JSON.parse(this._str); + this.boxcolor = "#AEA"; + this.triggerSlot(0); + } catch (err) { + this.boxcolor = "red"; } + } - this.lineargradient = ctx.createLinearGradient(0, 0, 0, this.size[1]); - this.lineargradient.addColorStop(0, this.properties["bgcolorTop"]); - this.lineargradient.addColorStop(1, this.properties["bgcolorBottom"]); + JSONParse.prototype.onExecute = function() { + this._str = this.getInputData(1); + this.setOutputData(1, this._obj); }; - WidgetPanel.prototype.onDrawForeground = function(ctx) { - if (this.flags.collapsed) { - return; - } + JSONParse.prototype.onAction = function(name) { + if(name == "parse") + this.parse(); + } - if (this.lineargradient == null) { - this.createGradient(ctx); - } + LiteGraph.registerNodeType("basic/jsonparse", JSONParse); - if (!this.lineargradient) { - return; - } + // to store json objects + function ConstantData() { + this.addOutput("data", "object"); + this.addProperty("value", ""); + this.widget = this.addWidget("text","json","","value"); + this.widgets_up = true; + this.size = [140, 30]; + this._value = null; + } - ctx.lineWidth = 1; - ctx.strokeStyle = this.properties["borderColor"]; - //ctx.fillStyle = "#ebebeb"; - ctx.fillStyle = this.lineargradient; + ConstantData.title = "Const Data"; + ConstantData.desc = "Constant Data"; - if (this.properties["shadowSize"]) { - ctx.shadowColor = "#000"; - ctx.shadowOffsetX = 0; - ctx.shadowOffsetY = 0; - ctx.shadowBlur = this.properties["shadowSize"]; - } else { - ctx.shadowColor = "transparent"; + ConstantData.prototype.onPropertyChanged = function(name, value) { + this.widget.value = value; + if (value == null || value == "") { + return; } - ctx.roundRect( - 0, - 0, - this.size[0] - 1, - this.size[1] - 1, - this.properties["shadowSize"] - ); - ctx.fill(); - ctx.shadowColor = "transparent"; - ctx.stroke(); + try { + this._value = JSON.parse(value); + this.boxcolor = "#AEA"; + } catch (err) { + this.boxcolor = "red"; + } }; - LiteGraph.registerNodeType("widget/panel", WidgetPanel); -})(this); + ConstantData.prototype.onExecute = function() { + this.setOutputData(0, this._value); + }; -(function(global) { - var LiteGraph = global.LiteGraph; - - function GamepadInput() { - this.addOutput("left_x_axis", "number"); - this.addOutput("left_y_axis", "number"); - this.addOutput("button_pressed", LiteGraph.EVENT); - this.properties = { gamepad_index: 0, threshold: 0.1 }; - - this._left_axis = new Float32Array(2); - this._right_axis = new Float32Array(2); - this._triggers = new Float32Array(2); - this._previous_buttons = new Uint8Array(17); - this._current_buttons = new Uint8Array(17); - } - - GamepadInput.title = "Gamepad"; - GamepadInput.desc = "gets the input of the gamepad"; - - GamepadInput.CENTER = 0; - GamepadInput.LEFT = 1; - GamepadInput.RIGHT = 2; - GamepadInput.UP = 4; - GamepadInput.DOWN = 8; - - GamepadInput.zero = new Float32Array(2); - GamepadInput.buttons = [ - "a", - "b", - "x", - "y", - "lb", - "rb", - "lt", - "rt", - "back", - "start", - "ls", - "rs", - "home" - ]; - - GamepadInput.prototype.onExecute = function() { - //get gamepad - var gamepad = this.getGamepad(); - var threshold = this.properties.threshold || 0.0; - - if (gamepad) { - this._left_axis[0] = - Math.abs(gamepad.xbox.axes["lx"]) > threshold - ? gamepad.xbox.axes["lx"] - : 0; - this._left_axis[1] = - Math.abs(gamepad.xbox.axes["ly"]) > threshold - ? gamepad.xbox.axes["ly"] - : 0; - this._right_axis[0] = - Math.abs(gamepad.xbox.axes["rx"]) > threshold - ? gamepad.xbox.axes["rx"] - : 0; - this._right_axis[1] = - Math.abs(gamepad.xbox.axes["ry"]) > threshold - ? gamepad.xbox.axes["ry"] - : 0; - this._triggers[0] = - Math.abs(gamepad.xbox.axes["ltrigger"]) > threshold - ? gamepad.xbox.axes["ltrigger"] - : 0; - this._triggers[1] = - Math.abs(gamepad.xbox.axes["rtrigger"]) > threshold - ? gamepad.xbox.axes["rtrigger"] - : 0; - } - - if (this.outputs) { - for (var i = 0; i < this.outputs.length; i++) { - var output = this.outputs[i]; - if (!output.links || !output.links.length) { - continue; - } - var v = null; - - if (gamepad) { - switch (output.name) { - case "left_axis": - v = this._left_axis; - break; - case "right_axis": - v = this._right_axis; - break; - case "left_x_axis": - v = this._left_axis[0]; - break; - case "left_y_axis": - v = this._left_axis[1]; - break; - case "right_x_axis": - v = this._right_axis[0]; - break; - case "right_y_axis": - v = this._right_axis[1]; - break; - case "trigger_left": - v = this._triggers[0]; - break; - case "trigger_right": - v = this._triggers[1]; - break; - case "a_button": - v = gamepad.xbox.buttons["a"] ? 1 : 0; - break; - case "b_button": - v = gamepad.xbox.buttons["b"] ? 1 : 0; - break; - case "x_button": - v = gamepad.xbox.buttons["x"] ? 1 : 0; - break; - case "y_button": - v = gamepad.xbox.buttons["y"] ? 1 : 0; - break; - case "lb_button": - v = gamepad.xbox.buttons["lb"] ? 1 : 0; - break; - case "rb_button": - v = gamepad.xbox.buttons["rb"] ? 1 : 0; - break; - case "ls_button": - v = gamepad.xbox.buttons["ls"] ? 1 : 0; - break; - case "rs_button": - v = gamepad.xbox.buttons["rs"] ? 1 : 0; - break; - case "hat_left": - v = gamepad.xbox.hatmap & GamepadInput.LEFT; - break; - case "hat_right": - v = gamepad.xbox.hatmap & GamepadInput.RIGHT; - break; - case "hat_up": - v = gamepad.xbox.hatmap & GamepadInput.UP; - break; - case "hat_down": - v = gamepad.xbox.hatmap & GamepadInput.DOWN; - break; - case "hat": - v = gamepad.xbox.hatmap; - break; - case "start_button": - v = gamepad.xbox.buttons["start"] ? 1 : 0; - break; - case "back_button": - v = gamepad.xbox.buttons["back"] ? 1 : 0; - break; - case "button_pressed": - for ( - var j = 0; - j < this._current_buttons.length; - ++j - ) { - if ( - this._current_buttons[j] && - !this._previous_buttons[j] - ) { - this.triggerSlot( - i, - GamepadInput.buttons[j] - ); - } - } - break; - default: - break; - } - } else { - //if no gamepad is connected, output 0 - switch (output.name) { - case "button_pressed": - break; - case "left_axis": - case "right_axis": - v = GamepadInput.zero; - break; - default: - v = 0; - } - } - this.setOutputData(i, v); - } - } - }; - - GamepadInput.mapping = {a:0,b:1,x:2,y:3,lb:4,rb:5,lt:6,rt:7,back:8,start:9,ls:10,rs:11 }; - GamepadInput.mapping_array = ["a","b","x","y","lb","rb","lt","rt","back","start","ls","rs"]; - - GamepadInput.prototype.getGamepad = function() { - var getGamepads = - navigator.getGamepads || - navigator.webkitGetGamepads || - navigator.mozGetGamepads; - if (!getGamepads) { - return null; - } - var gamepads = getGamepads.call(navigator); - var gamepad = null; - - this._previous_buttons.set(this._current_buttons); - - //pick the first connected - for (var i = this.properties.gamepad_index; i < 4; i++) { - if (!gamepads[i]) { - continue; - } - gamepad = gamepads[i]; - - //xbox controller mapping - var xbox = this.xbox_mapping; - if (!xbox) { - xbox = this.xbox_mapping = { - axes: [], - buttons: {}, - hat: "", - hatmap: GamepadInput.CENTER - }; - } - - xbox.axes["lx"] = gamepad.axes[0]; - xbox.axes["ly"] = gamepad.axes[1]; - xbox.axes["rx"] = gamepad.axes[2]; - xbox.axes["ry"] = gamepad.axes[3]; - xbox.axes["ltrigger"] = gamepad.buttons[6].value; - xbox.axes["rtrigger"] = gamepad.buttons[7].value; - xbox.hat = ""; - xbox.hatmap = GamepadInput.CENTER; - - for (var j = 0; j < gamepad.buttons.length; j++) { - this._current_buttons[j] = gamepad.buttons[j].pressed; - - if(j < 12) - { - xbox.buttons[ GamepadInput.mapping_array[j] ] = gamepad.buttons[j].pressed; - if(gamepad.buttons[j].was_pressed) - this.trigger( GamepadInput.mapping_array[j] + "_button_event" ); - } - else //mapping of XBOX - switch ( j ) //I use a switch to ensure that a player with another gamepad could play - { - case 12: - if (gamepad.buttons[j].pressed) { - xbox.hat += "up"; - xbox.hatmap |= GamepadInput.UP; - } - break; - case 13: - if (gamepad.buttons[j].pressed) { - xbox.hat += "down"; - xbox.hatmap |= GamepadInput.DOWN; - } - break; - case 14: - if (gamepad.buttons[j].pressed) { - xbox.hat += "left"; - xbox.hatmap |= GamepadInput.LEFT; - } - break; - case 15: - if (gamepad.buttons[j].pressed) { - xbox.hat += "right"; - xbox.hatmap |= GamepadInput.RIGHT; - } - break; - case 16: - xbox.buttons["home"] = gamepad.buttons[j].pressed; - break; - default: - } - } - gamepad.xbox = xbox; - return gamepad; - } - }; - - GamepadInput.prototype.onDrawBackground = function(ctx) { - if (this.flags.collapsed) { - return; - } - - //render gamepad state? - var la = this._left_axis; - var ra = this._right_axis; - ctx.strokeStyle = "#88A"; - ctx.strokeRect( - (la[0] + 1) * 0.5 * this.size[0] - 4, - (la[1] + 1) * 0.5 * this.size[1] - 4, - 8, - 8 - ); - ctx.strokeStyle = "#8A8"; - ctx.strokeRect( - (ra[0] + 1) * 0.5 * this.size[0] - 4, - (ra[1] + 1) * 0.5 * this.size[1] - 4, - 8, - 8 - ); - var h = this.size[1] / this._current_buttons.length; - ctx.fillStyle = "#AEB"; - for (var i = 0; i < this._current_buttons.length; ++i) { - if (this._current_buttons[i]) { - ctx.fillRect(0, h * i, 6, h); - } - } - }; - - GamepadInput.prototype.onGetOutputs = function() { - return [ - ["left_axis", "vec2"], - ["right_axis", "vec2"], - ["left_x_axis", "number"], - ["left_y_axis", "number"], - ["right_x_axis", "number"], - ["right_y_axis", "number"], - ["trigger_left", "number"], - ["trigger_right", "number"], - ["a_button", "number"], - ["b_button", "number"], - ["x_button", "number"], - ["y_button", "number"], - ["lb_button", "number"], - ["rb_button", "number"], - ["ls_button", "number"], - ["rs_button", "number"], - ["start_button", "number"], - ["back_button", "number"], - ["a_button_event", LiteGraph.EVENT ], - ["b_button_event", LiteGraph.EVENT ], - ["x_button_event", LiteGraph.EVENT ], - ["y_button_event", LiteGraph.EVENT ], - ["lb_button_event", LiteGraph.EVENT ], - ["rb_button_event", LiteGraph.EVENT ], - ["ls_button_event", LiteGraph.EVENT ], - ["rs_button_event", LiteGraph.EVENT ], - ["start_button_event", LiteGraph.EVENT ], - ["back_button_event", LiteGraph.EVENT ], - ["hat_left", "number"], - ["hat_right", "number"], - ["hat_up", "number"], - ["hat_down", "number"], - ["hat", "number"], - ["button_pressed", LiteGraph.EVENT] - ]; - }; - - LiteGraph.registerNodeType("input/gamepad", GamepadInput); - -})(this); + ConstantData.prototype.setValue = ConstantNumber.prototype.setValue; -(function(global) { - var LiteGraph = global.LiteGraph; + LiteGraph.registerNodeType("basic/data", ConstantData); - //Converter - function Converter() { - this.addInput("in", 0); - this.addOutput("out", 0); - this.size = [80, 30]; + // to store json objects + function ConstantArray() { + this._value = []; + this.addInput("json", ""); + this.addOutput("arrayOut", "array"); + this.addOutput("length", "number"); + this.addProperty("value", "[]"); + this.widget = this.addWidget("text","array",this.properties.value,"value"); + this.widgets_up = true; + this.size = [140, 50]; } - Converter.title = "Converter"; - Converter.desc = "type A to type B"; + ConstantArray.title = "Const Array"; + ConstantArray.desc = "Constant Array"; - Converter.prototype.onExecute = function() { - var v = this.getInputData(0); - if (v == null) { + ConstantArray.prototype.onPropertyChanged = function(name, value) { + this.widget.value = value; + if (value == null || value == "") { return; } - if (this.outputs) { - for (var i = 0; i < this.outputs.length; i++) { - var output = this.outputs[i]; - if (!output.links || !output.links.length) { - continue; - } - - var result = null; - switch (output.name) { - case "number": - result = v.length ? v[0] : parseFloat(v); - break; - case "vec2": - case "vec3": - case "vec4": - var result = null; - var count = 1; - switch (output.name) { - case "vec2": - count = 2; - break; - case "vec3": - count = 3; - break; - case "vec4": - count = 4; - break; - } - - var result = new Float32Array(count); - if (v.length) { - for ( - var j = 0; - j < v.length && j < result.length; - j++ - ) { - result[j] = v[j]; - } - } else { - result[0] = parseFloat(v); - } - break; - } - this.setOutputData(i, result); - } + try { + if(value[0] != "[") + this._value = JSON.parse("[" + value + "]"); + else + this._value = JSON.parse(value); + this.boxcolor = "#AEA"; + } catch (err) { + this.boxcolor = "red"; } }; - Converter.prototype.onGetOutputs = function() { - return [ - ["number", "number"], - ["vec2", "vec2"], - ["vec3", "vec3"], - ["vec4", "vec4"] - ]; + ConstantArray.prototype.onExecute = function() { + var v = this.getInputData(0); + if(v && v.length) { // clone + if(!this._value) + this._value = new Array(); + this._value.length = v.length; + for(var i = 0; i < v.length; ++i) + this._value[i] = v[i]; + } + this.setOutputData(0, this._value); + this.setOutputData(1, this._value ? ( this._value.length || 0) : 0 ); }; - LiteGraph.registerNodeType("math/converter", Converter); + ConstantArray.prototype.setValue = ConstantNumber.prototype.setValue; - //Bypass - function Bypass() { - this.addInput("in"); - this.addOutput("out"); - this.size = [80, 30]; + LiteGraph.registerNodeType("basic/array", ConstantArray); + + function SetArray() { + this.addInput("arr", "array"); + this.addInput("value", ""); + this.addOutput("arr", "array"); + this.properties = { index: 0 }; + this.widget = this.addWidget("number","i",this.properties.index,"index",{precision: 0, step: 10, min: 0}); } - Bypass.title = "Bypass"; - Bypass.desc = "removes the type"; + SetArray.title = "Set Array"; + SetArray.desc = "Sets index of array"; - Bypass.prototype.onExecute = function() { - var v = this.getInputData(0); - this.setOutputData(0, v); + SetArray.prototype.onExecute = function() { + var arr = this.getInputData(0); + if(!arr) + return; + var v = this.getInputData(1); + if(v === undefined ) + return; + if(this.properties.index) + arr[Math.floor(this.properties.index)] = v; + this.setOutputData(0,arr); }; - LiteGraph.registerNodeType("math/bypass", Bypass); + LiteGraph.registerNodeType("basic/set_array", SetArray ); - function ToNumber() { - this.addInput("in"); - this.addOutput("out"); + function ArrayElement() { + this.addInput("array", "array,table,string"); + this.addInput("index", "number"); + this.addOutput("value", ""); + this.addProperty("index",0); } - ToNumber.title = "to Number"; - ToNumber.desc = "Cast to number"; + ArrayElement.title = "Array[i]"; + ArrayElement.desc = "Returns an element from an array"; - ToNumber.prototype.onExecute = function() { - var v = this.getInputData(0); - this.setOutputData(0, Number(v)); + ArrayElement.prototype.onExecute = function() { + var array = this.getInputData(0); + var index = this.getInputData(1); + if(index == null) + index = this.properties.index; + if(array == null || index == null ) + return; + this.setOutputData(0, array[Math.floor(Number(index))] ); }; - LiteGraph.registerNodeType("math/to_number", ToNumber); + LiteGraph.registerNodeType("basic/array[]", ArrayElement); - function MathRange() { - this.addInput("in", "number", { locked: true }); - this.addOutput("out", "number", { locked: true }); - this.addOutput("clamped", "number", { locked: true }); + function TableElement() { + this.addInput("table", "table"); + this.addInput("row", "number"); + this.addInput("col", "number"); + this.addOutput("value", ""); + this.addProperty("row",0); + this.addProperty("column",0); + } - this.addProperty("in", 0); - this.addProperty("in_min", 0); - this.addProperty("in_max", 1); - this.addProperty("out_min", 0); - this.addProperty("out_max", 1); + TableElement.title = "Table[row][col]"; + TableElement.desc = "Returns an element from a table"; + + TableElement.prototype.onExecute = function() { + var table = this.getInputData(0); + var row = this.getInputData(1); + var col = this.getInputData(2); + if(row == null) + row = this.properties.row; + if(col == null) + col = this.properties.column; + if(table == null || row == null || col == null) + return; + var row = table[Math.floor(Number(row))]; + if(row) + this.setOutputData(0, row[Math.floor(Number(col))] ); + else + this.setOutputData(0, null ); + }; - this.size = [120, 50]; + LiteGraph.registerNodeType("basic/table[][]", TableElement); + + function ObjectProperty() { + this.addInput("obj", "object"); + this.addOutput("property", 0); + this.addProperty("value", 0); + this.widget = this.addWidget("text","prop.","",this.setValue.bind(this) ); + this.widgets_up = true; + this.size = [140, 30]; + this._value = null; } - MathRange.title = "Range"; - MathRange.desc = "Convert a number from one range to another"; + ObjectProperty.title = "Object property"; + ObjectProperty.desc = "Outputs the property of an object"; - MathRange.prototype.getTitle = function() { + ObjectProperty.prototype.setValue = function(v) { + this.properties.value = v; + this.widget.value = v; + }; + + ObjectProperty.prototype.getTitle = function() { if (this.flags.collapsed) { - return (this._last_v || 0).toFixed(2); + return "in." + this.properties.value; } return this.title; }; - MathRange.prototype.onExecute = function() { - if (this.inputs) { - for (var i = 0; i < this.inputs.length; i++) { - var input = this.inputs[i]; - var v = this.getInputData(i); - if (v === undefined) { - continue; - } - this.properties[input.name] = v; - } - } + ObjectProperty.prototype.onPropertyChanged = function(name, value) { + this.widget.value = value; + }; - var v = this.properties["in"]; - if (v === undefined || v === null || v.constructor !== Number) { - v = 0; + ObjectProperty.prototype.onExecute = function() { + var data = this.getInputData(0); + if (data != null) { + this.setOutputData(0, data[this.properties.value]); } + }; - var in_min = this.properties.in_min; - var in_max = this.properties.in_max; - var out_min = this.properties.out_min; - var out_max = this.properties.out_max; - /* - if( in_min > in_max ) - { - in_min = in_max; - in_max = this.properties.in_min; - } - if( out_min > out_max ) - { - out_min = out_max; - out_max = this.properties.out_min; - } - */ + LiteGraph.registerNodeType("basic/object_property", ObjectProperty); - this._last_v = ((v - in_min) / (in_max - in_min)) * (out_max - out_min) + out_min; - this.setOutputData(0, this._last_v); - this.setOutputData(1, clamp( this._last_v, out_min, out_max )); - }; + function ObjectKeys() { + this.addInput("obj", ""); + this.addOutput("keys", "array"); + this.size = [140, 30]; + } - MathRange.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 = "?"; + ObjectKeys.title = "Object keys"; + ObjectKeys.desc = "Outputs an array with the keys of an object"; + + ObjectKeys.prototype.onExecute = function() { + var data = this.getInputData(0); + if (data != null) { + this.setOutputData(0, Object.keys(data) ); } }; - MathRange.prototype.onGetInputs = function() { - return [ - ["in_min", "number"], - ["in_max", "number"], - ["out_min", "number"], - ["out_max", "number"] - ]; - }; + LiteGraph.registerNodeType("basic/object_keys", ObjectKeys); - LiteGraph.registerNodeType("math/range", MathRange); - function MathRand() { - this.addOutput("value", "number"); - this.addProperty("min", 0); - this.addProperty("max", 1); - this.size = [80, 30]; + function SetObject() { + this.addInput("obj", ""); + this.addInput("value", ""); + this.addOutput("obj", ""); + this.properties = { property: "" }; + this.name_widget = this.addWidget("text","prop.",this.properties.property,"property"); } - MathRand.title = "Rand"; - MathRand.desc = "Random number"; - - MathRand.prototype.onExecute = function() { - if (this.inputs) { - for (var i = 0; i < this.inputs.length; i++) { - var input = this.inputs[i]; - var v = this.getInputData(i); - if (v === undefined) { - continue; - } - this.properties[input.name] = v; - } - } + SetObject.title = "Set Object"; + SetObject.desc = "Adds propertiesrty to object"; - var min = this.properties.min; - var max = this.properties.max; - this._last_v = Math.random() * (max - min) + min; - this.setOutputData(0, this._last_v); + SetObject.prototype.onExecute = function() { + var obj = this.getInputData(0); + if(!obj) + return; + var v = this.getInputData(1); + if(v === undefined ) + return; + if(this.properties.property) + obj[this.properties.property] = v; + this.setOutputData(0,obj); }; - MathRand.prototype.onDrawBackground = function(ctx) { - //show the current value - this.outputs[0].label = (this._last_v || 0).toFixed(3); - }; + LiteGraph.registerNodeType("basic/set_object", SetObject ); - MathRand.prototype.onGetInputs = function() { - return [["min", "number"], ["max", "number"]]; + + function MergeObjects() { + this.addInput("A", "object"); + this.addInput("B", "object"); + this.addOutput("out", "object"); + this._result = {}; + var that = this; + this.addWidget("button","clear","",function() { + that._result = {}; + }); + this.size = this.computeSize(); + } + + MergeObjects.title = "Merge Objects"; + MergeObjects.desc = "Creates an object copying properties from others"; + + MergeObjects.prototype.onExecute = function() { + var A = this.getInputData(0); + var B = this.getInputData(1); + var C = this._result; + if(A) + for(var i in A) + C[i] = A[i]; + if(B) + for(var i in B) + C[i] = B[i]; + this.setOutputData(0,C); }; - LiteGraph.registerNodeType("math/rand", MathRand); + LiteGraph.registerNodeType("basic/merge_objects", MergeObjects ); - //basic continuous noise - function MathNoise() { - this.addInput("in", "number"); - this.addOutput("out", "number"); - this.addProperty("min", 0); - this.addProperty("max", 1); - this.addProperty("smooth", true); - this.addProperty("seed", 0); - this.addProperty("octaves", 1); - this.addProperty("persistence", 0.8); - this.addProperty("speed", 1); - this.size = [90, 30]; + // Store as variable + function Variable() { + this.size = [60, 30]; + this.addInput("in"); + this.addOutput("out"); + this.properties = { varname: "myname", container: Variable.LITEGRAPH }; + this.value = null; } - MathNoise.title = "Noise"; - MathNoise.desc = "Random number with temporal continuity"; - MathNoise.data = null; + Variable.title = "Variable"; + Variable.desc = "store/read variable value"; - MathNoise.getValue = function(f, smooth) { - if (!MathNoise.data) { - MathNoise.data = new Float32Array(1024); - for (var i = 0; i < MathNoise.data.length; ++i) { - MathNoise.data[i] = Math.random(); - } - } - f = f % 1024; - if (f < 0) { - f += 1024; - } - var f_min = Math.floor(f); - var f = f - f_min; - var r1 = MathNoise.data[f_min]; - var r2 = MathNoise.data[f_min == 1023 ? 0 : f_min + 1]; - if (smooth) { - f = f * f * f * (f * (f * 6.0 - 15.0) + 10.0); + Variable.LITEGRAPH = 0; // between all graphs + Variable.GRAPH = 1; // only inside this graph + Variable.GLOBALSCOPE = 2; // attached to Window + + Variable["@container"] = { type: "enum", values: {"litegraph": Variable.LITEGRAPH, "graph": Variable.GRAPH,"global": Variable.GLOBALSCOPE} }; + + Variable.prototype.onExecute = function() { + var container = this.getContainer(); + + if(this.isInputConnected(0)) { + this.value = this.getInputData(0); + container[this.properties.varname] = this.value; + this.setOutputData(0, this.value ); + return; } - return r1 * (1 - f) + r2 * f; - }; - MathNoise.prototype.onExecute = function() { - var f = this.getInputData(0) || 0; - var iterations = this.properties.octaves || 1; - var r = 0; - var amp = 1; - var seed = this.properties.seed || 0; - f += seed; - var speed = this.properties.speed || 1; - var total_amp = 0; - for(var i = 0; i < iterations; ++i) - { - r += MathNoise.getValue(f * (1+i) * speed, this.properties.smooth) * amp; - total_amp += amp; - amp *= this.properties.persistence; - if(amp < 0.001) - break; - } - r /= total_amp; - var min = this.properties.min; - var max = this.properties.max; - this._last_v = r * (max - min) + min; - this.setOutputData(0, this._last_v); + this.setOutputData( 0, container[this.properties.varname] ); }; - MathNoise.prototype.onDrawBackground = function(ctx) { - //show the current value - this.outputs[0].label = (this._last_v || 0).toFixed(3); + Variable.prototype.getContainer = function() { + switch(this.properties.container) { + case Variable.GRAPH: + if(this.graph) + return this.graph.vars; + return {}; + case Variable.GLOBALSCOPE: + return global; + case Variable.LITEGRAPH: + default: + return LiteGraph.Globals; + } + } + + Variable.prototype.getTitle = function() { + return this.properties.varname; }; - LiteGraph.registerNodeType("math/noise", MathNoise); + LiteGraph.registerNodeType("basic/variable", Variable); - //generates spikes every random time - function MathSpikes() { - this.addOutput("out", "number"); - this.addProperty("min_time", 1); - this.addProperty("max_time", 2); - this.addProperty("duration", 0.2); - this.size = [90, 30]; - this._remaining_time = 0; - this._blink_time = 0; + function length(v) { + if(v && v.length != null) + return Number(v.length); + return 0; } - MathSpikes.title = "Spikes"; - MathSpikes.desc = "spike every random time"; + LiteGraph.wrapFunctionAsNode( + "basic/length", + length, + [""], + "number", + ); + + LiteGraph.wrapFunctionAsNode( + "basic/not", + function(a) { + return !a; + }, + [""], + "boolean", + ); + + function DownloadData() { + this.size = [60, 30]; + this.addInput("data", 0 ); + this.addInput("download", LiteGraph.ACTION ); + this.properties = { filename: "data.json" }; + this.value = null; + var that = this; + this.addWidget("button","Download","", function(v) { + if(!that.value) + return; + that.downloadAsFile(); + }); + } - MathSpikes.prototype.onExecute = function() { - var dt = this.graph.elapsed_time; //in secs + DownloadData.title = "Download"; + DownloadData.desc = "Download some data"; - this._remaining_time -= dt; - this._blink_time -= dt; + DownloadData.prototype.downloadAsFile = function() { + if(this.value == null) + return; - var v = 0; - if (this._blink_time > 0) { - var f = this._blink_time / this.properties.duration; - v = 1 / (Math.pow(f * 8 - 4, 4) + 1); + var str = null; + if(this.value.constructor === String) + str = this.value; + else + str = JSON.stringify(this.value); + + var file = new Blob([str]); + var url = URL.createObjectURL( file ); + var element = document.createElement("a"); + element.setAttribute('href', url); + element.setAttribute('download', this.properties.filename ); + element.style.display = 'none'; + document.body.appendChild(element); + element.click(); + document.body.removeChild(element); + setTimeout( function() { + URL.revokeObjectURL( url ); + }, 1000*60 ); // wait one minute to revoke url + } + + DownloadData.prototype.onAction = function(action, param) { + var that = this; + setTimeout( function() { + that.downloadAsFile(); + }, 100); // deferred to avoid blocking the renderer with the popup + } + + DownloadData.prototype.onExecute = function() { + if (this.inputs[0]) { + this.value = this.getInputData(0); } + }; - if (this._remaining_time < 0) { - this._remaining_time = - Math.random() * - (this.properties.max_time - this.properties.min_time) + - this.properties.min_time; - this._blink_time = this.properties.duration; - this.boxcolor = "#FFF"; - } else { - this.boxcolor = "#000"; + DownloadData.prototype.getTitle = function() { + if (this.flags.collapsed) { + return this.properties.filename; } - this.setOutputData(0, v); + return this.title; }; - LiteGraph.registerNodeType("math/spikes", MathSpikes); + LiteGraph.registerNodeType("basic/download", DownloadData); - //Math clamp - function MathClamp() { - this.addInput("in", "number"); - this.addOutput("out", "number"); - this.size = [80, 30]; - this.addProperty("min", 0); - this.addProperty("max", 1); + + + // Watch a value in the editor + function Watch() { + this.size = [60, 30]; + this.addInput("value", 0, { label: "" }); + this.value = 0; } - MathClamp.title = "Clamp"; - MathClamp.desc = "Clamp number between min and max"; - //MathClamp.filter = "shader"; + Watch.title = "Watch"; + Watch.desc = "Show value of input"; - MathClamp.prototype.onExecute = function() { - var v = this.getInputData(0); - if (v == null) { - return; + Watch.prototype.onExecute = function() { + if (this.inputs[0]) { + this.value = this.getInputData(0); } - v = Math.max(this.properties.min, v); - v = Math.min(this.properties.max, v); - this.setOutputData(0, v); }; - MathClamp.prototype.getCode = function(lang) { - var code = ""; - if (this.isInputConnected(0)) { - code += - "clamp({{0}}," + - this.properties.min + - "," + - this.properties.max + - ")"; + Watch.prototype.getTitle = function() { + if (this.flags.collapsed) { + return this.inputs[0].label; } - return code; + return this.title; }; - LiteGraph.registerNodeType("math/clamp", MathClamp); - - //Math ABS - function MathLerp() { - this.properties = { f: 0.5 }; - this.addInput("A", "number"); - this.addInput("B", "number"); - - this.addOutput("out", "number"); - } - - MathLerp.title = "Lerp"; - MathLerp.desc = "Linear Interpolation"; - - MathLerp.prototype.onExecute = function() { - var v1 = this.getInputData(0); - if (v1 == null) { - v1 = 0; - } - var v2 = this.getInputData(1); - if (v2 == null) { - v2 = 0; - } - - var f = this.properties.f; - - var _f = this.getInputData(2); - if (_f !== undefined) { - f = _f; + Watch.toString = function(o) { + if (o == null) { + return "null"; + } else if (o.constructor === Number) { + return o.toFixed(3); + } else if (o.constructor === Array) { + var str = "["; + for (var i = 0; i < o.length; ++i) { + str += Watch.toString(o[i]) + (i + 1 != o.length ? "," : ""); + } + str += "]"; + return str; + } else { + return String(o); } - - this.setOutputData(0, v1 * (1 - f) + v2 * f); }; - MathLerp.prototype.onGetInputs = function() { - return [["f", "number"]]; + Watch.prototype.onDrawBackground = function(ctx) { + // show the current value + this.inputs[0].label = Watch.toString(this.value); }; - LiteGraph.registerNodeType("math/lerp", MathLerp); + LiteGraph.registerNodeType("basic/watch", Watch); - //Math ABS - function MathAbs() { - this.addInput("in", "number"); - this.addOutput("out", "number"); - this.size = [80, 30]; + // in case one type doesnt match other type but you want to connect them anyway + function Cast() { + this.addInput("in", 0); + this.addOutput("out", 0); + this.size = [40, 30]; } - MathAbs.title = "Abs"; - MathAbs.desc = "Absolute"; + Cast.title = "Cast"; + Cast.desc = "Allows to connect different types"; - MathAbs.prototype.onExecute = function() { - var v = this.getInputData(0); - if (v == null) { - return; - } - this.setOutputData(0, Math.abs(v)); + Cast.prototype.onExecute = function() { + this.setOutputData(0, this.getInputData(0)); }; - LiteGraph.registerNodeType("math/abs", MathAbs); + LiteGraph.registerNodeType("basic/cast", Cast); - //Math Floor - function MathFloor() { - this.addInput("in", "number"); - this.addOutput("out", "number"); + // Show value inside the debug console + function Console() { + this.mode = LiteGraph.ON_EVENT; this.size = [80, 30]; + this.addProperty("msg", ""); + this.addInput("log", LiteGraph.EVENT); + this.addInput("msg", 0); } - MathFloor.title = "Floor"; - MathFloor.desc = "Floor number to remove fractional part"; + Console.title = "Console"; + Console.desc = "Show value inside the console"; - MathFloor.prototype.onExecute = function() { - var v = this.getInputData(0); - if (v == null) { - return; + Console.prototype.onAction = function(action, param) { + // param is the action + var msg = this.getInputData(1); // getInputDataByName("msg"); + // if (msg == null || typeof msg == "undefined") return; + if (!msg) msg = this.properties.msg; + if (!msg) msg = "Event: "+param; // msg is undefined if the slot is lost? + if (action == "log") { + console.log(msg); + } else if (action == "warn") { + console.warn(msg); + } else if (action == "error") { + console.error(msg); } - this.setOutputData(0, Math.floor(v)); }; - LiteGraph.registerNodeType("math/floor", MathFloor); - - //Math frac - function MathFrac() { - this.addInput("in", "number"); - this.addOutput("out", "number"); - this.size = [80, 30]; - } - - MathFrac.title = "Frac"; - MathFrac.desc = "Returns fractional part"; - - MathFrac.prototype.onExecute = function() { - var v = this.getInputData(0); - if (v == null) { - return; + Console.prototype.onExecute = function() { + var msg = this.getInputData(1); // getInputDataByName("msg"); + if (!msg) msg = this.properties.msg; + if (msg != null && typeof msg != "undefined") { + this.properties.msg = msg; + console.log(msg); } - this.setOutputData(0, v % 1); }; - LiteGraph.registerNodeType("math/frac", MathFrac); - - //Math Floor - function MathSmoothStep() { - this.addInput("in", "number"); - this.addOutput("out", "number"); - this.size = [80, 30]; - this.properties = { A: 0, B: 1 }; - } - - MathSmoothStep.title = "Smoothstep"; - MathSmoothStep.desc = "Smoothstep"; - - MathSmoothStep.prototype.onExecute = function() { - var v = this.getInputData(0); - if (v === undefined) { - return; - } - - var edge0 = this.properties.A; - var edge1 = this.properties.B; - - // Scale, bias and saturate x to 0..1 range - v = clamp((v - edge0) / (edge1 - edge0), 0.0, 1.0); - // Evaluate polynomial - v = v * v * (3 - 2 * v); - - this.setOutputData(0, v); + Console.prototype.onGetInputs = function() { + return [ + ["log", LiteGraph.ACTION], + ["warn", LiteGraph.ACTION], + ["error", LiteGraph.ACTION], + ]; }; - LiteGraph.registerNodeType("math/smoothstep", MathSmoothStep); + LiteGraph.registerNodeType("basic/console", Console); - //Math scale - function MathScale() { - this.addInput("in", "number", { label: "" }); - this.addOutput("out", "number", { label: "" }); - this.size = [80, 30]; - this.addProperty("factor", 1); + // Show value inside the debug console + function Alert() { + this.mode = LiteGraph.ON_EVENT; + this.addProperty("msg", ""); + this.addInput("", LiteGraph.EVENT); + var that = this; + this.widget = this.addWidget("text", "Text", "", "msg"); + this.widgets_up = true; + this.size = [200, 30]; } - MathScale.title = "Scale"; - MathScale.desc = "v * factor"; + Alert.title = "Alert"; + Alert.desc = "Show an alert window"; + Alert.color = "#510"; - MathScale.prototype.onExecute = function() { - var value = this.getInputData(0); - if (value != null) { - this.setOutputData(0, value * this.properties.factor); - } + Alert.prototype.onConfigure = function(o) { + this.widget.value = o.properties.msg; }; - LiteGraph.registerNodeType("math/scale", MathScale); - - //Gate - function Gate() { - this.addInput("v","boolean"); - this.addInput("A"); - this.addInput("B"); - this.addOutput("out"); - } - - Gate.title = "Gate"; - Gate.desc = "if v is true, then outputs A, otherwise B"; - - Gate.prototype.onExecute = function() { - var v = this.getInputData(0); - this.setOutputData(0, this.getInputData( v ? 1 : 2 )); - }; + Alert.prototype.onAction = function(action, param) { + var msg = this.properties.msg; + setTimeout(function() { + alert(msg); + }, 10); + }; - LiteGraph.registerNodeType("math/gate", Gate); + LiteGraph.registerNodeType("basic/alert", Alert); + // Execites simple code + function NodeScript() { + this.size = [60, 30]; + this.addProperty("onExecute", "return A;"); + this.addInput("A", 0); + this.addInput("B", 0); + this.addOutput("out", 0); - //Math Average - function MathAverageFilter() { - this.addInput("in", "number"); - this.addOutput("out", "number"); - this.size = [80, 30]; - this.addProperty("samples", 10); - this._values = new Float32Array(10); - this._current = 0; + this._func = null; + this.data = {}; } - MathAverageFilter.title = "Average"; - MathAverageFilter.desc = "Average Filter"; + NodeScript.prototype.onConfigure = function(o) { + if (o.properties.onExecute && LiteGraph.allow_scripts) + this.compileCode(o.properties.onExecute); + else + console.warn("Script not compiled, LiteGraph.allow_scripts is false"); + }; - MathAverageFilter.prototype.onExecute = function() { - var v = this.getInputData(0); - if (v == null) { - v = 0; - } + NodeScript.title = "Script"; + NodeScript.desc = "executes a code (max 256 characters)"; - var num_samples = this._values.length; + NodeScript.widgets_info = {onExecute: { type: "code" }}; - this._values[this._current % num_samples] = v; - this._current += 1; - if (this._current > num_samples) { - this._current = 0; - } + NodeScript.prototype.onPropertyChanged = function(name, value) { + if (name == "onExecute" && LiteGraph.allow_scripts) + this.compileCode(value); + else + console.warn("Script not compiled, LiteGraph.allow_scripts is false"); + }; - var avr = 0; - for (var i = 0; i < num_samples; ++i) { - avr += this._values[i]; + NodeScript.prototype.compileCode = function(code) { + this._func = null; + if (code.length > 256) { + console.warn("Script too long, max 256 chars"); + } else { + var code_low = code.toLowerCase(); + var forbidden_words = [ + "script", + "body", + "document", + "eval", + "nodescript", + "function", + ]; // bad security solution + for (var i = 0; i < forbidden_words.length; ++i) { + if (code_low.indexOf(forbidden_words[i]) != -1) { + console.warn("invalid script"); + return; + } + } + try { + this._func = new Function("A", "B", "C", "DATA", "node", code); + } catch (err) { + console.error("Error parsing script"); + console.error(err); + } } - - this.setOutputData(0, avr / num_samples); }; - MathAverageFilter.prototype.onPropertyChanged = function(name, value) { - if (value < 1) { - value = 1; + NodeScript.prototype.onExecute = function() { + if (!this._func) { + return; } - this.properties.samples = Math.round(value); - var old = this._values; - this._values = new Float32Array(this.properties.samples); - if (old.length <= this._values.length) { - this._values.set(old); - } else { - this._values.set(old.subarray(0, this._values.length)); + try { + var A = this.getInputData(0); + var B = this.getInputData(1); + var C = this.getInputData(2); + this.setOutputData(0, this._func(A, B, C, this.data, this)); + } catch (err) { + console.error("Error in script"); + console.error(err); } }; - LiteGraph.registerNodeType("math/average", MathAverageFilter); - - //Math - function MathTendTo() { - this.addInput("in", "number"); - this.addOutput("out", "number"); - this.addProperty("factor", 0.1); - this.size = [80, 30]; - this._value = null; - } - - MathTendTo.title = "TendTo"; - MathTendTo.desc = "moves the output value always closer to the input"; - - MathTendTo.prototype.onExecute = function() { - var v = this.getInputData(0); - if (v == null) { - v = 0; - } - var f = this.properties.factor; - if (this._value == null) { - this._value = v; - } else { - this._value = this._value * (1 - f) + v * f; - } - this.setOutputData(0, this._value); + NodeScript.prototype.onGetOutputs = function() { + return [["C", ""]]; }; - LiteGraph.registerNodeType("math/tendTo", MathTendTo); + LiteGraph.registerNodeType("basic/script", NodeScript); - //Math operation - function MathOperation() { - this.addInput("A", "number,array,object"); - this.addInput("B", "number"); - this.addOutput("=", "number"); + + function GenericCompare() { + this.addInput("A", 0); + this.addInput("B", 0); + this.addOutput("true", "boolean"); + this.addOutput("false", "boolean"); this.addProperty("A", 1); this.addProperty("B", 1); - this.addProperty("OP", "+", "enum", { values: MathOperation.values }); - this._func = MathOperation.funcs[this.properties.OP]; - this._result = []; //only used for arrays + this.addProperty("OP", "==", "enum", { values: GenericCompare.values }); + this.addWidget("combo","Op.",this.properties.OP,{ property: "OP", values: GenericCompare.values } ); + + this.size = [80, 60]; } - MathOperation.values = ["+", "-", "*", "/", "%", "^", "max", "min"]; - MathOperation.funcs = { - "+": function(A,B) { return A + B; }, - "-": function(A,B) { return A - B; }, - "x": function(A,B) { return A * B; }, - "X": function(A,B) { return A * B; }, - "*": function(A,B) { return A * B; }, - "/": function(A,B) { return A / B; }, - "%": function(A,B) { return A % B; }, - "^": function(A,B) { return Math.pow(A, B); }, - "max": function(A,B) { return Math.max(A, B); }, - "min": function(A,B) { return Math.min(A, B); } - }; - - MathOperation.title = "Operation"; - MathOperation.desc = "Easy math operators"; - MathOperation["@OP"] = { + GenericCompare.values = ["==", "!="]; // [">", "<", "==", "!=", "<=", ">=", "||", "&&" ]; + GenericCompare["@OP"] = { type: "enum", title: "operation", - values: MathOperation.values + values: GenericCompare.values, }; - MathOperation.size = [100, 60]; - MathOperation.prototype.getTitle = function() { - if(this.properties.OP == "max" || this.properties.OP == "min") - return this.properties.OP + "(A,B)"; - return "A " + this.properties.OP + " B"; - }; + GenericCompare.title = "Compare *"; + GenericCompare.desc = "evaluates condition between A and B"; - MathOperation.prototype.setValue = function(v) { - if (typeof v == "string") { - v = parseFloat(v); - } - this.properties["value"] = v; + GenericCompare.prototype.getTitle = function() { + return "*A " + this.properties.OP + " *B"; }; - MathOperation.prototype.onPropertyChanged = function(name, value) - { - if (name != "OP") - return; - this._func = MathOperation.funcs[this.properties.OP]; - if(!this._func) - { - console.warn("Unknown operation: " + this.properties.OP); - this._func = function(A) { return A; }; - } - } - - MathOperation.prototype.onExecute = function() { + GenericCompare.prototype.onExecute = function() { var A = this.getInputData(0); - var B = this.getInputData(1); - if ( A != null ) { - if( A.constructor === Number ) - this.properties["A"] = A; + if (A === undefined) { + A = this.properties.A; } else { - A = this.properties["A"]; + this.properties.A = A; } - if (B != null) { - this.properties["B"] = B; + var B = this.getInputData(1); + if (B === undefined) { + B = this.properties.B; } else { - B = this.properties["B"]; + this.properties.B = B; } - var func = MathOperation.funcs[this.properties.OP]; - - var result; - if(A.constructor === Number) - { - result = 0; - result = func(A,B); - } - else if(A.constructor === Array) - { - result = this._result; - result.length = A.length; - for(var i = 0; i < A.length; ++i) - result[i] = func(A[i],B); - } - else - { - result = {}; - for(var i in A) - result[i] = func(A[i],B); - } - this.setOutputData(0, result); + var result = false; + if (typeof A == typeof B) { + switch (this.properties.OP) { + case "==": + case "!=": + // traverse both objects.. consider that this is not a true deep check! consider underscore or other library for thath :: _isEqual() + result = true; + switch(typeof A) { + case "object": + var aProps = Object.getOwnPropertyNames(A); + var bProps = Object.getOwnPropertyNames(B); + if (aProps.length != bProps.length) { + result = false; + break; + } + for (var i = 0; i < aProps.length; i++) { + var propName = aProps[i]; + if (A[propName] !== B[propName]) { + result = false; + break; + } + } + break; + default: + result = A == B; + } + if (this.properties.OP == "!=") result = !result; + break; + /* case ">": + result = A > B; + break; + case "<": + result = A < B; + break; + case "<=": + result = A <= B; + break; + case ">=": + result = A >= B; + break; + case "||": + result = A || B; + break; + case "&&": + result = A && B; + break;*/ + } + } + this.setOutputData(0, result); + this.setOutputData(1, !result); }; - MathOperation.prototype.onDrawBackground = function(ctx) { - if (this.flags.collapsed) { - return; - } + LiteGraph.registerNodeType("basic/CompareValues", GenericCompare); - ctx.font = "40px Arial"; - ctx.fillStyle = "#666"; - ctx.textAlign = "center"; - ctx.fillText( - this.properties.OP, - this.size[0] * 0.5, - (this.size[1] + LiteGraph.NODE_TITLE_HEIGHT) * 0.5 - ); - ctx.textAlign = "left"; - }; +})(this); - LiteGraph.registerNodeType("math/operation", MathOperation); +// event related nodes +(function(global) { + var LiteGraph = global.LiteGraph; - LiteGraph.registerSearchboxExtra("math/operation", "MAX", { - properties: {OP:"max"}, - title: "MAX()" - }); + // Show value inside the debug console + function LogEvent() { + this.size = [60, 30]; + this.addInput("event", LiteGraph.ACTION); + } - LiteGraph.registerSearchboxExtra("math/operation", "MIN", { - properties: {OP:"min"}, - title: "MIN()" - }); + LogEvent.title = "Log Event"; + LogEvent.desc = "Log event in console"; + LogEvent.prototype.onAction = function(action, param, options) { + console.log(action, param); + }; - //Math compare - function MathCompare() { - this.addInput("A", "number"); - this.addInput("B", "number"); - this.addOutput("A==B", "boolean"); - this.addOutput("A!=B", "boolean"); - this.addProperty("A", 0); - this.addProperty("B", 0); + LiteGraph.registerNodeType("events/log", LogEvent); + + // convert to Event if the value is true + function TriggerEvent() { + this.size = [60, 30]; + this.addInput("if", ""); + this.addOutput("true", LiteGraph.EVENT); + this.addOutput("change", LiteGraph.EVENT); + this.addOutput("false", LiteGraph.EVENT); + this.properties = { only_on_change: true }; + this.prev = 0; } - MathCompare.title = "Compare"; - MathCompare.desc = "compares between two values"; + TriggerEvent.title = "TriggerEvent"; + TriggerEvent.desc = "Triggers event if input evaluates to true"; - MathCompare.prototype.onExecute = function() { - var A = this.getInputData(0); - var B = this.getInputData(1); - if (A !== undefined) { - this.properties["A"] = A; - } else { - A = this.properties["A"]; - } + TriggerEvent.prototype.onExecute = function( param, options) { + var v = this.getInputData(0); + var changed = (v != this.prev); + if(this.prev === 0) + changed = false; + var must_resend = (changed && this.properties.only_on_change) || (!changed && !this.properties.only_on_change); + if(v && must_resend ) + this.triggerSlot(0, param, null, options); + if(!v && must_resend) + this.triggerSlot(2, param, null, options); + if(changed) + this.triggerSlot(1, param, null, options); + this.prev = v; + }; + + LiteGraph.registerNodeType("events/trigger", TriggerEvent); + + // Sequence of events + function Sequence() { + var that = this; + this.addInput("", LiteGraph.ACTION); + this.addInput("", LiteGraph.ACTION); + this.addInput("", LiteGraph.ACTION); + this.addOutput("", LiteGraph.EVENT); + this.addOutput("", LiteGraph.EVENT); + this.addOutput("", LiteGraph.EVENT); + this.addWidget("button","+",null,function() { + that.addInput("", LiteGraph.ACTION); + that.addOutput("", LiteGraph.EVENT); + }); + this.size = [90, 70]; + this.flags = { horizontal: true, render_box: false }; + } - if (B !== undefined) { - this.properties["B"] = B; - } else { - B = this.properties["B"]; - } + Sequence.title = "Sequence"; + Sequence.desc = "Triggers a sequence of events when an event arrives"; - 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; - 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); - } + Sequence.prototype.getTitle = function() { + return ""; }; - MathCompare.prototype.onGetOutputs = function() { - return [ - ["A==B", "boolean"], - ["A!=B", "boolean"], - ["A>B", "boolean"], - ["A=B", "boolean"], - ["A<=B", "boolean"] - ]; + Sequence.prototype.onAction = function(action, param, options) { + if (this.outputs) { + options = options || {}; + for (var i = 0; i < this.outputs.length; ++i) { + var output = this.outputs[i]; + // needs more info about this... + if( options.action_call ) // CREATE A NEW ID FOR THE ACTION + options.action_call = options.action_call + "_seq_" + i; + else + options.action_call = this.id + "_" + (action ? action : "action")+"_seq_"+i+"_"+Math.floor(Math.random()*9999); + this.triggerSlot(i, param, null, options); + } + } }; - LiteGraph.registerNodeType("math/compare", MathCompare); - - LiteGraph.registerSearchboxExtra("math/compare", "==", { - outputs: [["A==B", "boolean"]], - title: "A==B" - }); - LiteGraph.registerSearchboxExtra("math/compare", "!=", { - outputs: [["A!=B", "boolean"]], - title: "A!=B" - }); - LiteGraph.registerSearchboxExtra("math/compare", ">", { - outputs: [["A>B", "boolean"]], - title: "A>B" - }); - LiteGraph.registerSearchboxExtra("math/compare", "<", { - outputs: [["A=", { - outputs: [["A>=B", "boolean"]], - title: "A>=B" - }); - LiteGraph.registerSearchboxExtra("math/compare", "<=", { - outputs: [["A<=B", "boolean"]], - title: "A<=B" - }); + LiteGraph.registerNodeType("events/sequence", Sequence); - function MathCondition() { - this.addInput("A", "number"); - this.addInput("B", "number"); - this.addOutput("true", "boolean"); - this.addOutput("false", "boolean"); - this.addProperty("A", 1); - this.addProperty("B", 1); - this.addProperty("OP", ">", "enum", { values: MathCondition.values }); - this.addWidget("combo","Cond.",this.properties.OP,{ property: "OP", values: MathCondition.values } ); - this.size = [80, 60]; + // Sequence of events + function WaitAll() { + var that = this; + this.addInput("", LiteGraph.ACTION); + this.addInput("", LiteGraph.ACTION); + this.addOutput("", LiteGraph.EVENT); + this.addWidget("button","+",null,function() { + that.addInput("", LiteGraph.ACTION); + that.size[0] = 90; + }); + this.size = [90, 70]; + this.ready = []; } - MathCondition.values = [">", "<", "==", "!=", "<=", ">=", "||", "&&" ]; - MathCondition["@OP"] = { - type: "enum", - title: "operation", - values: MathCondition.values - }; - - MathCondition.title = "Condition"; - MathCondition.desc = "evaluates condition between A and B"; + WaitAll.title = "WaitAll"; + WaitAll.desc = "Wait until all input events arrive then triggers output"; - MathCondition.prototype.getTitle = function() { - return "A " + this.properties.OP + " B"; + WaitAll.prototype.getTitle = function() { + return ""; }; - MathCondition.prototype.onExecute = function() { - var A = this.getInputData(0); - if (A === undefined) { - A = this.properties.A; - } else { - this.properties.A = A; + WaitAll.prototype.onDrawBackground = function(ctx) { + if (this.flags.collapsed) { + return; } - - var B = this.getInputData(1); - if (B === undefined) { - B = this.properties.B; - } else { - this.properties.B = B; + for(var i = 0; i < this.inputs.length; ++i) { + var y = i * LiteGraph.NODE_SLOT_HEIGHT + 10; + ctx.fillStyle = this.ready[i] ? "#AFB" : "#000"; + ctx.fillRect(20, y, 10, 10); } + } - var result = true; - switch (this.properties.OP) { - case ">": - result = A > B; - break; - case "<": - result = A < B; - break; - case "==": - result = A == B; - break; - case "!=": - result = A != B; - break; - case "<=": - result = A <= B; - break; - case ">=": - result = A >= B; - break; - case "||": - result = A || B; - break; - case "&&": - result = A && B; - break; - } + WaitAll.prototype.onAction = function(action, param, options, slot_index) { + if(slot_index == null) + return; - this.setOutputData(0, result); - this.setOutputData(1, !result); + // check all + this.ready.length = this.outputs.length; + this.ready[slot_index] = true; + for(var i = 0; i < this.ready.length;++i) + if(!this.ready[i]) + return; + // pass + this.reset(); + this.triggerSlot(0); }; - LiteGraph.registerNodeType("math/condition", MathCondition); + WaitAll.prototype.reset = function() { + this.ready.length = 0; + } + LiteGraph.registerNodeType("events/waitAll", WaitAll); - function MathBranch() { - this.addInput("in", 0); - this.addInput("cond", "boolean"); - this.addOutput("true", 0); - this.addOutput("false", 0); - this.size = [80, 60]; + + // Sequencer for events + function Stepper() { + var that = this; + this.properties = { index: 0 }; + this.addInput("index", "number"); + this.addInput("step", LiteGraph.ACTION); + this.addInput("reset", LiteGraph.ACTION); + this.addOutput("index", "number"); + this.addOutput("", LiteGraph.EVENT); + this.addOutput("", LiteGraph.EVENT); + this.addOutput("", LiteGraph.EVENT,{removable: true}); + this.addWidget("button","+",null,function() { + that.addOutput("", LiteGraph.EVENT, {removable: true}); + }); + this.size = [120, 120]; + this.flags = { render_box: false }; } - MathBranch.title = "Branch"; - MathBranch.desc = "If condition is true, outputs IN in true, otherwise in false"; + Stepper.title = "Stepper"; + Stepper.desc = "Trigger events sequentially when an tick arrives"; - MathBranch.prototype.onExecute = function() { - var V = this.getInputData(0); - var cond = this.getInputData(1); + Stepper.prototype.onDrawBackground = function(ctx) { + if (this.flags.collapsed) { + return; + } + var index = this.properties.index || 0; + ctx.fillStyle = "#AFB"; + var w = this.size[0]; + var y = (index + 1)* LiteGraph.NODE_SLOT_HEIGHT + 4; + ctx.beginPath(); + ctx.moveTo(w - 30, y); + ctx.lineTo(w - 30, y + LiteGraph.NODE_SLOT_HEIGHT); + ctx.lineTo(w - 15, y + LiteGraph.NODE_SLOT_HEIGHT * 0.5); + ctx.fill(); + } - if(cond) - { - this.setOutputData(0, V); - this.setOutputData(1, null); - } - else - { - this.setOutputData(0, null); - this.setOutputData(1, V); - } - } + Stepper.prototype.onExecute = function() { + var index = this.getInputData(0); + if(index != null) { + index = Math.floor(index); + index = clamp( index, 0, this.outputs ? (this.outputs.length - 2) : 0 ); + if( index != this.properties.index ) { + this.properties.index = index; + this.triggerSlot( index+1 ); + } + } - LiteGraph.registerNodeType("math/branch", MathBranch); + this.setOutputData(0, this.properties.index ); + } + Stepper.prototype.onAction = function(action, param) { + if(action == "reset") + this.properties.index = 0; + else if(action == "step") { + this.triggerSlot(this.properties.index+1, param); + var n = this.outputs ? this.outputs.length - 1 : 0; + this.properties.index = (this.properties.index + 1) % n; + } + }; - function MathAccumulate() { - this.addInput("inc", "number"); - this.addOutput("total", "number"); - this.addProperty("increment", 1); - this.addProperty("value", 0); + LiteGraph.registerNodeType("events/stepper", Stepper); + + // Filter events + function FilterEvent() { + this.size = [60, 30]; + this.addInput("event", LiteGraph.ACTION); + this.addOutput("event", LiteGraph.EVENT); + this.properties = { + equal_to: "", + has_property: "", + property_equal_to: "", + }; } - MathAccumulate.title = "Accumulate"; - MathAccumulate.desc = "Increments a value every time"; + FilterEvent.title = "Filter Event"; + FilterEvent.desc = "Blocks events that do not match the filter"; - MathAccumulate.prototype.onExecute = function() { - if (this.properties.value === null) { - this.properties.value = 0; + FilterEvent.prototype.onAction = function(action, param, options) { + if (param == null) { + return; } - var inc = this.getInputData(0); - if (inc !== null) { - this.properties.value += inc; - } else { - this.properties.value += this.properties.increment; + if (this.properties.equal_to && this.properties.equal_to != param) { + return; } - this.setOutputData(0, this.properties.value); + + if (this.properties.has_property) { + var prop = param[this.properties.has_property]; + if (prop == null) { + return; + } + + if ( + this.properties.property_equal_to && + this.properties.property_equal_to != prop + ) { + return; + } + } + + this.triggerSlot(0, param, null, options); }; - LiteGraph.registerNodeType("math/accumulate", MathAccumulate); + LiteGraph.registerNodeType("events/filter", FilterEvent); - //Math Trigonometry - function MathTrigonometry() { - this.addInput("v", "number"); - this.addOutput("sin", "number"); - this.addProperty("amplitude", 1); - this.addProperty("offset", 0); - this.bgImageUrl = "nodes/imgs/icon-sin.png"; + function EventBranch() { + this.addInput("in", LiteGraph.ACTION); + this.addInput("cond", "boolean"); + this.addOutput("true", LiteGraph.EVENT); + this.addOutput("false", LiteGraph.EVENT); + this.size = [120, 60]; + this._value = false; } - MathTrigonometry.title = "Trigonometry"; - MathTrigonometry.desc = "Sin Cos Tan"; - //MathTrigonometry.filter = "shader"; + EventBranch.title = "Branch"; + EventBranch.desc = "If condition is true, outputs triggers true, otherwise false"; - MathTrigonometry.prototype.onExecute = function() { - var v = this.getInputData(0); - if (v == null) { - v = 0; - } - var amplitude = this.properties["amplitude"]; - var slot = this.findInputSlot("amplitude"); - if (slot != -1) { - amplitude = this.getInputData(slot); - } - var offset = this.properties["offset"]; - slot = this.findInputSlot("offset"); - if (slot != -1) { - offset = this.getInputData(slot); + EventBranch.prototype.onExecute = function() { + this._value = this.getInputData(1); + } + + EventBranch.prototype.onAction = function(action, param, options) { + this._value = this.getInputData(1); + this.triggerSlot(this._value ? 0 : 1, param, null, options); + } + + LiteGraph.registerNodeType("events/branch", EventBranch); + + // Show value inside the debug console + function EventCounter() { + this.addInput("inc", LiteGraph.ACTION); + this.addInput("dec", LiteGraph.ACTION); + this.addInput("reset", LiteGraph.ACTION); + this.addOutput("change", LiteGraph.EVENT); + this.addOutput("num", "number"); + this.addProperty("doCountExecution", false, "boolean", {name: "Count Executions"}); + this.addWidget("toggle","Count Exec.",this.properties.doCountExecution,"doCountExecution"); + this.num = 0; + } + + EventCounter.title = "Counter"; + EventCounter.desc = "Counts events"; + + EventCounter.prototype.getTitle = function() { + if (this.flags.collapsed) { + return String(this.num); } + return this.title; + }; - for (var i = 0, l = this.outputs.length; i < l; ++i) { - var output = this.outputs[i]; - var value; - 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, amplitude * value + offset); + EventCounter.prototype.onAction = function(action, param, options) { + var v = this.num; + if (action == "inc") { + this.num += 1; + } else if (action == "dec") { + this.num -= 1; + } else if (action == "reset") { + this.num = 0; + } + if (this.num != v) { + this.trigger("change", this.num); } }; - MathTrigonometry.prototype.onGetInputs = function() { - return [["v", "number"], ["amplitude", "number"], ["offset", "number"]]; + EventCounter.prototype.onDrawBackground = function(ctx) { + if (this.flags.collapsed) { + return; + } + ctx.fillStyle = "#AAA"; + ctx.font = "20px Arial"; + ctx.textAlign = "center"; + ctx.fillText(this.num, this.size[0] * 0.5, this.size[1] * 0.5); }; - MathTrigonometry.prototype.onGetOutputs = function() { - return [ - ["sin", "number"], - ["cos", "number"], - ["tan", "number"], - ["asin", "number"], - ["acos", "number"], - ["atan", "number"] - ]; + EventCounter.prototype.onExecute = function() { + if(this.properties.doCountExecution) { + this.num += 1; + } + this.setOutputData(1, this.num); }; - LiteGraph.registerNodeType("math/trigonometry", MathTrigonometry); + LiteGraph.registerNodeType("events/counter", EventCounter); - LiteGraph.registerSearchboxExtra("math/trigonometry", "SIN()", { - outputs: [["sin", "number"]], - title: "SIN()" - }); - LiteGraph.registerSearchboxExtra("math/trigonometry", "COS()", { - outputs: [["cos", "number"]], - title: "COS()" - }); - LiteGraph.registerSearchboxExtra("math/trigonometry", "TAN()", { - outputs: [["tan", "number"]], - title: "TAN()" - }); + // Show value inside the debug console + function DelayEvent() { + this.size = [60, 30]; + this.addProperty("time_in_ms", 1000); + this.addInput("event", LiteGraph.ACTION); + this.addOutput("on_time", LiteGraph.EVENT); - //math library for safe math operations without eval - function MathFormula() { - this.addInput("x", "number"); - this.addInput("y", "number"); - this.addOutput("", "number"); - this.properties = { x: 1.0, y: 1.0, formula: "x+y" }; - this.code_widget = this.addWidget( - "text", - "F(x,y)", - this.properties.formula, - function(v, canvas, node) { - node.properties.formula = v; - } - ); - this.addWidget("toggle", "allow", LiteGraph.allow_scripts, function(v) { - LiteGraph.allow_scripts = v; - }); - this._func = null; + this._pending = []; } - MathFormula.title = "Formula"; - MathFormula.desc = "Compute formula"; - MathFormula.size = [160, 100]; + DelayEvent.title = "Delay"; + DelayEvent.desc = "Delays one event"; - MathAverageFilter.prototype.onPropertyChanged = function(name, value) { - if (name == "formula") { - this.code_widget.value = value; + DelayEvent.prototype.onAction = function(action, param, options) { + var time = this.properties.time_in_ms; + if (time <= 0) { + this.trigger(null, param, options); + } else { + this._pending.push([time, param]); } }; - MathFormula.prototype.onExecute = function() { - if (!LiteGraph.allow_scripts) { - return; - } + DelayEvent.prototype.onExecute = function(param, options) { + var dt = this.graph.elapsed_time * 1000; // in ms - var x = this.getInputData(0); - var y = this.getInputData(1); - if (x != null) { - this.properties["x"] = x; - } else { - x = this.properties["x"]; + if (this.isInputConnected(1)) { + this.properties.time_in_ms = this.getInputData(1); } - if (y != null) { - this.properties["y"] = y; - } else { - y = this.properties["y"]; - } + for (var i = 0; i < this._pending.length; ++i) { + var actionPass = this._pending[i]; + actionPass[0] -= dt; + if (actionPass[0] > 0) { + continue; + } - var f = this.properties["formula"]; + // remove + this._pending.splice(i, 1); + --i; - var value; - try { - if (!this._func || this._func_code != this.properties.formula) { - this._func = new Function( - "x", - "y", - "TIME", - "return " + this.properties.formula - ); - this._func_code = this.properties.formula; - } - value = this._func(x, y, this.graph.globaltime); - this.boxcolor = null; - } catch (err) { - this.boxcolor = "red"; + // trigger + this.trigger(null, actionPass[1], options); } - this.setOutputData(0, value); - }; - - MathFormula.prototype.getTitle = function() { - return this._func_code || "Formula"; }; - MathFormula.prototype.onDrawBackground = function() { - var f = this.properties["formula"]; - if (this.outputs && this.outputs.length) { - this.outputs[0].label = f; - } + DelayEvent.prototype.onGetInputs = function() { + return [["event", LiteGraph.ACTION], ["time_in_ms", "number"]]; }; - LiteGraph.registerNodeType("math/formula", MathFormula); + LiteGraph.registerNodeType("events/delay", DelayEvent); - function Math3DVec2ToXY() { - this.addInput("vec2", "vec2"); - this.addOutput("x", "number"); - this.addOutput("y", "number"); + // Show value inside the debug console + function TimerEvent() { + this.addProperty("interval", 1000); + this.addProperty("event", "tick"); + this.addOutput("on_tick", LiteGraph.EVENT); + this.time = 0; + this.last_interval = 1000; + this.triggered = false; } - Math3DVec2ToXY.title = "Vec2->XY"; - Math3DVec2ToXY.desc = "vector 2 to components"; + TimerEvent.title = "Timer"; + TimerEvent.desc = "Sends an event every N milliseconds"; - Math3DVec2ToXY.prototype.onExecute = function() { - var v = this.getInputData(0); - if (v == null) { - return; - } + TimerEvent.prototype.onStart = function() { + this.time = 0; + }; - this.setOutputData(0, v[0]); - this.setOutputData(1, v[1]); + TimerEvent.prototype.getTitle = function() { + return "Timer: " + this.last_interval.toString() + "ms"; }; - LiteGraph.registerNodeType("math3d/vec2-to-xy", Math3DVec2ToXY); + TimerEvent.on_color = "#AAA"; + TimerEvent.off_color = "#222"; - function Math3DXYToVec2() { - this.addInputs([["x", "number"], ["y", "number"]]); - this.addOutput("vec2", "vec2"); - this.properties = { x: 0, y: 0 }; - this._data = new Float32Array(2); - } + TimerEvent.prototype.onDrawBackground = function() { + this.boxcolor = this.triggered + ? TimerEvent.on_color + : TimerEvent.off_color; + this.triggered = false; + }; - Math3DXYToVec2.title = "XY->Vec2"; - Math3DXYToVec2.desc = "components to vector2"; + TimerEvent.prototype.onExecute = function() { + var dt = this.graph.elapsed_time * 1000; // in ms - Math3DXYToVec2.prototype.onExecute = function() { - var x = this.getInputData(0); - if (x == null) { - x = this.properties.x; + var trigger = this.time == 0; + + this.time += dt; + this.last_interval = Math.max( + 1, + this.getInputOrProperty("interval") | 0, + ); + + if ( + !trigger && + (this.time < this.last_interval || isNaN(this.last_interval)) + ) { + if (this.inputs && this.inputs.length > 1 && this.inputs[1]) { + this.setOutputData(1, false); + } + return; } - var y = this.getInputData(1); - if (y == null) { - y = this.properties.y; + + this.triggered = true; + this.time = this.time % this.last_interval; + this.trigger("on_tick", this.properties.event); + if (this.inputs && this.inputs.length > 1 && this.inputs[1]) { + this.setOutputData(1, true); } + }; - var data = this._data; - data[0] = x; - data[1] = y; + TimerEvent.prototype.onGetInputs = function() { + return [["interval", "number"]]; + }; - this.setOutputData(0, data); + TimerEvent.prototype.onGetOutputs = function() { + return [["tick", "boolean"]]; }; - LiteGraph.registerNodeType("math3d/xy-to-vec2", Math3DXYToVec2); + LiteGraph.registerNodeType("events/timer", TimerEvent); - function Math3DVec3ToXYZ() { - this.addInput("vec3", "vec3"); - this.addOutput("x", "number"); - this.addOutput("y", "number"); - this.addOutput("z", "number"); + + + function SemaphoreEvent() { + this.addInput("go", LiteGraph.ACTION ); + this.addInput("green", LiteGraph.ACTION ); + this.addInput("red", LiteGraph.ACTION ); + this.addOutput("continue", LiteGraph.EVENT ); + this.addOutput("blocked", LiteGraph.EVENT ); + this.addOutput("is_green", "boolean" ); + this._ready = false; + this.properties = {}; + var that = this; + this.addWidget("button","reset","",function() { + that._ready = false; + }); } - Math3DVec3ToXYZ.title = "Vec3->XYZ"; - Math3DVec3ToXYZ.desc = "vector 3 to components"; + SemaphoreEvent.title = "Semaphore Event"; + SemaphoreEvent.desc = "Until both events are not triggered, it doesnt continue."; - Math3DVec3ToXYZ.prototype.onExecute = function() { - var v = this.getInputData(0); - if (v == null) { - return; - } + SemaphoreEvent.prototype.onExecute = function() { + this.setOutputData(1,this._ready); + this.boxcolor = this._ready ? "#9F9" : "#FA5"; + } - this.setOutputData(0, v[0]); - this.setOutputData(1, v[1]); - this.setOutputData(2, v[2]); + SemaphoreEvent.prototype.onAction = function(action, param) { + if( action == "go" ) + this.triggerSlot( this._ready ? 0 : 1 ); + else if( action == "green" ) + this._ready = true; + else if( action == "red" ) + this._ready = false; }; - LiteGraph.registerNodeType("math3d/vec3-to-xyz", Math3DVec3ToXYZ); + LiteGraph.registerNodeType("events/semaphore", SemaphoreEvent); - function Math3DXYZToVec3() { - this.addInputs([["x", "number"], ["y", "number"], ["z", "number"]]); - this.addOutput("vec3", "vec3"); - this.properties = { x: 0, y: 0, z: 0 }; - this._data = new Float32Array(3); + function OnceEvent() { + this.addInput("in", LiteGraph.ACTION ); + this.addInput("reset", LiteGraph.ACTION ); + this.addOutput("out", LiteGraph.EVENT ); + this._once = false; + this.properties = {}; + var that = this; + this.addWidget("button","reset","",function() { + that._once = false; + }); } - Math3DXYZToVec3.title = "XYZ->Vec3"; - Math3DXYZToVec3.desc = "components to vector3"; + OnceEvent.title = "Once"; + OnceEvent.desc = "Only passes an event once, then gets locked"; - Math3DXYZToVec3.prototype.onExecute = function() { - var x = this.getInputData(0); - if (x == null) { - x = this.properties.x; - } - var y = this.getInputData(1); - if (y == null) { - y = this.properties.y; - } - var z = this.getInputData(2); - if (z == null) { - z = this.properties.z; - } - - var data = this._data; - data[0] = x; - data[1] = y; - data[2] = z; - - this.setOutputData(0, data); + OnceEvent.prototype.onAction = function(action, param) { + if( action == "in" && !this._once ) { + this._once = true; + this.triggerSlot( 0, param ); + } else if( action == "reset" ) + this._once = false; }; - LiteGraph.registerNodeType("math3d/xyz-to-vec3", Math3DXYZToVec3); + LiteGraph.registerNodeType("events/once", OnceEvent); - function Math3DVec4ToXYZW() { - this.addInput("vec4", "vec4"); - this.addOutput("x", "number"); - this.addOutput("y", "number"); - this.addOutput("z", "number"); - this.addOutput("w", "number"); + function DataStore() { + this.addInput("data", 0); + this.addInput("assign", LiteGraph.ACTION); + this.addOutput("data", 0); + this._last_value = null; + this.properties = { data: null, serialize: true }; + var that = this; + this.addWidget("button","store","",function() { + that.properties.data = that._last_value; + }); } - Math3DVec4ToXYZW.title = "Vec4->XYZW"; - Math3DVec4ToXYZW.desc = "vector 4 to components"; + DataStore.title = "Data Store"; + DataStore.desc = "Stores data and only changes when event is received"; - Math3DVec4ToXYZW.prototype.onExecute = function() { - var v = this.getInputData(0); - if (v == null) { - return; - } + DataStore.prototype.onExecute = function() { + this._last_value = this.getInputData(0); + this.setOutputData(0, this.properties.data ); + } - this.setOutputData(0, v[0]); - this.setOutputData(1, v[1]); - this.setOutputData(2, v[2]); - this.setOutputData(3, v[3]); + DataStore.prototype.onAction = function(action, param, options) { + this.properties.data = this._last_value; }; - LiteGraph.registerNodeType("math3d/vec4-to-xyzw", Math3DVec4ToXYZW); - - function Math3DXYZWToVec4() { - this.addInputs([ - ["x", "number"], - ["y", "number"], - ["z", "number"], - ["w", "number"] - ]); - this.addOutput("vec4", "vec4"); - this.properties = { x: 0, y: 0, z: 0, w: 0 }; - this._data = new Float32Array(4); + DataStore.prototype.onSerialize = function(o) { + if(o.data == null) + return; + if(this.properties.serialize == false || (o.data.constructor !== String && o.data.constructor !== Number && o.data.constructor !== Boolean && o.data.constructor !== Array && o.data.constructor !== Object )) + o.data = null; } - Math3DXYZWToVec4.title = "XYZW->Vec4"; - Math3DXYZWToVec4.desc = "components to vector4"; - - Math3DXYZWToVec4.prototype.onExecute = function() { - var x = this.getInputData(0); - if (x == null) { - x = this.properties.x; - } - var y = this.getInputData(1); - if (y == null) { - y = this.properties.y; - } - var z = this.getInputData(2); - if (z == null) { - z = this.properties.z; - } - var w = this.getInputData(3); - if (w == null) { - w = this.properties.w; - } - - var data = this._data; - data[0] = x; - data[1] = y; - data[2] = z; - data[3] = w; + LiteGraph.registerNodeType("basic/data_store", DataStore); - this.setOutputData(0, data); - }; - LiteGraph.registerNodeType("math3d/xyzw-to-vec4", Math3DXYZWToVec4); })(this); -(function(global) { - var LiteGraph = global.LiteGraph; - - - function Math3DMat4() - { - this.addInput("T", "vec3"); - this.addInput("R", "vec3"); - this.addInput("S", "vec3"); - this.addOutput("mat4", "mat4"); - this.properties = { - "T":[0,0,0], - "R":[0,0,0], - "S":[1,1,1], - R_in_degrees: true - }; - this._result = mat4.create(); - this._must_update = true; - } - - Math3DMat4.title = "mat4"; - Math3DMat4.temp_quat = new Float32Array([0,0,0,1]); - Math3DMat4.temp_mat4 = new Float32Array(16); - Math3DMat4.temp_vec3 = new Float32Array(3); - - Math3DMat4.prototype.onPropertyChanged = function(name, value) - { - this._must_update = true; - } - - Math3DMat4.prototype.onExecute = function() - { - var M = this._result; - var Q = Math3DMat4.temp_quat; - var temp_mat4 = Math3DMat4.temp_mat4; - var temp_vec3 = Math3DMat4.temp_vec3; - - var T = this.getInputData(0); - var R = this.getInputData(1); - var S = this.getInputData(2); - - if( this._must_update || T || R || S ) - { - T = T || this.properties.T; - R = R || this.properties.R; - S = S || this.properties.S; - mat4.identity( M ); - mat4.translate( M, M, T ); - if(this.properties.R_in_degrees) - { - temp_vec3.set( R ); - vec3.scale(temp_vec3,temp_vec3,DEG2RAD); - quat.fromEuler( Q, temp_vec3 ); - } - else - quat.fromEuler( Q, R ); - mat4.fromQuat( temp_mat4, Q ); - mat4.multiply( M, M, temp_mat4 ); - mat4.scale( M, M, S ); - } - - this.setOutputData(0, M); - } - - LiteGraph.registerNodeType("math3d/mat4", Math3DMat4); - - //Math 3D operation - function Math3DOperation() { - this.addInput("A", "number,vec3"); - this.addInput("B", "number,vec3"); - this.addOutput("=", "number,vec3"); - this.addProperty("OP", "+", "enum", { values: Math3DOperation.values }); - this._result = vec3.create(); - } - - Math3DOperation.values = ["+", "-", "*", "/", "%", "^", "max", "min","dot","cross"]; - - LiteGraph.registerSearchboxExtra("math3d/operation", "CROSS()", { - properties: {"OP":"cross"}, - title: "CROSS()" - }); - - LiteGraph.registerSearchboxExtra("math3d/operation", "DOT()", { - properties: {"OP":"dot"}, - title: "DOT()" - }); - - Math3DOperation.title = "Operation"; - Math3DOperation.desc = "Easy math 3D operators"; - Math3DOperation["@OP"] = { - type: "enum", - title: "operation", - values: Math3DOperation.values - }; - Math3DOperation.size = [100, 60]; - - Math3DOperation.prototype.getTitle = function() { - if(this.properties.OP == "max" || this.properties.OP == "min" ) - return this.properties.OP + "(A,B)"; - return "A " + this.properties.OP + " B"; - }; - - Math3DOperation.prototype.onExecute = function() { - var A = this.getInputData(0); - var B = this.getInputData(1); - if(A == null || B == null) - return; - if(A.constructor === Number) - A = [A,A,A]; - if(B.constructor === Number) - B = [B,B,B]; - - var result = this._result; - switch (this.properties.OP) { - case "+": - result = vec3.add(result,A,B); - break; - case "-": - result = vec3.sub(result,A,B); - break; - case "x": - case "X": - case "*": - result = vec3.mul(result,A,B); - break; - case "/": - result = vec3.div(result,A,B); - break; - case "%": - result[0] = A[0]%B[0]; - result[1] = A[1]%B[1]; - result[2] = A[2]%B[2]; - break; - case "^": - result[0] = Math.pow(A[0],B[0]); - result[1] = Math.pow(A[1],B[1]); - result[2] = Math.pow(A[2],B[2]); - break; - case "max": - result[0] = Math.max(A[0],B[0]); - result[1] = Math.max(A[1],B[1]); - result[2] = Math.max(A[2],B[2]); - break; - case "min": - result[0] = Math.min(A[0],B[0]); - result[1] = Math.min(A[1],B[1]); - result[2] = Math.min(A[2],B[2]); - case "dot": - result = vec3.dot(A,B); - break; - case "cross": - vec3.cross(result,A,B); - break; - default: - console.warn("Unknown operation: " + this.properties.OP); - } - this.setOutputData(0, result); - }; - - Math3DOperation.prototype.onDrawBackground = function(ctx) { - if (this.flags.collapsed) { - return; - } - - ctx.font = "40px Arial"; - ctx.fillStyle = "#666"; - ctx.textAlign = "center"; - ctx.fillText( - this.properties.OP, - this.size[0] * 0.5, - (this.size[1] + LiteGraph.NODE_TITLE_HEIGHT) * 0.5 - ); - ctx.textAlign = "left"; - }; - - LiteGraph.registerNodeType("math3d/operation", Math3DOperation); - - function Math3DVec3Scale() { - this.addInput("in", "vec3"); - this.addInput("f", "number"); - this.addOutput("out", "vec3"); - this.properties = { f: 1 }; - this._data = new Float32Array(3); - } - - Math3DVec3Scale.title = "vec3_scale"; - Math3DVec3Scale.desc = "scales the components of a vec3"; - - Math3DVec3Scale.prototype.onExecute = function() { - var v = this.getInputData(0); - if (v == null) { - return; - } - var f = this.getInputData(1); - if (f == null) { - f = this.properties.f; - } - - var data = this._data; - data[0] = v[0] * f; - data[1] = v[1] * f; - data[2] = v[2] * f; - this.setOutputData(0, data); - }; - - LiteGraph.registerNodeType("math3d/vec3-scale", Math3DVec3Scale); - - function Math3DVec3Length() { - this.addInput("in", "vec3"); - this.addOutput("out", "number"); - } - - Math3DVec3Length.title = "vec3_length"; - Math3DVec3Length.desc = "returns the module of a vector"; - - Math3DVec3Length.prototype.onExecute = function() { - var v = this.getInputData(0); - if (v == null) { - return; - } - var dist = Math.sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]); - this.setOutputData(0, dist); - }; - - LiteGraph.registerNodeType("math3d/vec3-length", Math3DVec3Length); - - function Math3DVec3Normalize() { - this.addInput("in", "vec3"); - this.addOutput("out", "vec3"); - this._data = new Float32Array(3); - } - - Math3DVec3Normalize.title = "vec3_normalize"; - Math3DVec3Normalize.desc = "returns the vector normalized"; - - Math3DVec3Normalize.prototype.onExecute = function() { - var v = this.getInputData(0); - if (v == null) { - return; - } - var dist = Math.sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]); - var data = this._data; - data[0] = v[0] / dist; - data[1] = v[1] / dist; - data[2] = v[2] / dist; - - this.setOutputData(0, data); - }; - - LiteGraph.registerNodeType("math3d/vec3-normalize", Math3DVec3Normalize); - - function Math3DVec3Lerp() { - this.addInput("A", "vec3"); - this.addInput("B", "vec3"); - this.addInput("f", "vec3"); - this.addOutput("out", "vec3"); - this.properties = { f: 0.5 }; - this._data = new Float32Array(3); - } - - Math3DVec3Lerp.title = "vec3_lerp"; - Math3DVec3Lerp.desc = "returns the interpolated vector"; - - Math3DVec3Lerp.prototype.onExecute = function() { - var A = this.getInputData(0); - if (A == null) { - return; - } - var B = this.getInputData(1); - if (B == null) { - return; - } - var f = this.getInputOrProperty("f"); - - var data = this._data; - data[0] = A[0] * (1 - f) + B[0] * f; - data[1] = A[1] * (1 - f) + B[1] * f; - data[2] = A[2] * (1 - f) + B[2] * f; - - this.setOutputData(0, data); - }; - - LiteGraph.registerNodeType("math3d/vec3-lerp", Math3DVec3Lerp); - - function Math3DVec3Dot() { - this.addInput("A", "vec3"); - this.addInput("B", "vec3"); - this.addOutput("out", "number"); - } - - Math3DVec3Dot.title = "vec3_dot"; - Math3DVec3Dot.desc = "returns the dot product"; - - Math3DVec3Dot.prototype.onExecute = function() { - var A = this.getInputData(0); - if (A == null) { - return; - } - var B = this.getInputData(1); - if (B == null) { - return; - } - - var dot = A[0] * B[0] + A[1] * B[1] + A[2] * B[2]; - this.setOutputData(0, dot); - }; - - LiteGraph.registerNodeType("math3d/vec3-dot", Math3DVec3Dot); - - //if glMatrix is installed... - if (global.glMatrix) { - function Math3DQuaternion() { - this.addOutput("quat", "quat"); - this.properties = { x: 0, y: 0, z: 0, w: 1, normalize: false }; - this._value = quat.create(); - } - - Math3DQuaternion.title = "Quaternion"; - Math3DQuaternion.desc = "quaternion"; - - Math3DQuaternion.prototype.onExecute = function() { - this._value[0] = this.getInputOrProperty("x"); - this._value[1] = this.getInputOrProperty("y"); - this._value[2] = this.getInputOrProperty("z"); - this._value[3] = this.getInputOrProperty("w"); - if (this.properties.normalize) { - quat.normalize(this._value, this._value); - } - this.setOutputData(0, this._value); - }; - - Math3DQuaternion.prototype.onGetInputs = function() { - return [ - ["x", "number"], - ["y", "number"], - ["z", "number"], - ["w", "number"] - ]; - }; - - LiteGraph.registerNodeType("math3d/quaternion", Math3DQuaternion); - - function Math3DRotation() { - this.addInputs([["degrees", "number"], ["axis", "vec3"]]); - this.addOutput("quat", "quat"); - this.properties = { angle: 90.0, axis: vec3.fromValues(0, 1, 0) }; - - this._value = quat.create(); - } - - 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(this._value, axis, angle * 0.0174532925); - this.setOutputData(0, R); - }; - - LiteGraph.registerNodeType("math3d/rotation", Math3DRotation); - - - function MathEulerToQuat() { - this.addInput("euler", "vec3"); - this.addOutput("quat", "quat"); - this.properties = { euler:[0,0,0], use_yaw_pitch_roll: false }; - this._degs = vec3.create(); - this._value = quat.create(); - } - - MathEulerToQuat.title = "Euler->Quat"; - MathEulerToQuat.desc = "Converts euler angles (in degrees) to quaternion"; - - MathEulerToQuat.prototype.onExecute = function() { - var euler = this.getInputData(0); - if (euler == null) { - euler = this.properties.euler; - } - vec3.scale( this._degs, euler, DEG2RAD ); - if(this.properties.use_yaw_pitch_roll) - this._degs = [this._degs[2],this._degs[0],this._degs[1]]; - var R = quat.fromEuler(this._value, this._degs); - this.setOutputData(0, R); - }; - - LiteGraph.registerNodeType("math3d/euler_to_quat", MathEulerToQuat); - - function MathQuatToEuler() { - this.addInput(["quat", "quat"]); - this.addOutput("euler", "vec3"); - this._value = vec3.create(); - } - - MathQuatToEuler.title = "Euler->Quat"; - MathQuatToEuler.desc = "Converts rotX,rotY,rotZ in degrees to quat"; - - MathQuatToEuler.prototype.onExecute = function() { - var q = this.getInputData(0); - if(!q) - return; - var R = quat.toEuler(this._value, q); - vec3.scale( this._value, this._value, DEG2RAD ); - this.setOutputData(0, this._value); - }; - - LiteGraph.registerNodeType("math3d/quat_to_euler", MathQuatToEuler); - - - //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"); - - this._value = quat.create(); - } - - 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(this._value, A, B); - this.setOutputData(0, R); - }; - - LiteGraph.registerNodeType("math3d/mult-quat", Math3DMultQuat); - - function Math3DQuatSlerp() { - this.addInputs([ - ["A", "quat"], - ["B", "quat"], - ["factor", "number"] - ]); - this.addOutput("slerp", "quat"); - this.addProperty("factor", 0.5); - - this._value = quat.create(); - } - - Math3DQuatSlerp.title = "Quat Slerp"; - Math3DQuatSlerp.desc = "quaternion spherical interpolation"; - - Math3DQuatSlerp.prototype.onExecute = function() { - var A = this.getInputData(0); - if (A == null) { - return; - } - var B = this.getInputData(1); - if (B == null) { - return; - } - var factor = this.properties.factor; - if (this.getInputData(2) != null) { - factor = this.getInputData(2); - } - - var R = quat.slerp(this._value, A, B, factor); - this.setOutputData(0, R); - }; - - LiteGraph.registerNodeType("math3d/quat-slerp", Math3DQuatSlerp); - - - //Math3D rotate vec3 - function Math3DRemapRange() { - this.addInput("vec3", "vec3"); - this.addOutput("remap", "vec3"); - this.addOutput("clamped", "vec3"); - this.properties = { clamp: true, range_min: [-1, -1, 0], range_max: [1, 1, 0], target_min: [-1,-1,0], target_max:[1,1,0] }; - this._value = vec3.create(); - this._clamped = vec3.create(); - } - - Math3DRemapRange.title = "Remap Range"; - Math3DRemapRange.desc = "remap a 3D range"; - - Math3DRemapRange.prototype.onExecute = function() { - var vec = this.getInputData(0); - if(vec) - this._value.set(vec); - var range_min = this.properties.range_min; - var range_max = this.properties.range_max; - var target_min = this.properties.target_min; - var target_max = this.properties.target_max; - - //swap to avoid errors - /* - if(range_min > range_max) - { - range_min = range_max; - range_max = this.properties.range_min; - } - - if(target_min > target_max) - { - target_min = target_max; - target_max = this.properties.target_min; - } - */ - - for(var i = 0; i < 3; ++i) - { - var r = range_max[i] - range_min[i]; - this._clamped[i] = clamp( this._value[i], range_min[i], range_max[i] ); - if(r == 0) - { - this._value[i] = (target_min[i] + target_max[i]) * 0.5; - continue; - } - - var n = (this._value[i] - range_min[i]) / r; - if(this.properties.clamp) - n = clamp(n,0,1); - var t = target_max[i] - target_min[i]; - this._value[i] = target_min[i] + n * t; - } - - this.setOutputData(0,this._value); - this.setOutputData(1,this._clamped); - }; - - LiteGraph.registerNodeType("math3d/remap_range", Math3DRemapRange); - - - - } //glMatrix - else if (LiteGraph.debug) - console.warn("No glmatrix found, some Math3D nodes may not work"); - -})(this); - -//basic nodes -(function(global) { - var LiteGraph = global.LiteGraph; - - function toString(a) { - if(a && a.constructor === Object) - { - try - { - return JSON.stringify(a); - } - catch (err) - { - return String(a); - } - } - return String(a); - } - - LiteGraph.wrapFunctionAsNode("string/toString", toString, [""], "string"); - - function compare(a, b) { - return a == b; - } - - LiteGraph.wrapFunctionAsNode( - "string/compare", - compare, - ["string", "string"], - "boolean" - ); - - function concatenate(a, b) { - if (a === undefined) { - return b; - } - if (b === undefined) { - return a; - } - return a + b; - } - - LiteGraph.wrapFunctionAsNode( - "string/concatenate", - concatenate, - ["string", "string"], - "string" - ); - - function contains(a, b) { - if (a === undefined || b === undefined) { - return false; - } - return a.indexOf(b) != -1; - } - - LiteGraph.wrapFunctionAsNode( - "string/contains", - contains, - ["string", "string"], - "boolean" - ); - - function toUpperCase(a) { - if (a != null && a.constructor === String) { - return a.toUpperCase(); - } - return a; - } - - LiteGraph.wrapFunctionAsNode( - "string/toUpperCase", - toUpperCase, - ["string"], - "string" - ); - - function split(str, separator) { - if(separator == null) - separator = this.properties.separator; - if (str == null ) - return []; - if( str.constructor === String ) - return str.split(separator || " "); - else if( str.constructor === Array ) - { - var r = []; - for(var i = 0; i < str.length; ++i){ - if (typeof str[i] == "string") - r[i] = str[i].split(separator || " "); - } - return r; - } - return null; - } - - LiteGraph.wrapFunctionAsNode( - "string/split", - split, - ["string,array", "string"], - "array", - { separator: "," } - ); - - function toFixed(a) { - if (a != null && a.constructor === Number) { - return a.toFixed(this.properties.precision); - } - return a; - } - - LiteGraph.wrapFunctionAsNode( - "string/toFixed", - toFixed, - ["number"], - "string", - { precision: 0 } - ); - - - function StringToTable() { - this.addInput("", "string"); - this.addOutput("table", "table"); - this.addOutput("rows", "number"); - this.addProperty("value", ""); - this.addProperty("separator", ","); - this._table = null; - } - - StringToTable.title = "toTable"; - StringToTable.desc = "Splits a string to table"; - - StringToTable.prototype.onExecute = function() { - var input = this.getInputData(0); - if(!input) - return; - var separator = this.properties.separator || ","; - if(input != this._str || separator != this._last_separator ) - { - this._last_separator = separator; - this._str = input; - this._table = input.split("\n").map(function(a){ return a.trim().split(separator)}); - } - this.setOutputData(0, this._table ); - this.setOutputData(1, this._table ? this._table.length : 0 ); - }; - - LiteGraph.registerNodeType("string/toTable", StringToTable); - -})(this); - +// widgets (function(global) { var LiteGraph = global.LiteGraph; - function Selector() { - this.addInput("sel", "number"); - this.addInput("A"); - this.addInput("B"); - this.addInput("C"); - this.addInput("D"); - this.addOutput("out"); + /* Button ****************/ - this.selected = 0; + function WidgetButton() { + this.addOutput("", LiteGraph.EVENT); + this.addOutput("", "boolean"); + this.addProperty("text", "click me"); + this.addProperty("font_size", 30); + this.addProperty("message", ""); + this.size = [164, 84]; + this.clicked = false; } - Selector.title = "Selector"; - Selector.desc = "selects an output"; + WidgetButton.title = "Button"; + WidgetButton.desc = "Triggers an event"; - Selector.prototype.onDrawBackground = function(ctx) { + WidgetButton.font = "Arial"; + WidgetButton.prototype.onDrawForeground = function(ctx) { if (this.flags.collapsed) { return; } - ctx.fillStyle = "#AFB"; - var y = (this.selected + 1) * LiteGraph.NODE_SLOT_HEIGHT + 6; - ctx.beginPath(); - ctx.moveTo(50, y); - ctx.lineTo(50, y + LiteGraph.NODE_SLOT_HEIGHT); - ctx.lineTo(34, y + LiteGraph.NODE_SLOT_HEIGHT * 0.5); - ctx.fill(); + var margin = 10; + ctx.fillStyle = "black"; + ctx.fillRect( + margin + 1, + margin + 1, + this.size[0] - margin * 2, + this.size[1] - margin * 2, + ); + ctx.fillStyle = "#AAF"; + ctx.fillRect( + margin - 1, + margin - 1, + this.size[0] - margin * 2, + this.size[1] - margin * 2, + ); + ctx.fillStyle = this.clicked + ? "white" + : this.mouseOver + ? "#668" + : "#334"; + ctx.fillRect( + margin, + margin, + this.size[0] - margin * 2, + this.size[1] - margin * 2, + ); + + if (this.properties.text || this.properties.text === 0) { + var font_size = this.properties.font_size || 30; + ctx.textAlign = "center"; + ctx.fillStyle = this.clicked ? "black" : "white"; + ctx.font = font_size + "px " + WidgetButton.font; + ctx.fillText( + this.properties.text, + this.size[0] * 0.5, + this.size[1] * 0.5 + font_size * 0.3, + ); + ctx.textAlign = "left"; + } }; - Selector.prototype.onExecute = function() { - var sel = this.getInputData(0); - if (sel == null || sel.constructor !== Number) - sel = 0; - this.selected = sel = Math.round(sel) % (this.inputs.length - 1); - var v = this.getInputData(sel + 1); - if (v !== undefined) { - this.setOutputData(0, v); + WidgetButton.prototype.onMouseDown = function(e, local_pos) { + if ( + local_pos[0] > 1 && + local_pos[1] > 1 && + local_pos[0] < this.size[0] - 2 && + local_pos[1] < this.size[1] - 2 + ) { + this.clicked = true; + this.setOutputData(1, this.clicked); + this.triggerSlot(0, this.properties.message); + return true; } }; - Selector.prototype.onGetInputs = function() { - return [["E", 0], ["F", 0], ["G", 0], ["H", 0]]; + WidgetButton.prototype.onExecute = function() { + this.setOutputData(1, this.clicked); }; - LiteGraph.registerNodeType("logic/selector", Selector); + WidgetButton.prototype.onMouseUp = function(e) { + this.clicked = false; + }; - function Sequence() { - this.properties = { - sequence: "A,B,C" - }; - this.addInput("index", "number"); - this.addInput("seq"); - this.addOutput("out"); + LiteGraph.registerNodeType("widget/button", WidgetButton); - this.index = 0; - this.values = this.properties.sequence.split(","); + function WidgetToggle() { + this.addInput("", "boolean"); + this.addInput("e", LiteGraph.ACTION); + this.addOutput("v", "boolean"); + this.addOutput("e", LiteGraph.EVENT); + this.properties = { font: "", value: false }; + this.size = [160, 44]; } - Sequence.title = "Sequence"; - Sequence.desc = "select one element from a sequence from a string"; + WidgetToggle.title = "Toggle"; + WidgetToggle.desc = "Toggles between true or false"; - Sequence.prototype.onPropertyChanged = function(name, value) { - if (name == "sequence") { - this.values = value.split(","); + WidgetToggle.prototype.onDrawForeground = function(ctx) { + if (this.flags.collapsed) { + return; } - }; - Sequence.prototype.onExecute = function() { - var seq = this.getInputData(1); - if (seq && seq != this.current_sequence) { - this.values = seq.split(","); - this.current_sequence = seq; - } - var index = this.getInputData(0); - if (index == null) { - index = 0; - } - this.index = index = Math.round(index) % this.values.length; + var size = this.size[1] * 0.5; + var margin = 0.25; + var h = this.size[1] * 0.8; + ctx.font = this.properties.font || (size * 0.8).toFixed(0) + "px Arial"; + var w = ctx.measureText(this.title).width; + var x = (this.size[0] - (w + size)) * 0.5; - this.setOutputData(0, this.values[index]); + ctx.fillStyle = "#AAA"; + ctx.fillRect(x, h - size, size, size); + + ctx.fillStyle = this.properties.value ? "#AEF" : "#000"; + ctx.fillRect( + x + size * margin, + h - size + size * margin, + size * (1 - margin * 2), + size * (1 - margin * 2), + ); + + ctx.textAlign = "left"; + ctx.fillStyle = "#AAA"; + ctx.fillText(this.title, size * 1.2 + x, h * 0.85); + ctx.textAlign = "left"; }; - LiteGraph.registerNodeType("logic/sequence", Sequence); - - - function logicAnd(){ - this.properties = { }; - this.addInput("a", "boolean"); - this.addInput("b", "boolean"); - this.addOutput("out", "boolean"); - } - logicAnd.title = "AND"; - logicAnd.desc = "Return true if all inputs are true"; - logicAnd.prototype.onExecute = function() { - var ret = true; - for (var inX in this.inputs){ - if (!this.getInputData(inX)){ - var ret = false; - break; - } + WidgetToggle.prototype.onAction = function(action) { + this.properties.value = !this.properties.value; + this.trigger("e", this.properties.value); + }; + + WidgetToggle.prototype.onExecute = function() { + var v = this.getInputData(0); + if (v != null) { + this.properties.value = v; } - this.setOutputData(0, ret); + this.setOutputData(0, this.properties.value); }; - logicAnd.prototype.onGetInputs = function() { - return [ - ["and", "boolean"] + + WidgetToggle.prototype.onMouseDown = function(e, local_pos) { + if ( + local_pos[0] > 1 && + local_pos[1] > 1 && + local_pos[0] < this.size[0] - 2 && + local_pos[1] < this.size[1] - 2 + ) { + this.properties.value = !this.properties.value; + this.graph._version++; + this.trigger("e", this.properties.value); + return true; + } + }; + + LiteGraph.registerNodeType("widget/toggle", WidgetToggle); + + /* Number ****************/ + + function WidgetNumber() { + this.addOutput("", "number"); + this.size = [80, 60]; + this.properties = { min: -1000, max: 1000, value: 1, step: 1 }; + this.old_y = -1; + this._remainder = 0; + this._precision = 0; + this.mouse_captured = false; + } + + WidgetNumber.title = "Number"; + WidgetNumber.desc = "Widget to select number value"; + + WidgetNumber.pixels_threshold = 10; + WidgetNumber.markers_color = "#666"; + + WidgetNumber.prototype.onDrawForeground = function(ctx) { + var x = this.size[0] * 0.5; + var h = this.size[1]; + if (h > 30) { + ctx.fillStyle = WidgetNumber.markers_color; + ctx.beginPath(); + ctx.moveTo(x, h * 0.1); + ctx.lineTo(x + h * 0.1, h * 0.2); + ctx.lineTo(x + h * -0.1, h * 0.2); + ctx.fill(); + ctx.beginPath(); + ctx.moveTo(x, h * 0.9); + ctx.lineTo(x + h * 0.1, h * 0.8); + ctx.lineTo(x + h * -0.1, h * 0.8); + ctx.fill(); + ctx.font = (h * 0.7).toFixed(1) + "px Arial"; + } else { + ctx.font = (h * 0.8).toFixed(1) + "px Arial"; + } + + ctx.textAlign = "center"; + ctx.font = (h * 0.7).toFixed(1) + "px Arial"; + ctx.fillStyle = "#EEE"; + ctx.fillText( + this.properties.value.toFixed(this._precision), + x, + h * 0.75, + ); + }; + + WidgetNumber.prototype.onExecute = function() { + this.setOutputData(0, this.properties.value); + }; + + WidgetNumber.prototype.onPropertyChanged = function(name, value) { + var t = (this.properties.step + "").split("."); + this._precision = t.length > 1 ? t[1].length : 0; + }; + + WidgetNumber.prototype.onMouseDown = function(e, pos) { + if (pos[1] < 0) { + return; + } + + this.old_y = e.canvasY; + this.captureInput(true); + this.mouse_captured = true; + + return true; + }; + + WidgetNumber.prototype.onMouseMove = function(e) { + if (!this.mouse_captured) { + return; + } + + var delta = this.old_y - e.canvasY; + if (e.shiftKey) { + delta *= 10; + } + if (e.metaKey || e.altKey) { + delta *= 0.1; + } + this.old_y = e.canvasY; + + var steps = this._remainder + delta / WidgetNumber.pixels_threshold; + this._remainder = steps % 1; + steps = steps | 0; + + var v = clamp( + this.properties.value + steps * this.properties.step, + this.properties.min, + this.properties.max, + ); + this.properties.value = v; + this.graph._version++; + this.setDirtyCanvas(true); + }; + + WidgetNumber.prototype.onMouseUp = function(e, pos) { + if (e.click_time < 200) { + var steps = pos[1] > this.size[1] * 0.5 ? -1 : 1; + this.properties.value = clamp( + this.properties.value + steps * this.properties.step, + this.properties.min, + this.properties.max, + ); + this.graph._version++; + this.setDirtyCanvas(true); + } + + if (this.mouse_captured) { + this.mouse_captured = false; + this.captureInput(false); + } + }; + + LiteGraph.registerNodeType("widget/number", WidgetNumber); + + + /* Combo ****************/ + + function WidgetCombo() { + this.addOutput("", "string"); + this.addOutput("change", LiteGraph.EVENT); + this.size = [80, 60]; + this.properties = { value: "A", values: "A;B;C" }; + this.old_y = -1; + this.mouse_captured = false; + this._values = this.properties.values.split(";"); + var that = this; + this.widgets_up = true; + this.widget = this.addWidget("combo","", this.properties.value, function(v) { + that.properties.value = v; + that.triggerSlot(1, v); + }, { property: "value", values: this._values } ); + } + + WidgetCombo.title = "Combo"; + WidgetCombo.desc = "Widget to select from a list"; + + WidgetCombo.prototype.onExecute = function() { + this.setOutputData( 0, this.properties.value ); + }; + + WidgetCombo.prototype.onPropertyChanged = function(name, value) { + if(name == "values") { + this._values = value.split(";"); + this.widget.options.values = this._values; + } else if(name == "value") { + this.widget.value = value; + } + }; + + LiteGraph.registerNodeType("widget/combo", WidgetCombo); + + + /* Knob ****************/ + + function WidgetKnob() { + this.addOutput("", "number"); + this.size = [64, 84]; + this.properties = { + min: 0, + max: 1, + value: 0.5, + color: "#7AF", + precision: 2, + }; + this.value = -1; + } + + WidgetKnob.title = "Knob"; + WidgetKnob.desc = "Circular controller"; + WidgetKnob.size = [80, 100]; + + WidgetKnob.prototype.onDrawForeground = function(ctx) { + if (this.flags.collapsed) { + return; + } + + if (this.value == -1) { + this.value = + (this.properties.value - this.properties.min) / + (this.properties.max - this.properties.min); + } + + var center_x = this.size[0] * 0.5; + var center_y = this.size[1] * 0.5; + var radius = Math.min(this.size[0], this.size[1]) * 0.5 - 5; + var w = Math.floor(radius * 0.05); + + ctx.globalAlpha = 1; + ctx.save(); + ctx.translate(center_x, center_y); + ctx.rotate(Math.PI * 0.75); + + // bg + ctx.fillStyle = "rgba(0,0,0,0.5)"; + ctx.beginPath(); + ctx.moveTo(0, 0); + ctx.arc(0, 0, radius, 0, Math.PI * 1.5); + ctx.fill(); + + // value + ctx.strokeStyle = "black"; + ctx.fillStyle = this.properties.color; + ctx.lineWidth = 2; + ctx.beginPath(); + ctx.moveTo(0, 0); + ctx.arc( + 0, + 0, + radius - 4, + 0, + Math.PI * 1.5 * Math.max(0.01, this.value), + ); + ctx.closePath(); + ctx.fill(); + // ctx.stroke(); + ctx.lineWidth = 1; + ctx.globalAlpha = 1; + ctx.restore(); + + // inner + ctx.fillStyle = "black"; + ctx.beginPath(); + ctx.arc(center_x, center_y, radius * 0.75, 0, Math.PI * 2, true); + ctx.fill(); + + // miniball + ctx.fillStyle = this.mouseOver ? "white" : this.properties.color; + ctx.beginPath(); + var angle = this.value * Math.PI * 1.5 + Math.PI * 0.75; + ctx.arc( + center_x + Math.cos(angle) * radius * 0.65, + center_y + Math.sin(angle) * radius * 0.65, + radius * 0.05, + 0, + Math.PI * 2, + true, + ); + ctx.fill(); + + // text + ctx.fillStyle = this.mouseOver ? "white" : "#AAA"; + ctx.font = Math.floor(radius * 0.5) + "px Arial"; + ctx.textAlign = "center"; + ctx.fillText( + this.properties.value.toFixed(this.properties.precision), + center_x, + center_y + radius * 0.15, + ); + }; + + WidgetKnob.prototype.onExecute = function() { + this.setOutputData(0, this.properties.value); + this.boxcolor = LiteGraph.colorToString([ + this.value, + this.value, + this.value, + ]); + }; + + WidgetKnob.prototype.onMouseDown = function(e) { + 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 || + LiteGraph.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); + 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.onPropertyChanged = function(name, value) { + if (name == "min" || name == "max" || name == "value") { + this.properties[name] = parseFloat(value); + return true; // block + } + }; + + LiteGraph.registerNodeType("widget/knob", WidgetKnob); + + // Show value inside the debug console + function WidgetSliderGUI() { + this.addOutput("", "number"); + this.properties = { + value: 0.5, + min: 0, + max: 1, + text: "V", + }; + var that = this; + this.size = [140, 40]; + this.slider = this.addWidget( + "slider", + "V", + this.properties.value, + function(v) { + that.properties.value = v; + }, + this.properties, + ); + this.widgets_up = true; + } + + WidgetSliderGUI.title = "Inner Slider"; + + WidgetSliderGUI.prototype.onPropertyChanged = function(name, value) { + if (name == "value") { + this.slider.value = value; + } + }; + + WidgetSliderGUI.prototype.onExecute = function() { + this.setOutputData(0, this.properties.value); + }; + + LiteGraph.registerNodeType("widget/internal_slider", WidgetSliderGUI); + + // Widget H SLIDER + function WidgetHSlider() { + this.size = [160, 26]; + this.addOutput("", "number"); + this.properties = { color: "#7AF", min: 0, max: 1, value: 0.5 }; + this.value = -1; + } + + WidgetHSlider.title = "H.Slider"; + WidgetHSlider.desc = "Linear slider controller"; + + WidgetHSlider.prototype.onDrawForeground = function(ctx) { + if (this.value == -1) { + this.value = + (this.properties.value - this.properties.min) / + (this.properties.max - this.properties.min); + } + + // border + ctx.globalAlpha = 1; + ctx.lineWidth = 1; + ctx.fillStyle = "#000"; + ctx.fillRect(2, 2, this.size[0] - 4, this.size[1] - 4); + + ctx.fillStyle = this.properties.color; + ctx.beginPath(); + ctx.rect(4, 4, (this.size[0] - 8) * this.value, this.size[1] - 8); + ctx.fill(); + }; + + 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 = LiteGraph.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; + }; + + LiteGraph.registerNodeType("widget/hslider", WidgetHSlider); + + function WidgetProgress() { + this.size = [160, 26]; + this.addInput("", "number"); + this.properties = { min: 0, max: 1, value: 0, color: "#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.color; + 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); + + 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.replace(/[\r\n]/g, "\\n").split("\\n"); + for (var i=0; i < lines.length; i++) { + 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; + } + // 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=0; i < lines.length; i++) { + 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.onPropertyChanged = 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.flags.collapsed) { + return; + } + + 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(); + }; + + LiteGraph.registerNodeType("widget/panel", WidgetPanel); +})(this); + +(function(global) { + var LiteGraph = global.LiteGraph; + + function GamepadInput() { + this.addOutput("left_x_axis", "number"); + this.addOutput("left_y_axis", "number"); + this.addOutput("button_pressed", LiteGraph.EVENT); + this.properties = { gamepad_index: 0, threshold: 0.1 }; + + this._left_axis = new Float32Array(2); + this._right_axis = new Float32Array(2); + this._triggers = new Float32Array(2); + this._previous_buttons = new Uint8Array(17); + this._current_buttons = new Uint8Array(17); + } + + GamepadInput.title = "Gamepad"; + GamepadInput.desc = "gets the input of the gamepad"; + + GamepadInput.CENTER = 0; + GamepadInput.LEFT = 1; + GamepadInput.RIGHT = 2; + GamepadInput.UP = 4; + GamepadInput.DOWN = 8; + + GamepadInput.zero = new Float32Array(2); + GamepadInput.buttons = [ + "a", + "b", + "x", + "y", + "lb", + "rb", + "lt", + "rt", + "back", + "start", + "ls", + "rs", + "home", + ]; + + GamepadInput.prototype.onExecute = function() { + // get gamepad + var gamepad = this.getGamepad(); + var threshold = this.properties.threshold || 0.0; + + if (gamepad) { + this._left_axis[0] = + Math.abs(gamepad.xbox.axes["lx"]) > threshold + ? gamepad.xbox.axes["lx"] + : 0; + this._left_axis[1] = + Math.abs(gamepad.xbox.axes["ly"]) > threshold + ? gamepad.xbox.axes["ly"] + : 0; + this._right_axis[0] = + Math.abs(gamepad.xbox.axes["rx"]) > threshold + ? gamepad.xbox.axes["rx"] + : 0; + this._right_axis[1] = + Math.abs(gamepad.xbox.axes["ry"]) > threshold + ? gamepad.xbox.axes["ry"] + : 0; + this._triggers[0] = + Math.abs(gamepad.xbox.axes["ltrigger"]) > threshold + ? gamepad.xbox.axes["ltrigger"] + : 0; + this._triggers[1] = + Math.abs(gamepad.xbox.axes["rtrigger"]) > threshold + ? gamepad.xbox.axes["rtrigger"] + : 0; + } + + if (this.outputs) { + for (var i = 0; i < this.outputs.length; i++) { + var output = this.outputs[i]; + if (!output.links || !output.links.length) { + continue; + } + var v = null; + + if (gamepad) { + switch (output.name) { + case "left_axis": + v = this._left_axis; + break; + case "right_axis": + v = this._right_axis; + break; + case "left_x_axis": + v = this._left_axis[0]; + break; + case "left_y_axis": + v = this._left_axis[1]; + break; + case "right_x_axis": + v = this._right_axis[0]; + break; + case "right_y_axis": + v = this._right_axis[1]; + break; + case "trigger_left": + v = this._triggers[0]; + break; + case "trigger_right": + v = this._triggers[1]; + break; + case "a_button": + v = gamepad.xbox.buttons["a"] ? 1 : 0; + break; + case "b_button": + v = gamepad.xbox.buttons["b"] ? 1 : 0; + break; + case "x_button": + v = gamepad.xbox.buttons["x"] ? 1 : 0; + break; + case "y_button": + v = gamepad.xbox.buttons["y"] ? 1 : 0; + break; + case "lb_button": + v = gamepad.xbox.buttons["lb"] ? 1 : 0; + break; + case "rb_button": + v = gamepad.xbox.buttons["rb"] ? 1 : 0; + break; + case "ls_button": + v = gamepad.xbox.buttons["ls"] ? 1 : 0; + break; + case "rs_button": + v = gamepad.xbox.buttons["rs"] ? 1 : 0; + break; + case "hat_left": + v = gamepad.xbox.hatmap & GamepadInput.LEFT; + break; + case "hat_right": + v = gamepad.xbox.hatmap & GamepadInput.RIGHT; + break; + case "hat_up": + v = gamepad.xbox.hatmap & GamepadInput.UP; + break; + case "hat_down": + v = gamepad.xbox.hatmap & GamepadInput.DOWN; + break; + case "hat": + v = gamepad.xbox.hatmap; + break; + case "start_button": + v = gamepad.xbox.buttons["start"] ? 1 : 0; + break; + case "back_button": + v = gamepad.xbox.buttons["back"] ? 1 : 0; + break; + case "button_pressed": + for ( + var j = 0; + j < this._current_buttons.length; + ++j + ) { + if ( + this._current_buttons[j] && + !this._previous_buttons[j] + ) { + this.triggerSlot( + i, + GamepadInput.buttons[j], + ); + } + } + break; + default: + break; + } + } else { + // if no gamepad is connected, output 0 + switch (output.name) { + case "button_pressed": + break; + case "left_axis": + case "right_axis": + v = GamepadInput.zero; + break; + default: + v = 0; + } + } + this.setOutputData(i, v); + } + } + }; + + GamepadInput.mapping = {a: 0,b: 1,x: 2,y: 3,lb: 4,rb: 5,lt: 6,rt: 7,back: 8,start: 9,ls: 10,rs: 11 }; + GamepadInput.mapping_array = ["a","b","x","y","lb","rb","lt","rt","back","start","ls","rs"]; + + GamepadInput.prototype.getGamepad = function() { + var getGamepads = + navigator.getGamepads || + navigator.webkitGetGamepads || + navigator.mozGetGamepads; + if (!getGamepads) { + return null; + } + var gamepads = getGamepads.call(navigator); + var gamepad = null; + + this._previous_buttons.set(this._current_buttons); + + // pick the first connected + for (var i = this.properties.gamepad_index; i < 4; i++) { + if (!gamepads[i]) { + continue; + } + gamepad = gamepads[i]; + + // xbox controller mapping + var xbox = this.xbox_mapping; + if (!xbox) { + xbox = this.xbox_mapping = { + axes: [], + buttons: {}, + hat: "", + hatmap: GamepadInput.CENTER, + }; + } + + xbox.axes["lx"] = gamepad.axes[0]; + xbox.axes["ly"] = gamepad.axes[1]; + xbox.axes["rx"] = gamepad.axes[2]; + xbox.axes["ry"] = gamepad.axes[3]; + xbox.axes["ltrigger"] = gamepad.buttons[6].value; + xbox.axes["rtrigger"] = gamepad.buttons[7].value; + xbox.hat = ""; + xbox.hatmap = GamepadInput.CENTER; + + for (var j = 0; j < gamepad.buttons.length; j++) { + this._current_buttons[j] = gamepad.buttons[j].pressed; + + if(j < 12) { + xbox.buttons[GamepadInput.mapping_array[j]] = gamepad.buttons[j].pressed; + if(gamepad.buttons[j].was_pressed) + this.trigger( GamepadInput.mapping_array[j] + "_button_event" ); + } else // mapping of XBOX + switch ( j ) { // I use a switch to ensure that a player with another gamepad could play + case 12: + if (gamepad.buttons[j].pressed) { + xbox.hat += "up"; + xbox.hatmap |= GamepadInput.UP; + } + break; + case 13: + if (gamepad.buttons[j].pressed) { + xbox.hat += "down"; + xbox.hatmap |= GamepadInput.DOWN; + } + break; + case 14: + if (gamepad.buttons[j].pressed) { + xbox.hat += "left"; + xbox.hatmap |= GamepadInput.LEFT; + } + break; + case 15: + if (gamepad.buttons[j].pressed) { + xbox.hat += "right"; + xbox.hatmap |= GamepadInput.RIGHT; + } + break; + case 16: + xbox.buttons["home"] = gamepad.buttons[j].pressed; + break; + default: + } + } + gamepad.xbox = xbox; + return gamepad; + } + }; + + GamepadInput.prototype.onDrawBackground = function(ctx) { + if (this.flags.collapsed) { + return; + } + + // render gamepad state? + var la = this._left_axis; + var ra = this._right_axis; + ctx.strokeStyle = "#88A"; + ctx.strokeRect( + (la[0] + 1) * 0.5 * this.size[0] - 4, + (la[1] + 1) * 0.5 * this.size[1] - 4, + 8, + 8, + ); + ctx.strokeStyle = "#8A8"; + ctx.strokeRect( + (ra[0] + 1) * 0.5 * this.size[0] - 4, + (ra[1] + 1) * 0.5 * this.size[1] - 4, + 8, + 8, + ); + var h = this.size[1] / this._current_buttons.length; + ctx.fillStyle = "#AEB"; + for (var i = 0; i < this._current_buttons.length; ++i) { + if (this._current_buttons[i]) { + ctx.fillRect(0, h * i, 6, h); + } + } + }; + + GamepadInput.prototype.onGetOutputs = function() { + return [ + ["left_axis", "vec2"], + ["right_axis", "vec2"], + ["left_x_axis", "number"], + ["left_y_axis", "number"], + ["right_x_axis", "number"], + ["right_y_axis", "number"], + ["trigger_left", "number"], + ["trigger_right", "number"], + ["a_button", "number"], + ["b_button", "number"], + ["x_button", "number"], + ["y_button", "number"], + ["lb_button", "number"], + ["rb_button", "number"], + ["ls_button", "number"], + ["rs_button", "number"], + ["start_button", "number"], + ["back_button", "number"], + ["a_button_event", LiteGraph.EVENT ], + ["b_button_event", LiteGraph.EVENT ], + ["x_button_event", LiteGraph.EVENT ], + ["y_button_event", LiteGraph.EVENT ], + ["lb_button_event", LiteGraph.EVENT ], + ["rb_button_event", LiteGraph.EVENT ], + ["ls_button_event", LiteGraph.EVENT ], + ["rs_button_event", LiteGraph.EVENT ], + ["start_button_event", LiteGraph.EVENT ], + ["back_button_event", LiteGraph.EVENT ], + ["hat_left", "number"], + ["hat_right", "number"], + ["hat_up", "number"], + ["hat_down", "number"], + ["hat", "number"], + ["button_pressed", LiteGraph.EVENT], + ]; + }; + + LiteGraph.registerNodeType("input/gamepad", GamepadInput); + +})(this); + +(function(global) { + var LiteGraph = global.LiteGraph; + + // Converter + function Converter() { + this.addInput("in", 0); + this.addOutput("out", 0); + this.size = [80, 30]; + } + + Converter.title = "Converter"; + Converter.desc = "type A to type B"; + + Converter.prototype.onExecute = function() { + var v = this.getInputData(0); + if (v == null) { + return; + } + + if (this.outputs) { + for (var i = 0; i < this.outputs.length; i++) { + var output = this.outputs[i]; + if (!output.links || !output.links.length) { + continue; + } + + var result = null; + switch (output.name) { + case "number": + result = v.length ? v[0] : parseFloat(v); + break; + case "vec2": + case "vec3": + case "vec4": + var result = null; + var count = 1; + switch (output.name) { + case "vec2": + count = 2; + break; + case "vec3": + count = 3; + break; + case "vec4": + count = 4; + break; + } + + var result = new Float32Array(count); + if (v.length) { + for ( + var j = 0; + j < v.length && j < result.length; + j++ + ) { + result[j] = v[j]; + } + } else { + result[0] = parseFloat(v); + } + break; + } + this.setOutputData(i, result); + } + } + }; + + Converter.prototype.onGetOutputs = function() { + return [ + ["number", "number"], + ["vec2", "vec2"], + ["vec3", "vec3"], + ["vec4", "vec4"], + ]; + }; + + LiteGraph.registerNodeType("math/converter", Converter); + + // Bypass + function Bypass() { + this.addInput("in"); + this.addOutput("out"); + this.size = [80, 30]; + } + + Bypass.title = "Bypass"; + Bypass.desc = "removes the type"; + + Bypass.prototype.onExecute = function() { + var v = this.getInputData(0); + this.setOutputData(0, v); + }; + + LiteGraph.registerNodeType("math/bypass", Bypass); + + function ToNumber() { + this.addInput("in"); + this.addOutput("out"); + } + + ToNumber.title = "to Number"; + ToNumber.desc = "Cast to number"; + + ToNumber.prototype.onExecute = function() { + var v = this.getInputData(0); + this.setOutputData(0, Number(v)); + }; + + LiteGraph.registerNodeType("math/to_number", ToNumber); + + function MathRange() { + this.addInput("in", "number", { locked: true }); + this.addOutput("out", "number", { locked: true }); + this.addOutput("clamped", "number", { locked: true }); + + this.addProperty("in", 0); + this.addProperty("in_min", 0); + this.addProperty("in_max", 1); + this.addProperty("out_min", 0); + this.addProperty("out_max", 1); + + this.size = [120, 50]; + } + + MathRange.title = "Range"; + MathRange.desc = "Convert a number from one range to another"; + + MathRange.prototype.getTitle = function() { + if (this.flags.collapsed) { + return (this._last_v || 0).toFixed(2); + } + return this.title; + }; + + MathRange.prototype.onExecute = function() { + if (this.inputs) { + for (var i = 0; i < this.inputs.length; i++) { + var input = this.inputs[i]; + var v = this.getInputData(i); + if (v === undefined) { + continue; + } + this.properties[input.name] = v; + } + } + + var v = this.properties["in"]; + if (v === undefined || v === null || v.constructor !== Number) { + v = 0; + } + + var in_min = this.properties.in_min; + var in_max = this.properties.in_max; + var out_min = this.properties.out_min; + var out_max = this.properties.out_max; + /* + if( in_min > in_max ) + { + in_min = in_max; + in_max = this.properties.in_min; + } + if( out_min > out_max ) + { + out_min = out_max; + out_max = this.properties.out_min; + } + */ + + this._last_v = ((v - in_min) / (in_max - in_min)) * (out_max - out_min) + out_min; + this.setOutputData(0, this._last_v); + this.setOutputData(1, clamp( this._last_v, out_min, out_max )); + }; + + MathRange.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 = "?"; + } + }; + + MathRange.prototype.onGetInputs = function() { + return [ + ["in_min", "number"], + ["in_max", "number"], + ["out_min", "number"], + ["out_max", "number"], + ]; + }; + + LiteGraph.registerNodeType("math/range", MathRange); + + function MathRand() { + this.addOutput("value", "number"); + this.addProperty("min", 0); + this.addProperty("max", 1); + this.size = [80, 30]; + } + + MathRand.title = "Rand"; + MathRand.desc = "Random number"; + + MathRand.prototype.onExecute = function() { + if (this.inputs) { + for (var i = 0; i < this.inputs.length; i++) { + var input = this.inputs[i]; + var v = this.getInputData(i); + if (v === undefined) { + continue; + } + this.properties[input.name] = v; + } + } + + 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 + this.outputs[0].label = (this._last_v || 0).toFixed(3); + }; + + MathRand.prototype.onGetInputs = function() { + return [["min", "number"], ["max", "number"]]; + }; + + LiteGraph.registerNodeType("math/rand", MathRand); + + // basic continuous noise + function MathNoise() { + this.addInput("in", "number"); + this.addOutput("out", "number"); + this.addProperty("min", 0); + this.addProperty("max", 1); + this.addProperty("smooth", true); + this.addProperty("seed", 0); + this.addProperty("octaves", 1); + this.addProperty("persistence", 0.8); + this.addProperty("speed", 1); + this.size = [90, 30]; + } + + MathNoise.title = "Noise"; + MathNoise.desc = "Random number with temporal continuity"; + MathNoise.data = null; + + MathNoise.getValue = function(f, smooth) { + if (!MathNoise.data) { + MathNoise.data = new Float32Array(1024); + for (var i = 0; i < MathNoise.data.length; ++i) { + MathNoise.data[i] = Math.random(); + } + } + f = f % 1024; + if (f < 0) { + f += 1024; + } + var f_min = Math.floor(f); + var f = f - f_min; + var r1 = MathNoise.data[f_min]; + var r2 = MathNoise.data[f_min == 1023 ? 0 : f_min + 1]; + if (smooth) { + f = f * f * f * (f * (f * 6.0 - 15.0) + 10.0); + } + return r1 * (1 - f) + r2 * f; + }; + + MathNoise.prototype.onExecute = function() { + var f = this.getInputData(0) || 0; + var iterations = this.properties.octaves || 1; + var r = 0; + var amp = 1; + var seed = this.properties.seed || 0; + f += seed; + var speed = this.properties.speed || 1; + var total_amp = 0; + for(var i = 0; i < iterations; ++i) { + r += MathNoise.getValue(f * (1+i) * speed, this.properties.smooth) * amp; + total_amp += amp; + amp *= this.properties.persistence; + if(amp < 0.001) + break; + } + r /= total_amp; + var min = this.properties.min; + var max = this.properties.max; + this._last_v = r * (max - min) + min; + this.setOutputData(0, this._last_v); + }; + + MathNoise.prototype.onDrawBackground = function(ctx) { + // show the current value + this.outputs[0].label = (this._last_v || 0).toFixed(3); + }; + + LiteGraph.registerNodeType("math/noise", MathNoise); + + // generates spikes every random time + function MathSpikes() { + this.addOutput("out", "number"); + this.addProperty("min_time", 1); + this.addProperty("max_time", 2); + this.addProperty("duration", 0.2); + this.size = [90, 30]; + this._remaining_time = 0; + this._blink_time = 0; + } + + MathSpikes.title = "Spikes"; + MathSpikes.desc = "spike every random time"; + + MathSpikes.prototype.onExecute = function() { + var dt = this.graph.elapsed_time; // in secs + + this._remaining_time -= dt; + this._blink_time -= dt; + + var v = 0; + if (this._blink_time > 0) { + var f = this._blink_time / this.properties.duration; + v = 1 / (Math.pow(f * 8 - 4, 4) + 1); + } + + if (this._remaining_time < 0) { + this._remaining_time = + Math.random() * + (this.properties.max_time - this.properties.min_time) + + this.properties.min_time; + this._blink_time = this.properties.duration; + this.boxcolor = "#FFF"; + } else { + this.boxcolor = "#000"; + } + this.setOutputData(0, v); + }; + + LiteGraph.registerNodeType("math/spikes", MathSpikes); + + // Math clamp + function MathClamp() { + this.addInput("in", "number"); + this.addOutput("out", "number"); + this.size = [80, 30]; + this.addProperty("min", 0); + this.addProperty("max", 1); + } + + MathClamp.title = "Clamp"; + MathClamp.desc = "Clamp number between min and max"; + // MathClamp.filter = "shader"; + + 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); + }; + + MathClamp.prototype.getCode = function(lang) { + var code = ""; + if (this.isInputConnected(0)) { + code += + "clamp({{0}}," + + this.properties.min + + "," + + this.properties.max + + ")"; + } + return code; + }; + + LiteGraph.registerNodeType("math/clamp", MathClamp); + + // Math ABS + function MathLerp() { + this.properties = { f: 0.5 }; + this.addInput("A", "number"); + this.addInput("B", "number"); + + this.addOutput("out", "number"); + } + + MathLerp.title = "Lerp"; + MathLerp.desc = "Linear Interpolation"; + + MathLerp.prototype.onExecute = function() { + var v1 = this.getInputData(0); + if (v1 == null) { + v1 = 0; + } + var v2 = this.getInputData(1); + if (v2 == null) { + v2 = 0; + } + + var f = this.properties.f; + + var _f = this.getInputData(2); + if (_f !== undefined) { + f = _f; + } + + this.setOutputData(0, v1 * (1 - f) + v2 * f); + }; + + MathLerp.prototype.onGetInputs = function() { + return [["f", "number"]]; + }; + + LiteGraph.registerNodeType("math/lerp", MathLerp); + + // Math ABS + function MathAbs() { + this.addInput("in", "number"); + this.addOutput("out", "number"); + this.size = [80, 30]; + } + + 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 = [80, 30]; + } + + 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, Math.floor(v)); + }; + + LiteGraph.registerNodeType("math/floor", MathFloor); + + // Math frac + function MathFrac() { + this.addInput("in", "number"); + this.addOutput("out", "number"); + this.size = [80, 30]; + } + + 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 Floor + function MathSmoothStep() { + this.addInput("in", "number"); + this.addOutput("out", "number"); + this.size = [80, 30]; + this.properties = { A: 0, B: 1 }; + } + + MathSmoothStep.title = "Smoothstep"; + MathSmoothStep.desc = "Smoothstep"; + + MathSmoothStep.prototype.onExecute = function() { + var v = this.getInputData(0); + if (v === undefined) { + return; + } + + var edge0 = this.properties.A; + var edge1 = this.properties.B; + + // Scale, bias and saturate x to 0..1 range + v = clamp((v - edge0) / (edge1 - edge0), 0.0, 1.0); + // Evaluate polynomial + v = v * v * (3 - 2 * v); + + this.setOutputData(0, v); + }; + + LiteGraph.registerNodeType("math/smoothstep", MathSmoothStep); + + // Math scale + function MathScale() { + this.addInput("in", "number", { label: "" }); + this.addOutput("out", "number", { label: "" }); + this.size = [80, 30]; + this.addProperty("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); + + // Gate + function Gate() { + this.addInput("v","boolean"); + this.addInput("A"); + this.addInput("B"); + this.addOutput("out"); + } + + Gate.title = "Gate"; + Gate.desc = "if v is true, then outputs A, otherwise B"; + + Gate.prototype.onExecute = function() { + var v = this.getInputData(0); + this.setOutputData(0, this.getInputData( v ? 1 : 2 )); + }; + + LiteGraph.registerNodeType("math/gate", Gate); + + + // Math Average + function MathAverageFilter() { + this.addInput("in", "number"); + this.addOutput("out", "number"); + this.size = [80, 30]; + this.addProperty("samples", 10); + this._values = new Float32Array(10); + this._current = 0; + } + + MathAverageFilter.title = "Average"; + MathAverageFilter.desc = "Average Filter"; + + MathAverageFilter.prototype.onExecute = function() { + var v = this.getInputData(0); + if (v == null) { + v = 0; + } + + var num_samples = this._values.length; + + this._values[this._current % num_samples] = v; + this._current += 1; + if (this._current > num_samples) { + this._current = 0; + } + + var avr = 0; + for (var i = 0; i < num_samples; ++i) { + avr += this._values[i]; + } + + this.setOutputData(0, avr / num_samples); + }; + + MathAverageFilter.prototype.onPropertyChanged = function(name, value) { + if (value < 1) { + value = 1; + } + this.properties.samples = Math.round(value); + var old = this._values; + + this._values = new Float32Array(this.properties.samples); + if (old.length <= this._values.length) { + this._values.set(old); + } else { + this._values.set(old.subarray(0, this._values.length)); + } + }; + + LiteGraph.registerNodeType("math/average", MathAverageFilter); + + // Math + function MathTendTo() { + this.addInput("in", "number"); + this.addOutput("out", "number"); + this.addProperty("factor", 0.1); + this.size = [80, 30]; + this._value = null; + } + + MathTendTo.title = "TendTo"; + MathTendTo.desc = "moves the output value always closer to the input"; + + MathTendTo.prototype.onExecute = function() { + var v = this.getInputData(0); + if (v == null) { + v = 0; + } + var f = this.properties.factor; + if (this._value == null) { + this._value = v; + } else { + this._value = this._value * (1 - f) + v * f; + } + this.setOutputData(0, this._value); + }; + + LiteGraph.registerNodeType("math/tendTo", MathTendTo); + + // Math operation + function MathOperation() { + this.addInput("A", "number,array,object"); + this.addInput("B", "number"); + this.addOutput("=", "number"); + this.addProperty("A", 1); + this.addProperty("B", 1); + this.addProperty("OP", "+", "enum", { values: MathOperation.values }); + this._func = MathOperation.funcs[this.properties.OP]; + this._result = []; // only used for arrays + } + + MathOperation.values = ["+", "-", "*", "/", "%", "^", "max", "min"]; + MathOperation.funcs = { + "+": function(A,B) { + return A + B; + }, + "-": function(A,B) { + return A - B; + }, + "x": function(A,B) { + return A * B; + }, + "X": function(A,B) { + return A * B; + }, + "*": function(A,B) { + return A * B; + }, + "/": function(A,B) { + return A / B; + }, + "%": function(A,B) { + return A % B; + }, + "^": function(A,B) { + return Math.pow(A, B); + }, + "max": function(A,B) { + return Math.max(A, B); + }, + "min": function(A,B) { + return Math.min(A, B); + }, + }; + + MathOperation.title = "Operation"; + MathOperation.desc = "Easy math operators"; + MathOperation["@OP"] = { + type: "enum", + title: "operation", + values: MathOperation.values, + }; + MathOperation.size = [100, 60]; + + MathOperation.prototype.getTitle = function() { + if(this.properties.OP == "max" || this.properties.OP == "min") + return this.properties.OP + "(A,B)"; + return "A " + this.properties.OP + " B"; + }; + + MathOperation.prototype.setValue = function(v) { + if (typeof v == "string") { + v = parseFloat(v); + } + this.properties["value"] = v; + }; + + MathOperation.prototype.onPropertyChanged = function(name, value) { + if (name != "OP") + return; + this._func = MathOperation.funcs[this.properties.OP]; + if(!this._func) { + console.warn("Unknown operation: " + this.properties.OP); + this._func = function(A) { + return A; + }; + } + } + + MathOperation.prototype.onExecute = function() { + var A = this.getInputData(0); + var B = this.getInputData(1); + if ( A != null ) { + if( A.constructor === Number ) + this.properties["A"] = A; + } else { + A = this.properties["A"]; + } + + if (B != null) { + this.properties["B"] = B; + } else { + B = this.properties["B"]; + } + + var func = MathOperation.funcs[this.properties.OP]; + + var result; + if(A.constructor === Number) { + result = 0; + result = func(A,B); + } else if(A.constructor === Array) { + result = this._result; + result.length = A.length; + for(var i = 0; i < A.length; ++i) + result[i] = func(A[i],B); + } else { + result = {}; + for(var i in A) + result[i] = func(A[i],B); + } + this.setOutputData(0, result); + }; + + MathOperation.prototype.onDrawBackground = function(ctx) { + if (this.flags.collapsed) { + return; + } + + ctx.font = "40px Arial"; + ctx.fillStyle = "#666"; + ctx.textAlign = "center"; + ctx.fillText( + this.properties.OP, + this.size[0] * 0.5, + (this.size[1] + LiteGraph.NODE_TITLE_HEIGHT) * 0.5, + ); + ctx.textAlign = "left"; + }; + + LiteGraph.registerNodeType("math/operation", MathOperation); + + LiteGraph.registerSearchboxExtra("math/operation", "MAX", { + properties: {OP: "max"}, + title: "MAX()", + }); + + LiteGraph.registerSearchboxExtra("math/operation", "MIN", { + properties: {OP: "min"}, + title: "MIN()", + }); + + + // Math compare + function MathCompare() { + this.addInput("A", "number"); + this.addInput("B", "number"); + this.addOutput("A==B", "boolean"); + this.addOutput("A!=B", "boolean"); + this.addProperty("A", 0); + this.addProperty("B", 0); + } + + 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 !== undefined) { + this.properties["A"] = A; + } else { + A = this.properties["A"]; + } + + if (B !== undefined) { + 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; + 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", "boolean"], + ["A!=B", "boolean"], + ["A>B", "boolean"], + ["A=B", "boolean"], + ["A<=B", "boolean"], + ]; + }; + + LiteGraph.registerNodeType("math/compare", MathCompare); + + LiteGraph.registerSearchboxExtra("math/compare", "==", { + outputs: [["A==B", "boolean"]], + title: "A==B", + }); + LiteGraph.registerSearchboxExtra("math/compare", "!=", { + outputs: [["A!=B", "boolean"]], + title: "A!=B", + }); + LiteGraph.registerSearchboxExtra("math/compare", ">", { + outputs: [["A>B", "boolean"]], + title: "A>B", + }); + LiteGraph.registerSearchboxExtra("math/compare", "<", { + outputs: [["A=", { + outputs: [["A>=B", "boolean"]], + title: "A>=B", + }); + LiteGraph.registerSearchboxExtra("math/compare", "<=", { + outputs: [["A<=B", "boolean"]], + title: "A<=B", + }); + + function MathCondition() { + this.addInput("A", "number"); + this.addInput("B", "number"); + this.addOutput("true", "boolean"); + this.addOutput("false", "boolean"); + this.addProperty("A", 1); + this.addProperty("B", 1); + this.addProperty("OP", ">", "enum", { values: MathCondition.values }); + this.addWidget("combo","Cond.",this.properties.OP,{ property: "OP", values: MathCondition.values } ); + + this.size = [80, 60]; + } + + MathCondition.values = [">", "<", "==", "!=", "<=", ">=", "||", "&&" ]; + MathCondition["@OP"] = { + type: "enum", + title: "operation", + values: MathCondition.values, + }; + + MathCondition.title = "Condition"; + MathCondition.desc = "evaluates condition between A and B"; + + MathCondition.prototype.getTitle = function() { + return "A " + this.properties.OP + " B"; + }; + + MathCondition.prototype.onExecute = function() { + var A = this.getInputData(0); + if (A === undefined) { + A = this.properties.A; + } else { + this.properties.A = A; + } + + var B = this.getInputData(1); + if (B === undefined) { + B = this.properties.B; + } else { + this.properties.B = B; + } + + var result = true; + switch (this.properties.OP) { + case ">": + result = A > B; + break; + case "<": + result = A < B; + break; + case "==": + result = A == B; + break; + case "!=": + result = A != B; + break; + case "<=": + result = A <= B; + break; + case ">=": + result = A >= B; + break; + case "||": + result = A || B; + break; + case "&&": + result = A && B; + break; + } + + this.setOutputData(0, result); + this.setOutputData(1, !result); + }; + + LiteGraph.registerNodeType("math/condition", MathCondition); + + + function MathBranch() { + this.addInput("in", 0); + this.addInput("cond", "boolean"); + this.addOutput("true", 0); + this.addOutput("false", 0); + this.size = [80, 60]; + } + + MathBranch.title = "Branch"; + MathBranch.desc = "If condition is true, outputs IN in true, otherwise in false"; + + MathBranch.prototype.onExecute = function() { + var V = this.getInputData(0); + var cond = this.getInputData(1); + + if(cond) { + this.setOutputData(0, V); + this.setOutputData(1, null); + } else { + this.setOutputData(0, null); + this.setOutputData(1, V); + } + } + + LiteGraph.registerNodeType("math/branch", MathBranch); + + + function MathAccumulate() { + this.addInput("inc", "number"); + this.addOutput("total", "number"); + this.addProperty("increment", 1); + this.addProperty("value", 0); + } + + MathAccumulate.title = "Accumulate"; + MathAccumulate.desc = "Increments a value every time"; + + MathAccumulate.prototype.onExecute = function() { + if (this.properties.value === null) { + this.properties.value = 0; + } + + var inc = this.getInputData(0); + if (inc !== null) { + this.properties.value += inc; + } else { + this.properties.value += this.properties.increment; + } + this.setOutputData(0, this.properties.value); + }; + + LiteGraph.registerNodeType("math/accumulate", MathAccumulate); + + // Math Trigonometry + function MathTrigonometry() { + this.addInput("v", "number"); + this.addOutput("sin", "number"); + + this.addProperty("amplitude", 1); + this.addProperty("offset", 0); + this.bgImageUrl = "nodes/imgs/icon-sin.png"; + } + + MathTrigonometry.title = "Trigonometry"; + MathTrigonometry.desc = "Sin Cos Tan"; + // MathTrigonometry.filter = "shader"; + + MathTrigonometry.prototype.onExecute = function() { + var v = this.getInputData(0); + if (v == null) { + v = 0; + } + var amplitude = this.properties["amplitude"]; + var slot = this.findInputSlot("amplitude"); + if (slot != -1) { + amplitude = this.getInputData(slot); + } + var offset = this.properties["offset"]; + slot = this.findInputSlot("offset"); + if (slot != -1) { + offset = this.getInputData(slot); + } + + for (var i = 0, l = this.outputs.length; i < l; ++i) { + var output = this.outputs[i]; + var value; + 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, amplitude * value + offset); + } + }; + + MathTrigonometry.prototype.onGetInputs = function() { + return [["v", "number"], ["amplitude", "number"], ["offset", "number"]]; + }; + + MathTrigonometry.prototype.onGetOutputs = function() { + return [ + ["sin", "number"], + ["cos", "number"], + ["tan", "number"], + ["asin", "number"], + ["acos", "number"], + ["atan", "number"], + ]; + }; + + LiteGraph.registerNodeType("math/trigonometry", MathTrigonometry); + + LiteGraph.registerSearchboxExtra("math/trigonometry", "SIN()", { + outputs: [["sin", "number"]], + title: "SIN()", + }); + LiteGraph.registerSearchboxExtra("math/trigonometry", "COS()", { + outputs: [["cos", "number"]], + title: "COS()", + }); + LiteGraph.registerSearchboxExtra("math/trigonometry", "TAN()", { + outputs: [["tan", "number"]], + title: "TAN()", + }); + + // math library for safe math operations without eval + function MathFormula() { + this.addInput("x", "number"); + this.addInput("y", "number"); + this.addOutput("", "number"); + this.properties = { x: 1.0, y: 1.0, formula: "x+y" }; + this.code_widget = this.addWidget( + "text", + "F(x,y)", + this.properties.formula, + function(v, canvas, node) { + node.properties.formula = v; + }, + ); + this.addWidget("toggle", "allow", LiteGraph.allow_scripts, function(v) { + LiteGraph.allow_scripts = v; + }); + this._func = null; + } + + MathFormula.title = "Formula"; + MathFormula.desc = "Compute formula"; + MathFormula.size = [160, 100]; + + MathAverageFilter.prototype.onPropertyChanged = function(name, value) { + if (name == "formula") { + this.code_widget.value = value; + } + }; + + MathFormula.prototype.onExecute = function() { + if (!LiteGraph.allow_scripts) { + return; + } + + 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; + try { + if (!this._func || this._func_code != this.properties.formula) { + this._func = new Function( + "x", + "y", + "TIME", + "return " + this.properties.formula, + ); + this._func_code = this.properties.formula; + } + value = this._func(x, y, this.graph.globaltime); + this.boxcolor = null; + } catch (err) { + this.boxcolor = "red"; + } + this.setOutputData(0, value); + }; + + MathFormula.prototype.getTitle = function() { + return this._func_code || "Formula"; + }; + + MathFormula.prototype.onDrawBackground = function() { + var f = this.properties["formula"]; + if (this.outputs && this.outputs.length) { + this.outputs[0].label = f; + } + }; + + LiteGraph.registerNodeType("math/formula", MathFormula); + + function Math3DVec2ToXY() { + this.addInput("vec2", "vec2"); + this.addOutput("x", "number"); + this.addOutput("y", "number"); + } + + Math3DVec2ToXY.title = "Vec2->XY"; + Math3DVec2ToXY.desc = "vector 2 to components"; + + Math3DVec2ToXY.prototype.onExecute = function() { + var v = this.getInputData(0); + if (v == null) { + return; + } + + this.setOutputData(0, v[0]); + this.setOutputData(1, v[1]); + }; + + LiteGraph.registerNodeType("math3d/vec2-to-xy", Math3DVec2ToXY); + + function Math3DXYToVec2() { + this.addInputs([["x", "number"], ["y", "number"]]); + this.addOutput("vec2", "vec2"); + this.properties = { x: 0, y: 0 }; + this._data = new Float32Array(2); + } + + Math3DXYToVec2.title = "XY->Vec2"; + Math3DXYToVec2.desc = "components to vector2"; + + Math3DXYToVec2.prototype.onExecute = function() { + var x = this.getInputData(0); + if (x == null) { + x = this.properties.x; + } + var y = this.getInputData(1); + if (y == null) { + y = this.properties.y; + } + + var data = this._data; + data[0] = x; + data[1] = y; + + this.setOutputData(0, data); + }; + + LiteGraph.registerNodeType("math3d/xy-to-vec2", Math3DXYToVec2); + + 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"); + this.properties = { x: 0, y: 0, z: 0 }; + this._data = new Float32Array(3); + } + + Math3DXYZToVec3.title = "XYZ->Vec3"; + Math3DXYZToVec3.desc = "components to vector3"; + + Math3DXYZToVec3.prototype.onExecute = function() { + var x = this.getInputData(0); + if (x == null) { + x = this.properties.x; + } + var y = this.getInputData(1); + if (y == null) { + y = this.properties.y; + } + var z = this.getInputData(2); + if (z == null) { + z = this.properties.z; + } + + var data = this._data; + data[0] = x; + data[1] = y; + data[2] = z; + + this.setOutputData(0, data); + }; + + LiteGraph.registerNodeType("math3d/xyz-to-vec3", Math3DXYZToVec3); + + function Math3DVec4ToXYZW() { + this.addInput("vec4", "vec4"); + this.addOutput("x", "number"); + this.addOutput("y", "number"); + this.addOutput("z", "number"); + this.addOutput("w", "number"); + } + + Math3DVec4ToXYZW.title = "Vec4->XYZW"; + Math3DVec4ToXYZW.desc = "vector 4 to components"; + + Math3DVec4ToXYZW.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]); + this.setOutputData(3, v[3]); + }; + + LiteGraph.registerNodeType("math3d/vec4-to-xyzw", Math3DVec4ToXYZW); + + function Math3DXYZWToVec4() { + this.addInputs([ + ["x", "number"], + ["y", "number"], + ["z", "number"], + ["w", "number"], + ]); + this.addOutput("vec4", "vec4"); + this.properties = { x: 0, y: 0, z: 0, w: 0 }; + this._data = new Float32Array(4); + } + + Math3DXYZWToVec4.title = "XYZW->Vec4"; + Math3DXYZWToVec4.desc = "components to vector4"; + + Math3DXYZWToVec4.prototype.onExecute = function() { + var x = this.getInputData(0); + if (x == null) { + x = this.properties.x; + } + var y = this.getInputData(1); + if (y == null) { + y = this.properties.y; + } + var z = this.getInputData(2); + if (z == null) { + z = this.properties.z; + } + var w = this.getInputData(3); + if (w == null) { + w = this.properties.w; + } + + var data = this._data; + data[0] = x; + data[1] = y; + data[2] = z; + data[3] = w; + + this.setOutputData(0, data); + }; + + LiteGraph.registerNodeType("math3d/xyzw-to-vec4", Math3DXYZWToVec4); + +})(this); + +(function(global) { + var LiteGraph = global.LiteGraph; + + + function Math3DMat4() { + this.addInput("T", "vec3"); + this.addInput("R", "vec3"); + this.addInput("S", "vec3"); + this.addOutput("mat4", "mat4"); + this.properties = { + "T": [0,0,0], + "R": [0,0,0], + "S": [1,1,1], + R_in_degrees: true, + }; + this._result = mat4.create(); + this._must_update = true; + } + + Math3DMat4.title = "mat4"; + Math3DMat4.temp_quat = new Float32Array([0,0,0,1]); + Math3DMat4.temp_mat4 = new Float32Array(16); + Math3DMat4.temp_vec3 = new Float32Array(3); + + Math3DMat4.prototype.onPropertyChanged = function(name, value) { + this._must_update = true; + } + + Math3DMat4.prototype.onExecute = function() { + var M = this._result; + var Q = Math3DMat4.temp_quat; + var temp_mat4 = Math3DMat4.temp_mat4; + var temp_vec3 = Math3DMat4.temp_vec3; + + var T = this.getInputData(0); + var R = this.getInputData(1); + var S = this.getInputData(2); + + if( this._must_update || T || R || S ) { + T = T || this.properties.T; + R = R || this.properties.R; + S = S || this.properties.S; + mat4.identity( M ); + mat4.translate( M, M, T ); + if(this.properties.R_in_degrees) { + temp_vec3.set( R ); + vec3.scale(temp_vec3,temp_vec3,DEG2RAD); + quat.fromEuler( Q, temp_vec3 ); + } else + quat.fromEuler( Q, R ); + mat4.fromQuat( temp_mat4, Q ); + mat4.multiply( M, M, temp_mat4 ); + mat4.scale( M, M, S ); + } + + this.setOutputData(0, M); + } + + LiteGraph.registerNodeType("math3d/mat4", Math3DMat4); + + // Math 3D operation + function Math3DOperation() { + this.addInput("A", "number,vec3"); + this.addInput("B", "number,vec3"); + this.addOutput("=", "number,vec3"); + this.addProperty("OP", "+", "enum", { values: Math3DOperation.values }); + this._result = vec3.create(); + } + + Math3DOperation.values = ["+", "-", "*", "/", "%", "^", "max", "min","dot","cross"]; + + LiteGraph.registerSearchboxExtra("math3d/operation", "CROSS()", { + properties: {"OP": "cross"}, + title: "CROSS()", + }); + + LiteGraph.registerSearchboxExtra("math3d/operation", "DOT()", { + properties: {"OP": "dot"}, + title: "DOT()", + }); + + Math3DOperation.title = "Operation"; + Math3DOperation.desc = "Easy math 3D operators"; + Math3DOperation["@OP"] = { + type: "enum", + title: "operation", + values: Math3DOperation.values, + }; + Math3DOperation.size = [100, 60]; + + Math3DOperation.prototype.getTitle = function() { + if(this.properties.OP == "max" || this.properties.OP == "min" ) + return this.properties.OP + "(A,B)"; + return "A " + this.properties.OP + " B"; + }; + + Math3DOperation.prototype.onExecute = function() { + var A = this.getInputData(0); + var B = this.getInputData(1); + if(A == null || B == null) + return; + if(A.constructor === Number) + A = [A,A,A]; + if(B.constructor === Number) + B = [B,B,B]; + + var result = this._result; + switch (this.properties.OP) { + case "+": + result = vec3.add(result,A,B); + break; + case "-": + result = vec3.sub(result,A,B); + break; + case "x": + case "X": + case "*": + result = vec3.mul(result,A,B); + break; + case "/": + result = vec3.div(result,A,B); + break; + case "%": + result[0] = A[0]%B[0]; + result[1] = A[1]%B[1]; + result[2] = A[2]%B[2]; + break; + case "^": + result[0] = Math.pow(A[0],B[0]); + result[1] = Math.pow(A[1],B[1]); + result[2] = Math.pow(A[2],B[2]); + break; + case "max": + result[0] = Math.max(A[0],B[0]); + result[1] = Math.max(A[1],B[1]); + result[2] = Math.max(A[2],B[2]); + break; + case "min": + result[0] = Math.min(A[0],B[0]); + result[1] = Math.min(A[1],B[1]); + result[2] = Math.min(A[2],B[2]); + break; + case "dot": + result = vec3.dot(A,B); + break; + case "cross": + vec3.cross(result,A,B); + break; + default: + console.warn("Unknown operation: " + this.properties.OP); + } + this.setOutputData(0, result); + }; + + Math3DOperation.prototype.onDrawBackground = function(ctx) { + if (this.flags.collapsed) { + return; + } + + ctx.font = "40px Arial"; + ctx.fillStyle = "#666"; + ctx.textAlign = "center"; + ctx.fillText( + this.properties.OP, + this.size[0] * 0.5, + (this.size[1] + LiteGraph.NODE_TITLE_HEIGHT) * 0.5, + ); + ctx.textAlign = "left"; + }; + + LiteGraph.registerNodeType("math3d/operation", Math3DOperation); + + function Math3DVec3Scale() { + this.addInput("in", "vec3"); + this.addInput("f", "number"); + this.addOutput("out", "vec3"); + this.properties = { f: 1 }; + this._data = new Float32Array(3); + } + + Math3DVec3Scale.title = "vec3_scale"; + Math3DVec3Scale.desc = "scales the components of a vec3"; + + Math3DVec3Scale.prototype.onExecute = function() { + var v = this.getInputData(0); + if (v == null) { + return; + } + var f = this.getInputData(1); + if (f == null) { + f = this.properties.f; + } + + var data = this._data; + data[0] = v[0] * f; + data[1] = v[1] * f; + data[2] = v[2] * f; + this.setOutputData(0, data); + }; + + LiteGraph.registerNodeType("math3d/vec3-scale", Math3DVec3Scale); + + function Math3DVec3Length() { + this.addInput("in", "vec3"); + this.addOutput("out", "number"); + } + + Math3DVec3Length.title = "vec3_length"; + Math3DVec3Length.desc = "returns the module of a vector"; + + Math3DVec3Length.prototype.onExecute = function() { + var v = this.getInputData(0); + if (v == null) { + return; + } + var dist = Math.sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]); + this.setOutputData(0, dist); + }; + + LiteGraph.registerNodeType("math3d/vec3-length", Math3DVec3Length); + + function Math3DVec3Normalize() { + this.addInput("in", "vec3"); + this.addOutput("out", "vec3"); + this._data = new Float32Array(3); + } + + Math3DVec3Normalize.title = "vec3_normalize"; + Math3DVec3Normalize.desc = "returns the vector normalized"; + + Math3DVec3Normalize.prototype.onExecute = function() { + var v = this.getInputData(0); + if (v == null) { + return; + } + var dist = Math.sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]); + var data = this._data; + data[0] = v[0] / dist; + data[1] = v[1] / dist; + data[2] = v[2] / dist; + + this.setOutputData(0, data); + }; + + LiteGraph.registerNodeType("math3d/vec3-normalize", Math3DVec3Normalize); + + function Math3DVec3Lerp() { + this.addInput("A", "vec3"); + this.addInput("B", "vec3"); + this.addInput("f", "vec3"); + this.addOutput("out", "vec3"); + this.properties = { f: 0.5 }; + this._data = new Float32Array(3); + } + + Math3DVec3Lerp.title = "vec3_lerp"; + Math3DVec3Lerp.desc = "returns the interpolated vector"; + + Math3DVec3Lerp.prototype.onExecute = function() { + var A = this.getInputData(0); + if (A == null) { + return; + } + var B = this.getInputData(1); + if (B == null) { + return; + } + var f = this.getInputOrProperty("f"); + + var data = this._data; + data[0] = A[0] * (1 - f) + B[0] * f; + data[1] = A[1] * (1 - f) + B[1] * f; + data[2] = A[2] * (1 - f) + B[2] * f; + + this.setOutputData(0, data); + }; + + LiteGraph.registerNodeType("math3d/vec3-lerp", Math3DVec3Lerp); + + function Math3DVec3Dot() { + this.addInput("A", "vec3"); + this.addInput("B", "vec3"); + this.addOutput("out", "number"); + } + + Math3DVec3Dot.title = "vec3_dot"; + Math3DVec3Dot.desc = "returns the dot product"; + + Math3DVec3Dot.prototype.onExecute = function() { + var A = this.getInputData(0); + if (A == null) { + return; + } + var B = this.getInputData(1); + if (B == null) { + return; + } + + var dot = A[0] * B[0] + A[1] * B[1] + A[2] * B[2]; + this.setOutputData(0, dot); + }; + + LiteGraph.registerNodeType("math3d/vec3-dot", Math3DVec3Dot); + + // if glMatrix is installed... + if (global.glMatrix) { + function Math3DQuaternion() { + this.addOutput("quat", "quat"); + this.properties = { x: 0, y: 0, z: 0, w: 1, normalize: false }; + this._value = quat.create(); + } + + Math3DQuaternion.title = "Quaternion"; + Math3DQuaternion.desc = "quaternion"; + + Math3DQuaternion.prototype.onExecute = function() { + this._value[0] = this.getInputOrProperty("x"); + this._value[1] = this.getInputOrProperty("y"); + this._value[2] = this.getInputOrProperty("z"); + this._value[3] = this.getInputOrProperty("w"); + if (this.properties.normalize) { + quat.normalize(this._value, this._value); + } + this.setOutputData(0, this._value); + }; + + Math3DQuaternion.prototype.onGetInputs = function() { + return [ + ["x", "number"], + ["y", "number"], + ["z", "number"], + ["w", "number"], + ]; + }; + + LiteGraph.registerNodeType("math3d/quaternion", Math3DQuaternion); + + function Math3DRotation() { + this.addInputs([["degrees", "number"], ["axis", "vec3"]]); + this.addOutput("quat", "quat"); + this.properties = { angle: 90.0, axis: vec3.fromValues(0, 1, 0) }; + + this._value = quat.create(); + } + + 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(this._value, axis, angle * 0.0174532925); + this.setOutputData(0, R); + }; + + LiteGraph.registerNodeType("math3d/rotation", Math3DRotation); + + + function MathEulerToQuat() { + this.addInput("euler", "vec3"); + this.addOutput("quat", "quat"); + this.properties = { euler: [0,0,0], use_yaw_pitch_roll: false }; + this._degs = vec3.create(); + this._value = quat.create(); + } + + MathEulerToQuat.title = "Euler->Quat"; + MathEulerToQuat.desc = "Converts euler angles (in degrees) to quaternion"; + + MathEulerToQuat.prototype.onExecute = function() { + var euler = this.getInputData(0); + if (euler == null) { + euler = this.properties.euler; + } + vec3.scale( this._degs, euler, DEG2RAD ); + if(this.properties.use_yaw_pitch_roll) + this._degs = [this._degs[2],this._degs[0],this._degs[1]]; + var R = quat.fromEuler(this._value, this._degs); + this.setOutputData(0, R); + }; + + LiteGraph.registerNodeType("math3d/euler_to_quat", MathEulerToQuat); + + function MathQuatToEuler() { + this.addInput(["quat", "quat"]); + this.addOutput("euler", "vec3"); + this._value = vec3.create(); + } + + MathQuatToEuler.title = "Euler->Quat"; + MathQuatToEuler.desc = "Converts rotX,rotY,rotZ in degrees to quat"; + + MathQuatToEuler.prototype.onExecute = function() { + var q = this.getInputData(0); + if(!q) + return; + var R = quat.toEuler(this._value, q); + vec3.scale( this._value, this._value, DEG2RAD ); + this.setOutputData(0, this._value); + }; + + LiteGraph.registerNodeType("math3d/quat_to_euler", MathQuatToEuler); + + + // 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"); + + this._value = quat.create(); + } + + 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(this._value, A, B); + this.setOutputData(0, R); + }; + + LiteGraph.registerNodeType("math3d/mult-quat", Math3DMultQuat); + + function Math3DQuatSlerp() { + this.addInputs([ + ["A", "quat"], + ["B", "quat"], + ["factor", "number"], + ]); + this.addOutput("slerp", "quat"); + this.addProperty("factor", 0.5); + + this._value = quat.create(); + } + + Math3DQuatSlerp.title = "Quat Slerp"; + Math3DQuatSlerp.desc = "quaternion spherical interpolation"; + + Math3DQuatSlerp.prototype.onExecute = function() { + var A = this.getInputData(0); + if (A == null) { + return; + } + var B = this.getInputData(1); + if (B == null) { + return; + } + var factor = this.properties.factor; + if (this.getInputData(2) != null) { + factor = this.getInputData(2); + } + + var R = quat.slerp(this._value, A, B, factor); + this.setOutputData(0, R); + }; + + LiteGraph.registerNodeType("math3d/quat-slerp", Math3DQuatSlerp); + + + // Math3D rotate vec3 + function Math3DRemapRange() { + this.addInput("vec3", "vec3"); + this.addOutput("remap", "vec3"); + this.addOutput("clamped", "vec3"); + this.properties = { clamp: true, range_min: [-1, -1, 0], range_max: [1, 1, 0], target_min: [-1,-1,0], target_max: [1,1,0] }; + this._value = vec3.create(); + this._clamped = vec3.create(); + } + + Math3DRemapRange.title = "Remap Range"; + Math3DRemapRange.desc = "remap a 3D range"; + + Math3DRemapRange.prototype.onExecute = function() { + var vec = this.getInputData(0); + if(vec) + this._value.set(vec); + var range_min = this.properties.range_min; + var range_max = this.properties.range_max; + var target_min = this.properties.target_min; + var target_max = this.properties.target_max; + + // swap to avoid errors + /* + if(range_min > range_max) + { + range_min = range_max; + range_max = this.properties.range_min; + } + + if(target_min > target_max) + { + target_min = target_max; + target_max = this.properties.target_min; + } + */ + + for(var i = 0; i < 3; ++i) { + var r = range_max[i] - range_min[i]; + this._clamped[i] = clamp( this._value[i], range_min[i], range_max[i] ); + if(r == 0) { + this._value[i] = (target_min[i] + target_max[i]) * 0.5; + continue; + } + + var n = (this._value[i] - range_min[i]) / r; + if(this.properties.clamp) + n = clamp(n,0,1); + var t = target_max[i] - target_min[i]; + this._value[i] = target_min[i] + n * t; + } + + this.setOutputData(0,this._value); + this.setOutputData(1,this._clamped); + }; + + LiteGraph.registerNodeType("math3d/remap_range", Math3DRemapRange); + + + + } else if (LiteGraph.debug) + console.warn("No glmatrix found, some Math3D nodes may not work"); + +})(this); + +// basic nodes +(function(global) { + var LiteGraph = global.LiteGraph; + + function toString(a) { + if(a && a.constructor === Object) { + try { + return JSON.stringify(a); + } catch (err) { + return String(a); + } + } + return String(a); + } + + LiteGraph.wrapFunctionAsNode("string/toString", toString, [""], "string"); + + function compare(a, b) { + return a == b; + } + + LiteGraph.wrapFunctionAsNode( + "string/compare", + compare, + ["string", "string"], + "boolean", + ); + + function concatenate(a, b) { + if (a === undefined) { + return b; + } + if (b === undefined) { + return a; + } + return a + b; + } + + LiteGraph.wrapFunctionAsNode( + "string/concatenate", + concatenate, + ["string", "string"], + "string", + ); + + function contains(a, b) { + if (a === undefined || b === undefined) { + return false; + } + return a.indexOf(b) != -1; + } + + LiteGraph.wrapFunctionAsNode( + "string/contains", + contains, + ["string", "string"], + "boolean", + ); + + function toUpperCase(a) { + if (a != null && a.constructor === String) { + return a.toUpperCase(); + } + return a; + } + + LiteGraph.wrapFunctionAsNode( + "string/toUpperCase", + toUpperCase, + ["string"], + "string", + ); + + function split(str, separator) { + if(separator == null) + separator = this.properties.separator; + if (str == null ) + return []; + if( str.constructor === String ) + return str.split(separator || " "); + else if( str.constructor === Array ) { + var r = []; + for(var i = 0; i < str.length; ++i) { + if (typeof str[i] == "string") + r[i] = str[i].split(separator || " "); + } + return r; + } + return null; + } + + LiteGraph.wrapFunctionAsNode( + "string/split", + split, + ["string,array", "string"], + "array", + { separator: "," }, + ); + + function toFixed(a) { + if (a != null && a.constructor === Number) { + return a.toFixed(this.properties.precision); + } + return a; + } + + LiteGraph.wrapFunctionAsNode( + "string/toFixed", + toFixed, + ["number"], + "string", + { precision: 0 }, + ); + + + function StringToTable() { + this.addInput("", "string"); + this.addOutput("table", "table"); + this.addOutput("rows", "number"); + this.addProperty("value", ""); + this.addProperty("separator", ","); + this._table = null; + } + + StringToTable.title = "toTable"; + StringToTable.desc = "Splits a string to table"; + + StringToTable.prototype.onExecute = function() { + var input = this.getInputData(0); + if(!input) + return; + var separator = this.properties.separator || ","; + if(input != this._str || separator != this._last_separator ) { + this._last_separator = separator; + this._str = input; + this._table = input.split("\n").map(function(a) { + return a.trim().split(separator) + }); + } + this.setOutputData(0, this._table ); + this.setOutputData(1, this._table ? this._table.length : 0 ); + }; + + LiteGraph.registerNodeType("string/toTable", StringToTable); + +})(this); + +(function(global) { + var LiteGraph = global.LiteGraph; + + function Selector() { + this.addInput("sel", "number"); + this.addInput("A"); + this.addInput("B"); + this.addInput("C"); + this.addInput("D"); + this.addOutput("out"); + + this.selected = 0; + } + + Selector.title = "Selector"; + Selector.desc = "selects an output"; + + Selector.prototype.onDrawBackground = function(ctx) { + if (this.flags.collapsed) { + return; + } + ctx.fillStyle = "#AFB"; + var y = (this.selected + 1) * LiteGraph.NODE_SLOT_HEIGHT + 6; + ctx.beginPath(); + ctx.moveTo(50, y); + ctx.lineTo(50, y + LiteGraph.NODE_SLOT_HEIGHT); + ctx.lineTo(34, y + LiteGraph.NODE_SLOT_HEIGHT * 0.5); + ctx.fill(); + }; + + Selector.prototype.onExecute = function() { + var sel = this.getInputData(0); + if (sel == null || sel.constructor !== Number) + sel = 0; + this.selected = sel = Math.round(sel) % (this.inputs.length - 1); + var v = this.getInputData(sel + 1); + if (v !== undefined) { + this.setOutputData(0, v); + } + }; + + Selector.prototype.onGetInputs = function() { + return [["E", 0], ["F", 0], ["G", 0], ["H", 0]]; + }; + + LiteGraph.registerNodeType("logic/selector", Selector); + + function Sequence() { + this.properties = {sequence: "A,B,C"}; + this.addInput("index", "number"); + this.addInput("seq"); + this.addOutput("out"); + + this.index = 0; + this.values = this.properties.sequence.split(","); + } + + Sequence.title = "Sequence"; + Sequence.desc = "select one element from a sequence from a string"; + + Sequence.prototype.onPropertyChanged = function(name, value) { + if (name == "sequence") { + this.values = value.split(","); + } + }; + + Sequence.prototype.onExecute = function() { + var seq = this.getInputData(1); + if (seq && seq != this.current_sequence) { + this.values = seq.split(","); + this.current_sequence = seq; + } + var index = this.getInputData(0); + if (index == null) { + index = 0; + } + this.index = index = Math.round(index) % this.values.length; + + this.setOutputData(0, this.values[index]); + }; + + LiteGraph.registerNodeType("logic/sequence", Sequence); + + + function logicAnd() { + this.properties = { }; + this.addInput("a", "boolean"); + this.addInput("b", "boolean"); + this.addOutput("out", "boolean"); + } + logicAnd.title = "AND"; + logicAnd.desc = "Return true if all inputs are true"; + logicAnd.prototype.onExecute = function() { + var ret = true; + for (var inX in this.inputs) { + if (!this.getInputData(inX)) { + var ret = false; + break; + } + } + this.setOutputData(0, ret); + }; + logicAnd.prototype.onGetInputs = function() { + return [ + ["and", "boolean"], + ]; + }; + LiteGraph.registerNodeType("logic/AND", logicAnd); + + + function logicOr() { + this.properties = { }; + this.addInput("a", "boolean"); + this.addInput("b", "boolean"); + this.addOutput("out", "boolean"); + } + logicOr.title = "OR"; + logicOr.desc = "Return true if at least one input is true"; + logicOr.prototype.onExecute = function() { + var ret = false; + for (var inX in this.inputs) { + if (this.getInputData(inX)) { + ret = true; + break; + } + } + this.setOutputData(0, ret); + }; + logicOr.prototype.onGetInputs = function() { + return [ + ["or", "boolean"], + ]; + }; + LiteGraph.registerNodeType("logic/OR", logicOr); + + + function logicNot() { + this.properties = { }; + this.addInput("in", "boolean"); + this.addOutput("out", "boolean"); + } + logicNot.title = "NOT"; + logicNot.desc = "Return the logical negation"; + logicNot.prototype.onExecute = function() { + var ret = !this.getInputData(0); + this.setOutputData(0, ret); + }; + LiteGraph.registerNodeType("logic/NOT", logicNot); + + + function logicCompare() { + this.properties = { }; + this.addInput("a", "boolean"); + this.addInput("b", "boolean"); + this.addOutput("out", "boolean"); + } + logicCompare.title = "bool == bool"; + logicCompare.desc = "Compare for logical equality"; + logicCompare.prototype.onExecute = function() { + var last = null; + var ret = true; + for (var inX in this.inputs) { + if (last === null) last = this.getInputData(inX); + else + if (last != this.getInputData(inX)) { + ret = false; + break; + } + } + this.setOutputData(0, ret); + }; + logicCompare.prototype.onGetInputs = function() { + return [ + ["bool", "boolean"], + ]; + }; + LiteGraph.registerNodeType("logic/CompareBool", logicCompare); + + + function logicBranch() { + this.properties = { }; + this.addInput("onTrigger", LiteGraph.ACTION); + this.addInput("condition", "boolean"); + this.addOutput("true", LiteGraph.EVENT); + this.addOutput("false", LiteGraph.EVENT); + this.mode = LiteGraph.ON_TRIGGER; + } + logicBranch.title = "Branch"; + logicBranch.desc = "Branch execution on condition"; + logicBranch.prototype.onExecute = function(param, options) { + var condtition = this.getInputData(1); + if (condtition) { + this.triggerSlot(0); + }else{ + this.triggerSlot(1); + } + }; + LiteGraph.registerNodeType("logic/IF", logicBranch); +})(this); + +(function(global) { + var LiteGraph = global.LiteGraph; + + function GraphicsPlot() { + this.addInput("A", "Number"); + this.addInput("B", "Number"); + this.addInput("C", "Number"); + this.addInput("D", "Number"); + + this.values = [[], [], [], []]; + this.properties = { scale: 2 }; + } + + GraphicsPlot.title = "Plot"; + GraphicsPlot.desc = "Plots data over time"; + GraphicsPlot.colors = ["#FFF", "#F99", "#9F9", "#99F"]; + + GraphicsPlot.prototype.onExecute = function(ctx) { + if (this.flags.collapsed) { + return; + } + + var size = this.size; + + for (var i = 0; i < 4; ++i) { + var v = this.getInputData(i); + if (v == null) { + continue; + } + var values = this.values[i]; + values.push(v); + if (values.length > size[0]) { + values.shift(); + } + } + }; + + GraphicsPlot.prototype.onDrawBackground = function(ctx) { + if (this.flags.collapsed) { + return; + } + + var size = this.size; + + var scale = (0.5 * size[1]) / this.properties.scale; + var colors = GraphicsPlot.colors; + var offset = size[1] * 0.5; + + ctx.fillStyle = "#000"; + ctx.fillRect(0, 0, size[0], size[1]); + ctx.strokeStyle = "#555"; + ctx.beginPath(); + ctx.moveTo(0, offset); + ctx.lineTo(size[0], offset); + ctx.stroke(); + + if (this.inputs) { + for (var i = 0; i < 4; ++i) { + var values = this.values[i]; + if (!this.inputs[i] || !this.inputs[i].link) { + continue; + } + ctx.strokeStyle = colors[i]; + ctx.beginPath(); + var v = values[0] * scale * -1 + offset; + ctx.moveTo(0, clamp(v, 0, size[1])); + for (var j = 1; j < values.length && j < size[0]; ++j) { + var v = values[j] * scale * -1 + offset; + ctx.lineTo(j, clamp(v, 0, size[1])); + } + ctx.stroke(); + } + } + }; + + LiteGraph.registerNodeType("graphics/plot", GraphicsPlot); + + function GraphicsImage() { + this.addOutput("frame", "image"); + this.properties = { url: "" }; + } + + GraphicsImage.title = "Image"; + GraphicsImage.desc = "Image loader"; + GraphicsImage.widgets = [{ name: "load", text: "Load", type: "button" }]; + + GraphicsImage.supported_extensions = ["jpg", "jpeg", "png", "gif"]; + + GraphicsImage.prototype.onAdded = function() { + if (this.properties["url"] != "" && this.img == null) { + this.loadImage(this.properties["url"]); + } + }; + + GraphicsImage.prototype.onDrawBackground = function(ctx) { + if (this.flags.collapsed) { + return; + } + if (this.img && this.size[0] > 5 && this.size[1] > 5 && this.img.width) { + ctx.drawImage(this.img, 0, 0, this.size[0], this.size[1]); + } + }; + + 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 && this.img.dirty) { + this.img.dirty = false; + } + }; + + GraphicsImage.prototype.onPropertyChanged = function(name, value) { + this.properties[name] = value; + if (name == "url" && value != "") { + this.loadImage(value); + } + + return true; + }; + + GraphicsImage.prototype.loadImage = function(url, callback) { + if (url == "") { + this.img = null; + return; + } + + this.img = document.createElement("img"); + + if (url.substr(0, 4) == "http" && LiteGraph.proxy) { + url = LiteGraph.proxy + url.substr(url.indexOf(":") + 3); + } + + this.img.src = url; + this.boxcolor = "#F95"; + var that = this; + this.img.onload = function() { + if (callback) { + callback(this); + } + console.log( "Image loaded, size: " + that.img.width + "x" + that.img.height ); + this.dirty = true; + that.boxcolor = "#9F9"; + that.setDirtyCanvas(true); + }; + this.img.onerror = function() { + console.log("error loading the image:" + url); + } + }; + + GraphicsImage.prototype.onWidget = function(e, widget) { + if (widget.name == "load") { + this.loadImage(this.properties["url"]); + } + }; + + GraphicsImage.prototype.onDropFile = function(file) { + var that = this; + if (this._url) { + URL.revokeObjectURL(this._url); + } + this._url = URL.createObjectURL(file); + this.properties.url = this._url; + this.loadImage(this._url, function(img) { + that.size[1] = (img.height / img.width) * that.size[0]; + }); + }; + + LiteGraph.registerNodeType("graphics/image", GraphicsImage); + + 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=0; i < result.length; i++) { + result[i] /= 255; + } + + this.boxcolor = colorToString(result); + this.setOutputData(0, result); + }; + + LiteGraph.registerNodeType("color/palette", ColorPalette); + + function ImageFrame() { + this.addInput("", "image,canvas"); + 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 && !this.flags.collapsed) { + 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); + + function ImageFade() { + this.addInputs([ + ["img1", "image"], + ["img2", "image"], + ["fade", "number"], + ]); + this.addOutput("", "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"); + + 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 ImageCrop() { + this.addInput("", "image"); + this.addOutput("", "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.onDrawBackground = function(ctx) { + if (this.flags.collapsed) { + return; + } + if (this.canvas) { + ctx.drawImage( + this.canvas, + 0, + 0, + this.canvas.width, + this.canvas.height, + 0, + 0, + this.size[0], + this.size[1], + ); + } + }; + + ImageCrop.prototype.onPropertyChanged = function(name, value) { + this.properties[name] = value; + + if (name == "scale") { + this.properties[name] = parseFloat(value); + if (this.properties[name] == 0) { + console.error("Error in scale"); + this.properties[name] = 1.0; + } + } else { + this.properties[name] = parseInt(value); + } + + this.createCanvas(); + + return true; + }; + + LiteGraph.registerNodeType("graphics/cropImage", ImageCrop); + + // CANVAS stuff + + function CanvasNode() { + this.addInput("clear", LiteGraph.ACTION); + this.addOutput("", "canvas"); + this.properties = { width: 512, height: 512, autoclear: true }; + + this.canvas = document.createElement("canvas"); + this.ctx = this.canvas.getContext("2d"); + } + + CanvasNode.title = "Canvas"; + CanvasNode.desc = "Canvas to render stuff"; + + CanvasNode.prototype.onExecute = function() { + var canvas = this.canvas; + var w = this.properties.width | 0; + var h = this.properties.height | 0; + if (canvas.width != w) { + canvas.width = w; + } + if (canvas.height != h) { + canvas.height = h; + } + + if (this.properties.autoclear) { + this.ctx.clearRect(0, 0, canvas.width, canvas.height); + } + this.setOutputData(0, canvas); + }; + + CanvasNode.prototype.onAction = function(action, param) { + if (action == "clear") { + this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height); + } + }; + + LiteGraph.registerNodeType("graphics/canvas", CanvasNode); + + function DrawImageNode() { + this.addInput("canvas", "canvas"); + this.addInput("img", "image,canvas"); + this.addInput("x", "number"); + this.addInput("y", "number"); + this.properties = { x: 0, y: 0, opacity: 1 }; + } + + DrawImageNode.title = "DrawImage"; + DrawImageNode.desc = "Draws image into a canvas"; + + DrawImageNode.prototype.onExecute = function() { + var canvas = this.getInputData(0); + if (!canvas) { + return; + } + + var img = this.getInputOrProperty("img"); + if (!img) { + return; + } + + var x = this.getInputOrProperty("x"); + var y = this.getInputOrProperty("y"); + var ctx = canvas.getContext("2d"); + ctx.drawImage(img, x, y); + }; + + LiteGraph.registerNodeType("graphics/drawImage", DrawImageNode); + + function DrawRectangleNode() { + this.addInput("canvas", "canvas"); + this.addInput("x", "number"); + this.addInput("y", "number"); + this.addInput("w", "number"); + this.addInput("h", "number"); + this.properties = { + x: 0, + y: 0, + w: 10, + h: 10, + color: "white", + opacity: 1, + }; + } + + DrawRectangleNode.title = "DrawRectangle"; + DrawRectangleNode.desc = "Draws rectangle in canvas"; + + DrawRectangleNode.prototype.onExecute = function() { + var canvas = this.getInputData(0); + if (!canvas) { + return; + } + + var x = this.getInputOrProperty("x"); + var y = this.getInputOrProperty("y"); + var w = this.getInputOrProperty("w"); + var h = this.getInputOrProperty("h"); + var ctx = canvas.getContext("2d"); + ctx.fillRect(x, y, w, h); + }; + + LiteGraph.registerNodeType("graphics/drawRectangle", DrawRectangleNode); + + function ImageVideo() { + this.addInput("t", "number"); + this.addOutputs([["frame", "image"], ["t", "number"], ["d", "number"]]); + this.properties = { url: "", use_proxy: true }; + } + + 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; + + var pos = url.substr(0,10).indexOf(":"); + var protocol = ""; + if(pos != -1) + protocol = url.substr(0,pos); + + var host = ""; + if(protocol) { + host = url.substr(0,url.indexOf("/",protocol.length + 3)); + host = host.substr(protocol.length+3); + } + + if ( + this.properties.use_proxy && + protocol && + LiteGraph.proxy && + host != location.host + ) { + url = LiteGraph.proxy + url.substr(url.indexOf(":") + 3); + } + + 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 + console.log("Duration: " + this.duration + " seconds"); + console.log("Size: " + this.videoWidth + "," + this.videoHeight); + that.setDirtyCanvas(true); + this.width = this.videoWidth; + this.height = this.videoHeight; + }); + this._video.addEventListener("progress", function(e) { + // onload + console.log("video loading..."); + }); + this._video.addEventListener("error", function(e) { + console.error("Error loading video: " + this.src); + if (this.error) { + switch (this.error.code) { + case this.error.MEDIA_ERR_ABORTED: + console.error("You stopped the video."); + break; + case this.error.MEDIA_ERR_NETWORK: + console.error("Network error - please try again later."); + break; + case this.error.MEDIA_ERR_DECODE: + console.error("Video is broken.."); + break; + case this.error.MEDIA_ERR_SRC_NOT_SUPPORTED: + console.error("Sorry, your browser can't play this video."); + break; + } + } + }); + + this._video.addEventListener("ended", function(e) { + console.log("Video Ended."); + this.play(); // loop + }); + + // document.body.appendChild(this.video); + }; + + ImageVideo.prototype.onPropertyChanged = 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.videoWidth ) { // is loaded + 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; + } + console.log("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 = { filterFacingMode: false, facingMode: "user" }; + this.boxcolor = "black"; + this.frame = 0; + } + + ImageWebcam.title = "Webcam"; + ImageWebcam.desc = "Webcam image"; + ImageWebcam.is_webcam_open = false; + + ImageWebcam.prototype.openStream = function() { + if (!navigator.mediaDevices.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. + var constraints = { + audio: false, + video: !this.properties.filterFacingMode ? true : { facingMode: this.properties.facingMode }, + }; + navigator.mediaDevices + .getUserMedia(constraints) + .then(this.streamReady.bind(this)) + .catch(onFailSoHard); + + var that = this; + function onFailSoHard(e) { + console.log("Webcam rejected", e); + that._webcam_stream = false; + ImageWebcam.is_webcam_open = false; + that.boxcolor = "red"; + that.trigger("stream_error"); + } + }; + + ImageWebcam.prototype.closeStream = function() { + if (this._webcam_stream) { + var tracks = this._webcam_stream.getTracks(); + if (tracks.length) { + for (var i = 0; i < tracks.length; ++i) { + tracks[i].stop(); + } + } + ImageWebcam.is_webcam_open = false; + this._webcam_stream = null; + this._video = null; + this.boxcolor = "black"; + this.trigger("stream_closed"); + } + }; + + ImageWebcam.prototype.onPropertyChanged = function(name, value) { + if (name == "facingMode") { + this.properties.facingMode = value; + this.closeStream(); + this.openStream(); + } + }; + + ImageWebcam.prototype.onRemoved = function() { + this.closeStream(); + }; + + ImageWebcam.prototype.streamReady = function(localMediaStream) { + this._webcam_stream = localMediaStream; + // this._waiting_confirmation = false; + this.boxcolor = "green"; + + var video = this._video; + if (!video) { + video = document.createElement("video"); + video.autoplay = true; + video.srcObject = 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.is_webcam_open = true; + }; + } + + this.trigger("stream_ready", video); + }; + + ImageWebcam.prototype.onExecute = function() { + if (this._webcam_stream == null && !this._waiting_confirmation) { + this.openStream(); + } + + if (!this._video || !this._video.videoWidth) { + return; + } + + this._video.frame = ++this.frame; + this._video.width = this._video.videoWidth; + this._video.height = this._video.videoHeight; + this.setOutputData(0, this._video); + for (var i = 1; i < this.outputs.length; ++i) { + if (!this.outputs[i]) { + continue; + } + switch (this.outputs[i].name) { + case "width": + this.setOutputData(i, this._video.videoWidth); + break; + case "height": + this.setOutputData(i, this._video.videoHeight); + break; + } + } + }; + + ImageWebcam.prototype.getExtraMenuOptions = function(graphcanvas) { + var that = this; + var txt = !that.properties.show ? "Show Frame" : "Hide Frame"; + return [ + { + content: txt, + callback: function() { + that.properties.show = !that.properties.show; + }, + }, + ]; + }; + + ImageWebcam.prototype.onDrawBackground = function(ctx) { + if ( + this.flags.collapsed || + this.size[1] <= 20 || + !this.properties.show + ) { + return; + } + + if (!this._video) { + return; + } + + // render to graph canvas + ctx.save(); + ctx.drawImage(this._video, 0, 0, this.size[0], this.size[1]); + ctx.restore(); + }; + + ImageWebcam.prototype.onGetOutputs = function() { + return [ + ["width", "number"], + ["height", "number"], + ["stream_ready", LiteGraph.EVENT], + ["stream_closed", LiteGraph.EVENT], + ["stream_error", LiteGraph.EVENT], + ]; + }; + + LiteGraph.registerNodeType("graphics/webcam", ImageWebcam); +})(this); + +(function(global) { + var LiteGraph = global.LiteGraph; + var LGraphCanvas = global.LGraphCanvas; + + // Works with Litegl.js to create WebGL nodes + global.LGraphTexture = null; + + if (typeof GL == "undefined") + return; + + LGraphCanvas.link_type_colors["Texture"] = "#987"; + + function LGraphTexture() { + this.addOutput("tex", "Texture"); + this.addOutput("name", "string"); + this.properties = { name: "", filter: true }; + this.size = [ + LGraphTexture.image_preview_size, + LGraphTexture.image_preview_size, + ]; + } + + global.LGraphTexture = LGraphTexture; + + LGraphTexture.title = "Texture"; + LGraphTexture.desc = "Texture"; + LGraphTexture.widgets_info = { + name: { widget: "texture" }, + filter: { widget: "checkbox" }, + }; + + // REPLACE THIS TO INTEGRATE WITH YOUR FRAMEWORK + LGraphTexture.loadTextureCallback = null; // function in charge of loading textures when not present in the container + LGraphTexture.image_preview_size = 256; + + // flags to choose output texture type + LGraphTexture.UNDEFINED = 0; // not specified + LGraphTexture.PASS_THROUGH = 1; // do not apply FX (like disable but passing the in to the out) + LGraphTexture.COPY = 2; // create new texture with the same properties as the origin texture + LGraphTexture.LOW = 3; // create new texture with low precision (byte) + LGraphTexture.HIGH = 4; // create new texture with high precision (half-float) + LGraphTexture.REUSE = 5; // reuse input texture + LGraphTexture.DEFAULT = 2; // use the default + + LGraphTexture.MODE_VALUES = { + "undefined": LGraphTexture.UNDEFINED, + "pass through": LGraphTexture.PASS_THROUGH, + copy: LGraphTexture.COPY, + low: LGraphTexture.LOW, + high: LGraphTexture.HIGH, + reuse: LGraphTexture.REUSE, + default: LGraphTexture.DEFAULT, + }; + + // returns the container where all the loaded textures are stored (overwrite if you have a Resources Manager) + LGraphTexture.getTexturesContainer = function() { + return gl.textures; + }; + + // process the loading of a texture (overwrite it if you have a Resources Manager) + LGraphTexture.loadTexture = function(name, options) { + options = options || {}; + var url = name; + if (url.substr(0, 7) == "http://") { + if (LiteGraph.proxy) { + // proxy external files + url = LiteGraph.proxy + url.substr(7); + } + } + + var container = LGraphTexture.getTexturesContainer(); + var tex = (container[name] = GL.Texture.fromURL(url, options)); + return tex; + }; + + LGraphTexture.getTexture = function(name) { + var container = this.getTexturesContainer(); + + if (!container) { + throw "Cannot load texture, container of textures not found"; + } + + var tex = container[name]; + if (!tex && name && name[0] != ":") { + return this.loadTexture(name); + } + + return tex; + }; + + // used to compute the appropiate output texture + LGraphTexture.getTargetTexture = function(origin, target, mode) { + if (!origin) { + throw "LGraphTexture.getTargetTexture expects a reference texture"; + } + + var tex_type = null; + + switch (mode) { + case LGraphTexture.LOW: + tex_type = gl.UNSIGNED_BYTE; + break; + case LGraphTexture.HIGH: + tex_type = gl.HIGH_PRECISION_FORMAT; + break; + case LGraphTexture.REUSE: + return origin; + case LGraphTexture.COPY: + default: + tex_type = origin ? origin.type : gl.UNSIGNED_BYTE; + break; + } + + if ( + !target || + target.width != origin.width || + target.height != origin.height || + target.type != tex_type || + target.format != origin.format + ) { + target = new GL.Texture(origin.width, origin.height, { + type: tex_type, + format: origin.format, + filter: gl.LINEAR, + }); + } + + return target; + }; + + LGraphTexture.getTextureType = function(precision, ref_texture) { + var type = ref_texture ? ref_texture.type : gl.UNSIGNED_BYTE; + switch (precision) { + case LGraphTexture.HIGH: + type = gl.HIGH_PRECISION_FORMAT; + break; + case LGraphTexture.LOW: + type = gl.UNSIGNED_BYTE; + break; + // no default + } + return type; + }; + + LGraphTexture.getWhiteTexture = function() { + if (this._white_texture) { + return this._white_texture; + } + var texture = (this._white_texture = GL.Texture.fromMemory( + 1, + 1, + [255, 255, 255, 255], + { format: gl.RGBA, wrap: gl.REPEAT, filter: gl.NEAREST }, + )); + return texture; + }; + + LGraphTexture.getNoiseTexture = function() { + if (this._noise_texture) { + return this._noise_texture; + } + + var noise = new Uint8Array(512 * 512 * 4); + for (var i = 0; i < 512 * 512 * 4; ++i) { + noise[i] = Math.random() * 255; + } + + var texture = GL.Texture.fromMemory(512, 512, noise, { + format: gl.RGBA, + wrap: gl.REPEAT, + filter: gl.NEAREST, + }); + this._noise_texture = texture; + return texture; + }; + + LGraphTexture.prototype.onDropFile = function(data, filename, file) { + if (!data) { + this._drop_texture = null; + this.properties.name = ""; + } else { + var texture = null; + if (typeof data == "string") { + texture = GL.Texture.fromURL(data); + } else if (filename.toLowerCase().indexOf(".dds") != -1) { + texture = GL.Texture.fromDDSInMemory(data); + } else { + var blob = new Blob([file]); + var url = URL.createObjectURL(blob); + texture = GL.Texture.fromURL(url); + } + + this._drop_texture = texture; + this.properties.name = filename; + } + }; + + LGraphTexture.prototype.getExtraMenuOptions = function(graphcanvas) { + var that = this; + if (!this._drop_texture) { + return; + } + return [ + { + content: "Clear", + callback: function() { + that._drop_texture = null; + that.properties.name = ""; + }, + }, + ]; + }; + + LGraphTexture.prototype.onExecute = function() { + var tex = null; + if (this.isOutputConnected(1)) { + tex = this.getInputData(0); + } + + if (!tex && this._drop_texture) { + tex = this._drop_texture; + } + + if (!tex && this.properties.name) { + tex = LGraphTexture.getTexture(this.properties.name); + } + + if (!tex) { + this.setOutputData( 0, null ); + this.setOutputData( 1, "" ); + return; + } + + this._last_tex = tex; + + if (this.properties.filter === false) { + tex.setParameter(gl.TEXTURE_MAG_FILTER, gl.NEAREST); + } else { + tex.setParameter(gl.TEXTURE_MAG_FILTER, gl.LINEAR); + } + + this.setOutputData( 0, tex ); + this.setOutputData( 1, tex.fullpath || tex.filename ); + + for (var i = 2; i < this.outputs.length; i++) { + var output = this.outputs[i]; + if (!output) { + continue; + } + var v = null; + if (output.name == "width") { + v = tex.width; + } else if (output.name == "height") { + v = tex.height; + } else if (output.name == "aspect") { + v = tex.width / tex.height; + } + this.setOutputData(i, v); + } + }; + + LGraphTexture.prototype.onResourceRenamed = function( + old_name, + new_name, + ) { + if (this.properties.name == old_name) { + this.properties.name = new_name; + } + }; + + LGraphTexture.prototype.onDrawBackground = function(ctx) { + if (this.flags.collapsed || this.size[1] <= 20) { + return; + } + + if (this._drop_texture && ctx.webgl) { + ctx.drawImage( + this._drop_texture, + 0, + 0, + this.size[0], + this.size[1], + ); + // this._drop_texture.renderQuad(this.pos[0],this.pos[1],this.size[0],this.size[1]); + return; + } + + // Different texture? then get it from the GPU + if (this._last_preview_tex != this._last_tex) { + if (ctx.webgl) { + this._canvas = this._last_tex; + } else { + var tex_canvas = LGraphTexture.generateLowResTexturePreview(this._last_tex); + if (!tex_canvas) { + return; + } + + this._last_preview_tex = this._last_tex; + this._canvas = cloneCanvas(tex_canvas); + } + } + + if (!this._canvas) { + return; + } + + // render to graph canvas + ctx.save(); + if (!ctx.webgl) { + // reverse image + ctx.translate(0, this.size[1]); + ctx.scale(1, -1); + } + ctx.drawImage(this._canvas, 0, 0, this.size[0], this.size[1]); + ctx.restore(); + }; + + // very slow, used at your own risk + LGraphTexture.generateLowResTexturePreview = function(tex) { + if (!tex) { + return null; + } + + var size = LGraphTexture.image_preview_size; + var temp_tex = tex; + + if (tex.format == gl.DEPTH_COMPONENT) { + return null; + } // cannot generate from depth + + // Generate low-level version in the GPU to speed up + if (tex.width > size || tex.height > size) { + temp_tex = this._preview_temp_tex; + if (!this._preview_temp_tex) { + temp_tex = new GL.Texture(size, size, {minFilter: gl.NEAREST}); + this._preview_temp_tex = temp_tex; + } + + // copy + tex.copyTo(temp_tex); + tex = temp_tex; + } + + // create intermediate canvas with lowquality version + var tex_canvas = this._preview_canvas; + if (!tex_canvas) { + tex_canvas = createCanvas(size, size); + this._preview_canvas = tex_canvas; + } + + if (temp_tex) { + temp_tex.toCanvas(tex_canvas); + } + return tex_canvas; + }; + + LGraphTexture.prototype.getResources = function(res) { + if(this.properties.name) + res[this.properties.name] = GL.Texture; + return res; + }; + + LGraphTexture.prototype.onGetInputs = function() { + return [["in", "Texture"]]; + }; + + LGraphTexture.prototype.onGetOutputs = function() { + return [ + ["width", "number"], + ["height", "number"], + ["aspect", "number"], + ]; + }; + + // used to replace shader code + LGraphTexture.replaceCode = function( code, context ) { + return code.replace(/\{\{[a-zA-Z0-9_]*\}\}/g, function(v) { + v = v.replace( /[{}]/g, "" ); + return context[v] || ""; + }); + } + + LiteGraph.registerNodeType("texture/texture", LGraphTexture); + + //* ************************* + function LGraphTexturePreview() { + this.addInput("Texture", "Texture"); + this.properties = { flipY: false }; + this.size = [ + LGraphTexture.image_preview_size, + LGraphTexture.image_preview_size, + ]; + } + + LGraphTexturePreview.title = "Preview"; + LGraphTexturePreview.desc = "Show a texture in the graph canvas"; + LGraphTexturePreview.allow_preview = false; + + LGraphTexturePreview.prototype.onDrawBackground = function(ctx) { + if (this.flags.collapsed) { + return; + } + + if (!ctx.webgl && !LGraphTexturePreview.allow_preview) { + return; + } // not working well + + var tex = this.getInputData(0); + if (!tex) { + return; + } + + var tex_canvas = null; + + if (!tex.handle && ctx.webgl) { + tex_canvas = tex; + } else { + tex_canvas = LGraphTexture.generateLowResTexturePreview(tex); + } + + // render to graph canvas + ctx.save(); + if (this.properties.flipY) { + ctx.translate(0, this.size[1]); + ctx.scale(1, -1); + } + ctx.drawImage(tex_canvas, 0, 0, this.size[0], this.size[1]); + ctx.restore(); + }; + + LiteGraph.registerNodeType("texture/preview", LGraphTexturePreview); + + //* ************************************* + + function LGraphTextureSave() { + this.addInput("Texture", "Texture"); + this.addOutput("tex", "Texture"); + this.addOutput("name", "string"); + this.properties = { name: "", generate_mipmaps: false }; + } + + LGraphTextureSave.title = "Save"; + LGraphTextureSave.desc = "Save a texture in the repository"; + + LGraphTextureSave.prototype.getPreviewTexture = function() { + return this._texture; + } + + LGraphTextureSave.prototype.onExecute = function() { + var tex = this.getInputData(0); + if (!tex) { + return; + } + + if (this.properties.generate_mipmaps) { + tex.bind(0); + tex.setParameter( gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR ); + gl.generateMipmap(tex.texture_type); + tex.unbind(0); + } + + if (this.properties.name) { + // for cases where we want to perform something when storing it + if (LGraphTexture.storeTexture) { + LGraphTexture.storeTexture(this.properties.name, tex); + } else { + var container = LGraphTexture.getTexturesContainer(); + container[this.properties.name] = tex; + } + } + + this._texture = tex; + this.setOutputData(0, tex); + this.setOutputData(1, this.properties.name); + }; + + LiteGraph.registerNodeType("texture/save", LGraphTextureSave); + + //* *************************************************** + + function LGraphTextureOperation() { + this.addInput("Texture", "Texture"); + this.addInput("TextureB", "Texture"); + this.addInput("value", "number"); + this.addOutput("Texture", "Texture"); + this.help = "

pixelcode must be vec3, uvcode must be vec2, is optional

\ +

uv: tex. coords

color: texture colorB: textureB

time: scene time value: input value

For multiline you must type: result = ...

"; + + this.properties = { + value: 1, + pixelcode: "color + colorB * value", + uvcode: "", + precision: LGraphTexture.DEFAULT, + }; + + this.has_error = false; + } + + LGraphTextureOperation.widgets_info = { + uvcode: { widget: "code" }, + pixelcode: { widget: "code" }, + precision: { widget: "combo", values: LGraphTexture.MODE_VALUES }, + }; + + LGraphTextureOperation.title = "Operation"; + LGraphTextureOperation.desc = "Texture shader operation"; + + LGraphTextureOperation.presets = {}; + + LGraphTextureOperation.prototype.getExtraMenuOptions = function(graphcanvas) { + var that = this; + var txt = !that.properties.show ? "Show Texture" : "Hide Texture"; + return [ + { + content: txt, + callback: function() { + that.properties.show = !that.properties.show; + }, + }, + ]; + }; + + LGraphTextureOperation.prototype.onPropertyChanged = function() { + this.has_error = false; + } + + LGraphTextureOperation.prototype.onDrawBackground = function(ctx) { + if ( + this.flags.collapsed || + this.size[1] <= 20 || + !this.properties.show + ) { + return; + } + + if (!this._tex) { + return; + } + + // only works if using a webgl renderer + if (this._tex.gl != ctx) { + return; + } + + // render to graph canvas + ctx.save(); + ctx.drawImage(this._tex, 0, 0, this.size[0], this.size[1]); + ctx.restore(); + }; + + LGraphTextureOperation.prototype.onExecute = function() { + var tex = this.getInputData(0); + + if (!this.isOutputConnected(0)) { + return; + } // saves work + + if (this.properties.precision === LGraphTexture.PASS_THROUGH) { + this.setOutputData(0, tex); + return; + } + + var texB = this.getInputData(1); + + if (!this.properties.uvcode && !this.properties.pixelcode) { + return; + } + + var width = 512; + var height = 512; + if (tex) { + width = tex.width; + height = tex.height; + } else if (texB) { + width = texB.width; + height = texB.height; + } + + if(!texB) + texB = GL.Texture.getWhiteTexture(); + + var type = LGraphTexture.getTextureType( this.properties.precision, tex ); + + if (!tex && !this._tex) { + this._tex = new GL.Texture(width, height, { type: type, format: gl.RGBA, filter: gl.LINEAR }); + } else { + this._tex = LGraphTexture.getTargetTexture( tex || this._tex, this._tex, this.properties.precision ); + } + + var uvcode = ""; + if (this.properties.uvcode) { + uvcode = "uv = " + this.properties.uvcode; + if (this.properties.uvcode.indexOf(";") != -1) { + // there are line breaks, means multiline code + uvcode = this.properties.uvcode; + } + } + + var pixelcode = ""; + if (this.properties.pixelcode) { + pixelcode = "result = " + this.properties.pixelcode; + if (this.properties.pixelcode.indexOf(";") != -1) { + // there are line breaks, means multiline code + pixelcode = this.properties.pixelcode; + } + } + + var shader = this._shader; + + if ( !this.has_error && (!shader || this._shader_code != uvcode + "|" + pixelcode) ) { + + var final_pixel_code = LGraphTexture.replaceCode( LGraphTextureOperation.pixel_shader, { UV_CODE: uvcode, PIXEL_CODE: pixelcode }); + + try { + shader = new GL.Shader( Shader.SCREEN_VERTEX_SHADER, final_pixel_code ); + this.boxcolor = "#00FF00"; + } catch (err) { + // console.log("Error compiling shader: ", err, final_pixel_code ); + GL.Shader.dumpErrorToConsole(err,Shader.SCREEN_VERTEX_SHADER, final_pixel_code); + this.boxcolor = "#FF0000"; + this.has_error = true; + return; + } + this._shader = shader; + this._shader_code = uvcode + "|" + pixelcode; + } + + if(!this._shader) + return; + + var value = this.getInputData(2); + if (value != null) { + this.properties.value = value; + } else { + value = parseFloat(this.properties.value); + } + + var time = this.graph.getTime(); + + this._tex.drawTo(function() { + gl.disable(gl.DEPTH_TEST); + gl.disable(gl.CULL_FACE); + gl.disable(gl.BLEND); + if (tex) { + tex.bind(0); + } + if (texB) { + texB.bind(1); + } + var mesh = Mesh.getScreenQuad(); + shader + .uniforms({ + u_texture: 0, + u_textureB: 1, + value: value, + texSize: [width, height,1/width,1/height], + time: time, + }) + .draw(mesh); + }); + + this.setOutputData(0, this._tex); + }; + + LGraphTextureOperation.pixel_shader = + "precision highp float;\n\ + \n\ + uniform sampler2D u_texture;\n\ + uniform sampler2D u_textureB;\n\ + varying vec2 v_coord;\n\ + uniform vec4 texSize;\n\ + uniform float time;\n\ + uniform float value;\n\ + \n\ + void main() {\n\ + vec2 uv = v_coord;\n\ + {{UV_CODE}};\n\ + vec4 color4 = texture2D(u_texture, uv);\n\ + vec3 color = color4.rgb;\n\ + vec4 color4B = texture2D(u_textureB, uv);\n\ + vec3 colorB = color4B.rgb;\n\ + vec3 result = color;\n\ + float alpha = 1.0;\n\ + {{PIXEL_CODE}};\n\ + gl_FragColor = vec4(result, alpha);\n\ + }\n\ + "; + + LGraphTextureOperation.registerPreset = function ( name, code ) { + LGraphTextureOperation.presets[name] = code; + } + + LGraphTextureOperation.registerPreset("",""); + LGraphTextureOperation.registerPreset("bypass","color"); + LGraphTextureOperation.registerPreset("add","color + colorB * value"); + LGraphTextureOperation.registerPreset("substract","(color - colorB) * value"); + LGraphTextureOperation.registerPreset("mate","mix( color, colorB, color4B.a * value)"); + LGraphTextureOperation.registerPreset("invert","vec3(1.0) - color"); + LGraphTextureOperation.registerPreset("multiply","color * colorB * value"); + LGraphTextureOperation.registerPreset("divide","(color / colorB) / value"); + LGraphTextureOperation.registerPreset("difference","abs(color - colorB) * value"); + LGraphTextureOperation.registerPreset("max","max(color, colorB) * value"); + LGraphTextureOperation.registerPreset("min","min(color, colorB) * value"); + LGraphTextureOperation.registerPreset("displace","texture2D(u_texture, uv + (colorB.xy - vec2(0.5)) * value).xyz"); + LGraphTextureOperation.registerPreset("grayscale","vec3(color.x + color.y + color.z) * value / 3.0"); + LGraphTextureOperation.registerPreset("saturation","mix( vec3(color.x + color.y + color.z) / 3.0, color, value )"); + LGraphTextureOperation.registerPreset("normalmap","\n\ + float z0 = texture2D(u_texture, uv + vec2(-texSize.z, -texSize.w) ).x;\n\ + float z1 = texture2D(u_texture, uv + vec2(0.0, -texSize.w) ).x;\n\ + float z2 = texture2D(u_texture, uv + vec2(texSize.z, -texSize.w) ).x;\n\ + float z3 = texture2D(u_texture, uv + vec2(-texSize.z, 0.0) ).x;\n\ + float z4 = color.x;\n\ + float z5 = texture2D(u_texture, uv + vec2(texSize.z, 0.0) ).x;\n\ + float z6 = texture2D(u_texture, uv + vec2(-texSize.z, texSize.w) ).x;\n\ + float z7 = texture2D(u_texture, uv + vec2(0.0, texSize.w) ).x;\n\ + float z8 = texture2D(u_texture, uv + vec2(texSize.z, texSize.w) ).x;\n\ + vec3 normal = vec3( z2 + 2.0*z4 + z7 - z0 - 2.0*z3 - z5, z5 + 2.0*z6 + z7 -z0 - 2.0*z1 - z2, 1.0 );\n\ + normal.xy *= value;\n\ + result.xyz = normalize(normal) * 0.5 + vec3(0.5);\n\ + "); + LGraphTextureOperation.registerPreset("threshold","vec3(color.x > colorB.x * value ? 1.0 : 0.0,color.y > colorB.y * value ? 1.0 : 0.0,color.z > colorB.z * value ? 1.0 : 0.0)"); + + // webglstudio stuff... + LGraphTextureOperation.prototype.onInspect = function(widgets) { + var that = this; + widgets.addCombo("Presets","",{ + values: Object.keys(LGraphTextureOperation.presets), callback: function(v) { + var code = LGraphTextureOperation.presets[v]; + if(!code) + return; + that.setProperty("pixelcode",code); + that.title = v; + widgets.refresh(); + }, + }); + } + + LiteGraph.registerNodeType("texture/operation", LGraphTextureOperation); + + //* *************************************************** + + function LGraphTextureShader() { + this.addOutput("out", "Texture"); + this.properties = { + code: "", + u_value: 1, + u_color: [1,1,1,1], + width: 512, + height: 512, + precision: LGraphTexture.DEFAULT, + }; + + this.properties.code = LGraphTextureShader.pixel_shader; + this._uniforms = { u_value: 1, u_color: vec4.create(), in_texture: 0, texSize: vec4.create(), time: 0 }; + } + + LGraphTextureShader.title = "Shader"; + LGraphTextureShader.desc = "Texture shader"; + LGraphTextureShader.widgets_info = { + code: { type: "code", lang: "glsl" }, + precision: { widget: "combo", values: LGraphTexture.MODE_VALUES }, + }; + + LGraphTextureShader.prototype.onPropertyChanged = function( + name, + value, + ) { + if (name != "code") { + return; + } + + var shader = this.getShader(); + if (!shader) { + return; + } + + // update connections + var uniforms = shader.uniformInfo; + + // remove deprecated slots + if (this.inputs) { + var already = {}; + for (var i = 0; i < this.inputs.length; ++i) { + var info = this.getInputInfo(i); + if (!info) { + continue; + } + + if (uniforms[info.name] && !already[info.name]) { + already[info.name] = true; + continue; + } + this.removeInput(i); + i--; + } + } + + // update existing ones + for (var i in uniforms) { + var info = shader.uniformInfo[i]; + if (info.loc === null) { + continue; + } // is an attribute, not a uniform + if (i == "time") { + // default one + continue; + } + + var type = "number"; + if (this._shader.samplers[i]) { + type = "texture"; + } else { + switch (info.size) { + case 1: + type = "number"; + break; + case 2: + type = "vec2"; + break; + case 3: + type = "vec3"; + break; + case 4: + type = "vec4"; + break; + case 9: + type = "mat3"; + break; + case 16: + type = "mat4"; + break; + default: + continue; + } + } + + var slot = this.findInputSlot(i); + if (slot == -1) { + this.addInput(i, type); + continue; + } + + var input_info = this.getInputInfo(slot); + if (!input_info) { + this.addInput(i, type); + } else { + if (input_info.type == type) { + continue; + } + this.removeInput(slot, type); + this.addInput(i, type); + } + } + }; + + LGraphTextureShader.prototype.getShader = function() { + // replug + if (this._shader && this._shader_code == this.properties.code) { + return this._shader; + } + + this._shader_code = this.properties.code; + this._shader = new GL.Shader( Shader.SCREEN_VERTEX_SHADER, this.properties.code ); + if (!this._shader) { + this.boxcolor = "red"; + return null; + } else { + this.boxcolor = "green"; + } + return this._shader; + }; + + LGraphTextureShader.prototype.onExecute = function() { + if (!this.isOutputConnected(0)) { + return; + } // saves work + + var shader = this.getShader(); + if (!shader) { + return; + } + + var tex_slot = 0; + var in_tex = null; + + // set uniforms + if(this.inputs) + for (var i = 0; i < this.inputs.length; ++i) { + var info = this.getInputInfo(i); + var data = this.getInputData(i); + if (data == null) { + continue; + } + + if (data.constructor === GL.Texture) { + data.bind(tex_slot); + if (!in_tex) { + in_tex = data; + } + data = tex_slot; + tex_slot++; + } + shader.setUniform(info.name, data); // data is tex_slot + } + + var uniforms = this._uniforms; + var type = LGraphTexture.getTextureType( this.properties.precision, in_tex ); + + // render to texture + var w = this.properties.width | 0; + var h = this.properties.height | 0; + if (w == 0) { + w = in_tex ? in_tex.width : gl.canvas.width; + } + if (h == 0) { + h = in_tex ? in_tex.height : gl.canvas.height; + } + uniforms.texSize[0] = w; + uniforms.texSize[1] = h; + uniforms.texSize[2] = 1/w; + uniforms.texSize[3] = 1/h; + uniforms.time = this.graph.getTime(); + uniforms.u_value = this.properties.u_value; + uniforms.u_color.set( this.properties.u_color ); + + if ( !this._tex || this._tex.type != type || this._tex.width != w || this._tex.height != h ) { + this._tex = new GL.Texture(w, h, { type: type, format: gl.RGBA, filter: gl.LINEAR }); + } + var tex = this._tex; + tex.drawTo(function() { + shader.uniforms(uniforms).draw(GL.Mesh.getScreenQuad()); + }); + + this.setOutputData(0, this._tex); + }; + + LGraphTextureShader.pixel_shader = +"precision highp float;\n\ +\n\ +varying vec2 v_coord;\n\ +uniform float time; //time in seconds\n\ +uniform vec4 texSize; //tex resolution\n\ +uniform float u_value;\n\ +uniform vec4 u_color;\n\n\ +void main() {\n\ + vec2 uv = v_coord;\n\ + vec3 color = vec3(0.0);\n\ + //your code here\n\ + color.xy=uv;\n\n\ + gl_FragColor = vec4(color, 1.0);\n\ +}\n\ +"; + + LiteGraph.registerNodeType("texture/shader", LGraphTextureShader); + + // Texture Scale Offset + + function LGraphTextureScaleOffset() { + this.addInput("in", "Texture"); + this.addInput("scale", "vec2"); + this.addInput("offset", "vec2"); + this.addOutput("out", "Texture"); + this.properties = { + offset: vec2.fromValues(0, 0), + scale: vec2.fromValues(1, 1), + precision: LGraphTexture.DEFAULT, + }; + } + + LGraphTextureScaleOffset.widgets_info = {precision: { widget: "combo", values: LGraphTexture.MODE_VALUES }}; + + LGraphTextureScaleOffset.title = "Scale/Offset"; + LGraphTextureScaleOffset.desc = "Applies an scaling and offseting"; + + LGraphTextureScaleOffset.prototype.onExecute = function() { + var tex = this.getInputData(0); + + if (!this.isOutputConnected(0) || !tex) { + return; + } // saves work + + if (this.properties.precision === LGraphTexture.PASS_THROUGH) { + this.setOutputData(0, tex); + return; + } + + var width = tex.width; + var height = tex.height; + var type = this.precision === LGraphTexture.LOW ? gl.UNSIGNED_BYTE : gl.HIGH_PRECISION_FORMAT; + if (this.precision === LGraphTexture.DEFAULT) { + type = tex.type; + } + + if ( + !this._tex || + this._tex.width != width || + this._tex.height != height || + this._tex.type != type + ) { + this._tex = new GL.Texture(width, height, { + type: type, + format: gl.RGBA, + filter: gl.LINEAR, + }); + } + + var shader = this._shader; + + if (!shader) { + shader = new GL.Shader( + GL.Shader.SCREEN_VERTEX_SHADER, + LGraphTextureScaleOffset.pixel_shader, + ); + } + + var scale = this.getInputData(1); + if (scale) { + this.properties.scale[0] = scale[0]; + this.properties.scale[1] = scale[1]; + } else { + scale = this.properties.scale; + } + + var offset = this.getInputData(2); + if (offset) { + this.properties.offset[0] = offset[0]; + this.properties.offset[1] = offset[1]; + } else { + offset = this.properties.offset; + } + + this._tex.drawTo(function() { + gl.disable(gl.DEPTH_TEST); + gl.disable(gl.CULL_FACE); + gl.disable(gl.BLEND); + tex.bind(0); + var mesh = Mesh.getScreenQuad(); + shader + .uniforms({ + u_texture: 0, + u_scale: scale, + u_offset: offset, + }) + .draw(mesh); + }); + + this.setOutputData(0, this._tex); + }; + + LGraphTextureScaleOffset.pixel_shader = + "precision highp float;\n\ + \n\ + uniform sampler2D u_texture;\n\ + uniform sampler2D u_textureB;\n\ + varying vec2 v_coord;\n\ + uniform vec2 u_scale;\n\ + uniform vec2 u_offset;\n\ + \n\ + void main() {\n\ + vec2 uv = v_coord;\n\ + uv = uv / u_scale - u_offset;\n\ + gl_FragColor = texture2D(u_texture, uv);\n\ + }\n\ + "; + + LiteGraph.registerNodeType( + "texture/scaleOffset", + LGraphTextureScaleOffset, + ); + + // Warp (distort a texture) ************************* + + function LGraphTextureWarp() { + this.addInput("in", "Texture"); + this.addInput("warp", "Texture"); + this.addInput("factor", "number"); + this.addOutput("out", "Texture"); + this.properties = { + factor: 0.01, + scale: [1,1], + offset: [0,0], + precision: LGraphTexture.DEFAULT, + }; + + this._uniforms = { + u_texture: 0, + u_textureB: 1, + u_factor: 1, + u_scale: vec2.create(), + u_offset: vec2.create(), + }; + } + + LGraphTextureWarp.widgets_info = {precision: { widget: "combo", values: LGraphTexture.MODE_VALUES }}; + + LGraphTextureWarp.title = "Warp"; + LGraphTextureWarp.desc = "Texture warp operation"; + + LGraphTextureWarp.prototype.onExecute = function() { + var tex = this.getInputData(0); + + if (!this.isOutputConnected(0)) { + return; + } // saves work + + if (this.properties.precision === LGraphTexture.PASS_THROUGH) { + this.setOutputData(0, tex); + return; + } + + var texB = this.getInputData(1); + + var width = 512; + var height = 512; + var type = gl.UNSIGNED_BYTE; + if (tex) { + width = tex.width; + height = tex.height; + type = tex.type; + } else if (texB) { + width = texB.width; + height = texB.height; + type = texB.type; + } + + if (!tex && !this._tex) { + this._tex = new GL.Texture(width, height, { + type: + this.precision === LGraphTexture.LOW + ? gl.UNSIGNED_BYTE + : gl.HIGH_PRECISION_FORMAT, + format: gl.RGBA, + filter: gl.LINEAR, + }); + } else { + this._tex = LGraphTexture.getTargetTexture( + tex || this._tex, + this._tex, + this.properties.precision, + ); + } + + var shader = this._shader; + + if (!shader) { + shader = new GL.Shader( + GL.Shader.SCREEN_VERTEX_SHADER, + LGraphTextureWarp.pixel_shader, + ); + } + + var factor = this.getInputData(2); + if (factor != null) { + this.properties.factor = factor; + } else { + factor = parseFloat(this.properties.factor); + } + var uniforms = this._uniforms; + uniforms.u_factor = factor; + uniforms.u_scale.set( this.properties.scale ); + uniforms.u_offset.set( this.properties.offset ); + + this._tex.drawTo(function() { + gl.disable(gl.DEPTH_TEST); + gl.disable(gl.CULL_FACE); + gl.disable(gl.BLEND); + if (tex) { + tex.bind(0); + } + if (texB) { + texB.bind(1); + } + var mesh = Mesh.getScreenQuad(); + shader + .uniforms( uniforms ) + .draw(mesh); + }); + + this.setOutputData(0, this._tex); + }; + + LGraphTextureWarp.pixel_shader = + "precision highp float;\n\ + \n\ + uniform sampler2D u_texture;\n\ + uniform sampler2D u_textureB;\n\ + varying vec2 v_coord;\n\ + uniform float u_factor;\n\ + uniform vec2 u_scale;\n\ + uniform vec2 u_offset;\n\ + \n\ + void main() {\n\ + vec2 uv = v_coord;\n\ + uv += ( texture2D(u_textureB, uv).rg - vec2(0.5)) * u_factor * u_scale + u_offset;\n\ + gl_FragColor = texture2D(u_texture, uv);\n\ + }\n\ + "; + + LiteGraph.registerNodeType("texture/warp", LGraphTextureWarp); + + //* *************************************************** + + // Texture to Viewport ***************************************** + function LGraphTextureToViewport() { + this.addInput("Texture", "Texture"); + this.properties = { + additive: false, + antialiasing: false, + filter: true, + disable_alpha: false, + gamma: 1.0, + viewport: [0,0,1,1], + }; + this.size[0] = 130; + } + + LGraphTextureToViewport.title = "to Viewport"; + LGraphTextureToViewport.desc = "Texture to viewport"; + + LGraphTextureToViewport._prev_viewport = new Float32Array(4); + + LGraphTextureToViewport.prototype.onDrawBackground = function( ctx ) { + if ( this.flags.collapsed || this.size[1] <= 40 ) + return; + + var tex = this.getInputData(0); + if (!tex) { + return; + } + + ctx.drawImage( ctx == gl ? tex : gl.canvas, 10,30, this.size[0] -20, this.size[1] -40); + } + + LGraphTextureToViewport.prototype.onExecute = function() { + var tex = this.getInputData(0); + if (!tex) { + return; + } + + if (this.properties.disable_alpha) { + gl.disable(gl.BLEND); + } else { + gl.enable(gl.BLEND); + if (this.properties.additive) { + gl.blendFunc(gl.SRC_ALPHA, gl.ONE); + } else { + gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA); + } + } + + gl.disable(gl.DEPTH_TEST); + var gamma = this.properties.gamma || 1.0; + if (this.isInputConnected(1)) { + gamma = this.getInputData(1); + } + + tex.setParameter( + gl.TEXTURE_MAG_FILTER, + this.properties.filter ? gl.LINEAR : gl.NEAREST, + ); + + var old_viewport = LGraphTextureToViewport._prev_viewport; + old_viewport.set( gl.viewport_data ); + var new_view = this.properties.viewport; + gl.viewport( old_viewport[0] + old_viewport[2] * new_view[0], old_viewport[1] + old_viewport[3] * new_view[1], old_viewport[2] * new_view[2], old_viewport[3] * new_view[3] ); + var viewport = gl.getViewport(); // gl.getParameter(gl.VIEWPORT); + + if (this.properties.antialiasing) { + if (!LGraphTextureToViewport._shader) { + LGraphTextureToViewport._shader = new GL.Shader( + GL.Shader.SCREEN_VERTEX_SHADER, + LGraphTextureToViewport.aa_pixel_shader, + ); + } + + var mesh = Mesh.getScreenQuad(); + tex.bind(0); + LGraphTextureToViewport._shader + .uniforms({ + u_texture: 0, + uViewportSize: [tex.width, tex.height], + u_igamma: 1 / gamma, + inverseVP: [1 / tex.width, 1 / tex.height], + }) + .draw(mesh); + } else { + if (gamma != 1.0) { + if (!LGraphTextureToViewport._gamma_shader) { + LGraphTextureToViewport._gamma_shader = new GL.Shader( + Shader.SCREEN_VERTEX_SHADER, + LGraphTextureToViewport.gamma_pixel_shader, + ); + } + tex.toViewport(LGraphTextureToViewport._gamma_shader, { + u_texture: 0, + u_igamma: 1 / gamma, + }); + } else { + tex.toViewport(); + } + } + + gl.viewport( old_viewport[0], old_viewport[1], old_viewport[2], old_viewport[3] ); + }; + + LGraphTextureToViewport.prototype.onGetInputs = function() { + return [["gamma", "number"]]; + }; + + LGraphTextureToViewport.aa_pixel_shader = + "precision highp float;\n\ + precision highp float;\n\ + varying vec2 v_coord;\n\ + uniform sampler2D u_texture;\n\ + uniform vec2 uViewportSize;\n\ + uniform vec2 inverseVP;\n\ + uniform float u_igamma;\n\ + #define FXAA_REDUCE_MIN (1.0/ 128.0)\n\ + #define FXAA_REDUCE_MUL (1.0 / 8.0)\n\ + #define FXAA_SPAN_MAX 8.0\n\ + \n\ + /* from mitsuhiko/webgl-meincraft based on the code on geeks3d.com */\n\ + vec4 applyFXAA(sampler2D tex, vec2 fragCoord)\n\ + {\n\ + vec4 color = vec4(0.0);\n\ + /*vec2 inverseVP = vec2(1.0 / uViewportSize.x, 1.0 / uViewportSize.y);*/\n\ + vec3 rgbNW = texture2D(tex, (fragCoord + vec2(-1.0, -1.0)) * inverseVP).xyz;\n\ + vec3 rgbNE = texture2D(tex, (fragCoord + vec2(1.0, -1.0)) * inverseVP).xyz;\n\ + vec3 rgbSW = texture2D(tex, (fragCoord + vec2(-1.0, 1.0)) * inverseVP).xyz;\n\ + vec3 rgbSE = texture2D(tex, (fragCoord + vec2(1.0, 1.0)) * inverseVP).xyz;\n\ + vec3 rgbM = texture2D(tex, fragCoord * inverseVP).xyz;\n\ + vec3 luma = vec3(0.299, 0.587, 0.114);\n\ + float lumaNW = dot(rgbNW, luma);\n\ + float lumaNE = dot(rgbNE, luma);\n\ + float lumaSW = dot(rgbSW, luma);\n\ + float lumaSE = dot(rgbSE, luma);\n\ + float lumaM = dot(rgbM, luma);\n\ + float lumaMin = min(lumaM, min(min(lumaNW, lumaNE), min(lumaSW, lumaSE)));\n\ + float lumaMax = max(lumaM, max(max(lumaNW, lumaNE), max(lumaSW, lumaSE)));\n\ + \n\ + vec2 dir;\n\ + dir.x = -((lumaNW + lumaNE) - (lumaSW + lumaSE));\n\ + dir.y = ((lumaNW + lumaSW) - (lumaNE + lumaSE));\n\ + \n\ + float dirReduce = max((lumaNW + lumaNE + lumaSW + lumaSE) * (0.25 * FXAA_REDUCE_MUL), FXAA_REDUCE_MIN);\n\ + \n\ + float rcpDirMin = 1.0 / (min(abs(dir.x), abs(dir.y)) + dirReduce);\n\ + dir = min(vec2(FXAA_SPAN_MAX, FXAA_SPAN_MAX), max(vec2(-FXAA_SPAN_MAX, -FXAA_SPAN_MAX), dir * rcpDirMin)) * inverseVP;\n\ + \n\ + vec3 rgbA = 0.5 * (texture2D(tex, fragCoord * inverseVP + dir * (1.0 / 3.0 - 0.5)).xyz + \n\ + texture2D(tex, fragCoord * inverseVP + dir * (2.0 / 3.0 - 0.5)).xyz);\n\ + vec3 rgbB = rgbA * 0.5 + 0.25 * (texture2D(tex, fragCoord * inverseVP + dir * -0.5).xyz + \n\ + texture2D(tex, fragCoord * inverseVP + dir * 0.5).xyz);\n\ + \n\ + //return vec4(rgbA,1.0);\n\ + float lumaB = dot(rgbB, luma);\n\ + if ((lumaB < lumaMin) || (lumaB > lumaMax))\n\ + color = vec4(rgbA, 1.0);\n\ + else\n\ + color = vec4(rgbB, 1.0);\n\ + if(u_igamma != 1.0)\n\ + color.xyz = pow( color.xyz, vec3(u_igamma) );\n\ + return color;\n\ + }\n\ + \n\ + void main() {\n\ + gl_FragColor = applyFXAA( u_texture, v_coord * uViewportSize) ;\n\ + }\n\ + "; + + LGraphTextureToViewport.gamma_pixel_shader = + "precision highp float;\n\ + precision highp float;\n\ + varying vec2 v_coord;\n\ + uniform sampler2D u_texture;\n\ + uniform float u_igamma;\n\ + void main() {\n\ + vec4 color = texture2D( u_texture, v_coord);\n\ + color.xyz = pow(color.xyz, vec3(u_igamma) );\n\ + gl_FragColor = color;\n\ + }\n\ + "; + + LiteGraph.registerNodeType( + "texture/toviewport", + LGraphTextureToViewport, + ); + + // Texture Copy ***************************************** + function LGraphTextureCopy() { + this.addInput("Texture", "Texture"); + this.addOutput("", "Texture"); + this.properties = { + size: 0, + generate_mipmaps: false, + precision: LGraphTexture.DEFAULT, + }; + } + + LGraphTextureCopy.title = "Copy"; + LGraphTextureCopy.desc = "Copy Texture"; + LGraphTextureCopy.widgets_info = { + size: { + widget: "combo", + values: [0, 32, 64, 128, 256, 512, 1024, 2048], + }, + precision: { widget: "combo", values: LGraphTexture.MODE_VALUES }, + }; + + LGraphTextureCopy.prototype.onExecute = function() { + var tex = this.getInputData(0); + if (!tex && !this._temp_texture) { + return; + } + + if (!this.isOutputConnected(0)) { + return; + } // saves work + + // copy the texture + if (tex) { + var width = tex.width; + var height = tex.height; + + if (this.properties.size != 0) { + width = this.properties.size; + height = this.properties.size; + } + + var temp = this._temp_texture; + + var type = tex.type; + if (this.properties.precision === LGraphTexture.LOW) { + type = gl.UNSIGNED_BYTE; + } else if (this.properties.precision === LGraphTexture.HIGH) { + type = gl.HIGH_PRECISION_FORMAT; + } + + if ( + !temp || + temp.width != width || + temp.height != height || + temp.type != type + ) { + var minFilter = gl.LINEAR; + if ( + this.properties.generate_mipmaps && + isPowerOfTwo(width) && + isPowerOfTwo(height) + ) { + minFilter = gl.LINEAR_MIPMAP_LINEAR; + } + this._temp_texture = new GL.Texture(width, height, { + type: type, + format: gl.RGBA, + minFilter: minFilter, + magFilter: gl.LINEAR, + }); + } + tex.copyTo(this._temp_texture); + + if (this.properties.generate_mipmaps) { + this._temp_texture.bind(0); + gl.generateMipmap(this._temp_texture.texture_type); + this._temp_texture.unbind(0); + } + } + + this.setOutputData(0, this._temp_texture); + }; + + LiteGraph.registerNodeType("texture/copy", LGraphTextureCopy); + + // Texture Downsample ***************************************** + function LGraphTextureDownsample() { + this.addInput("Texture", "Texture"); + this.addOutput("", "Texture"); + this.properties = { + iterations: 1, + generate_mipmaps: false, + precision: LGraphTexture.DEFAULT, + }; + } + + LGraphTextureDownsample.title = "Downsample"; + LGraphTextureDownsample.desc = "Downsample Texture"; + LGraphTextureDownsample.widgets_info = { + iterations: { type: "number", step: 1, precision: 0, min: 0 }, + precision: { widget: "combo", values: LGraphTexture.MODE_VALUES }, + }; + + LGraphTextureDownsample.prototype.onExecute = function() { + var tex = this.getInputData(0); + if (!tex && !this._temp_texture) { + return; + } + + if (!this.isOutputConnected(0)) { + return; + } // saves work + + // we do not allow any texture different than texture 2D + if (!tex || tex.texture_type !== GL.TEXTURE_2D) { + return; + } + + if (this.properties.iterations < 1) { + this.setOutputData(0, tex); + return; + } + + var shader = LGraphTextureDownsample._shader; + if (!shader) { + LGraphTextureDownsample._shader = shader = new GL.Shader( + GL.Shader.SCREEN_VERTEX_SHADER, + LGraphTextureDownsample.pixel_shader, + ); + } + + var width = tex.width | 0; + var height = tex.height | 0; + var type = tex.type; + if (this.properties.precision === LGraphTexture.LOW) { + type = gl.UNSIGNED_BYTE; + } else if (this.properties.precision === LGraphTexture.HIGH) { + type = gl.HIGH_PRECISION_FORMAT; + } + var iterations = this.properties.iterations || 1; + + var origin = tex; + var target = null; + + var temp = []; + var options = { + type: type, + format: tex.format, + }; + + var offset = vec2.create(); + var uniforms = {u_offset: offset}; + + if (this._texture) { + GL.Texture.releaseTemporary(this._texture); + } + + for (var i = 0; i < iterations; ++i) { + offset[0] = 1 / width; + offset[1] = 1 / height; + width = width >> 1 || 0; + height = height >> 1 || 0; + target = GL.Texture.getTemporary(width, height, options); + temp.push(target); + origin.setParameter(GL.TEXTURE_MAG_FILTER, GL.NEAREST); + origin.copyTo(target, shader, uniforms); + if (width == 1 && height == 1) { + break; + } // nothing else to do + origin = target; + } + + // keep the last texture used + this._texture = temp.pop(); + + // free the rest + for (var i = 0; i < temp.length; ++i) { + GL.Texture.releaseTemporary(temp[i]); + } + + if (this.properties.generate_mipmaps) { + this._texture.bind(0); + gl.generateMipmap(this._texture.texture_type); + this._texture.unbind(0); + } + + this.setOutputData(0, this._texture); + }; + + LGraphTextureDownsample.pixel_shader = + "precision highp float;\n\ + precision highp float;\n\ + uniform sampler2D u_texture;\n\ + uniform vec2 u_offset;\n\ + varying vec2 v_coord;\n\ + \n\ + void main() {\n\ + vec4 color = texture2D(u_texture, v_coord );\n\ + color += texture2D(u_texture, v_coord + vec2( u_offset.x, 0.0 ) );\n\ + color += texture2D(u_texture, v_coord + vec2( 0.0, u_offset.y ) );\n\ + color += texture2D(u_texture, v_coord + vec2( u_offset.x, u_offset.y ) );\n\ + gl_FragColor = color * 0.25;\n\ + }\n\ + "; + + LiteGraph.registerNodeType( + "texture/downsample", + LGraphTextureDownsample, + ); + + + + function LGraphTextureResize() { + this.addInput("Texture", "Texture"); + this.addOutput("", "Texture"); + this.properties = { + size: [512,512], + generate_mipmaps: false, + precision: LGraphTexture.DEFAULT, + }; + } + + LGraphTextureResize.title = "Resize"; + LGraphTextureResize.desc = "Resize Texture"; + LGraphTextureResize.widgets_info = { + iterations: { type: "number", step: 1, precision: 0, min: 0 }, + precision: { widget: "combo", values: LGraphTexture.MODE_VALUES }, + }; + + LGraphTextureResize.prototype.onExecute = function() { + var tex = this.getInputData(0); + if (!tex && !this._temp_texture) { + return; + } + + if (!this.isOutputConnected(0)) { + return; + } // saves work + + // we do not allow any texture different than texture 2D + if (!tex || tex.texture_type !== GL.TEXTURE_2D) { + return; + } + + var width = this.properties.size[0] | 0; + var height = this.properties.size[1] | 0; + if(width == 0) + width = tex.width; + if(height == 0) + height = tex.height; + var type = tex.type; + if (this.properties.precision === LGraphTexture.LOW) { + type = gl.UNSIGNED_BYTE; + } else if (this.properties.precision === LGraphTexture.HIGH) { + type = gl.HIGH_PRECISION_FORMAT; + } + + if( !this._texture || this._texture.width != width || this._texture.height != height || this._texture.type != type ) + this._texture = new GL.Texture( width, height, { type: type } ); + + tex.copyTo( this._texture ); + + if (this.properties.generate_mipmaps) { + this._texture.bind(0); + gl.generateMipmap(this._texture.texture_type); + this._texture.unbind(0); + } + + this.setOutputData(0, this._texture); + }; + + LiteGraph.registerNodeType( "texture/resize", LGraphTextureResize ); + + // Texture Average ***************************************** + function LGraphTextureAverage() { + this.addInput("Texture", "Texture"); + this.addOutput("tex", "Texture"); + this.addOutput("avg", "vec4"); + this.addOutput("lum", "number"); + this.properties = { + use_previous_frame: true, // to avoid stalls + high_quality: false, // to use as much pixels as possible + }; + + this._uniforms = { + u_texture: 0, + u_mipmap_offset: 0, + }; + this._luminance = new Float32Array(4); + } + + LGraphTextureAverage.title = "Average"; + LGraphTextureAverage.desc = + "Compute a partial average (32 random samples) of a texture and stores it as a 1x1 pixel texture.\n If high_quality is true, then it generates the mipmaps first and reads from the lower one."; + + LGraphTextureAverage.prototype.onExecute = function() { + if (!this.properties.use_previous_frame) { + this.updateAverage(); + } + + var v = this._luminance; + this.setOutputData(0, this._temp_texture); + this.setOutputData(1, v); + this.setOutputData(2, (v[0] + v[1] + v[2]) / 3); + }; + + // executed before rendering the frame + LGraphTextureAverage.prototype.onPreRenderExecute = function() { + this.updateAverage(); + }; + + LGraphTextureAverage.prototype.updateAverage = function() { + var tex = this.getInputData(0); + if (!tex) { + return; + } + + if ( + !this.isOutputConnected(0) && + !this.isOutputConnected(1) && + !this.isOutputConnected(2) + ) { + return; + } // saves work + + if (!LGraphTextureAverage._shader) { + LGraphTextureAverage._shader = new GL.Shader( + GL.Shader.SCREEN_VERTEX_SHADER, + LGraphTextureAverage.pixel_shader, + ); + // creates 256 random numbers and stores them in two mat4 + var samples = new Float32Array(16); + for (var i = 0; i < samples.length; ++i) { + samples[i] = Math.random(); // poorly distributed samples + } + // upload only once + LGraphTextureAverage._shader.uniforms({ + u_samples_a: samples.subarray(0, 16), + u_samples_b: samples.subarray(16, 32), + }); + } + + var temp = this._temp_texture; + var type = gl.UNSIGNED_BYTE; + if (tex.type != type) { + // force floats, half floats cannot be read with gl.readPixels + type = gl.FLOAT; + } + + if (!temp || temp.type != type) { + this._temp_texture = new GL.Texture(1, 1, { + type: type, + format: gl.RGBA, + filter: gl.NEAREST, + }); + } + + this._uniforms.u_mipmap_offset = 0; + + if(this.properties.high_quality) { + if( !this._temp_pot2_texture || this._temp_pot2_texture.type != type ) + this._temp_pot2_texture = new GL.Texture(512, 512, { + type: type, + format: gl.RGBA, + minFilter: gl.LINEAR_MIPMAP_LINEAR, + magFilter: gl.LINEAR, + }); + + tex.copyTo( this._temp_pot2_texture ); + tex = this._temp_pot2_texture; + tex.bind(0); + gl.generateMipmap(GL.TEXTURE_2D); + this._uniforms.u_mipmap_offset = 9; + } + + var shader = LGraphTextureAverage._shader; + var uniforms = this._uniforms; + uniforms.u_mipmap_offset = this.properties.mipmap_offset; + gl.disable(gl.DEPTH_TEST); + gl.disable(gl.BLEND); + this._temp_texture.drawTo(function() { + tex.toViewport(shader, uniforms); + }); + + if (this.isOutputConnected(1) || this.isOutputConnected(2)) { + var pixel = this._temp_texture.getPixels(); + if (pixel) { + var v = this._luminance; + var type = this._temp_texture.type; + v.set(pixel); + if (type == gl.UNSIGNED_BYTE) { + vec4.scale(v, v, 1 / 255); + } else if ( + type == GL.HALF_FLOAT || + type == GL.HALF_FLOAT_OES + ) { + // no half floats possible, hard to read back unless copyed to a FLOAT texture, so temp_texture is always forced to FLOAT + } + } + } + }; + + LGraphTextureAverage.pixel_shader = + "precision highp float;\n\ + precision highp float;\n\ + uniform mat4 u_samples_a;\n\ + uniform mat4 u_samples_b;\n\ + uniform sampler2D u_texture;\n\ + uniform float u_mipmap_offset;\n\ + varying vec2 v_coord;\n\ + \n\ + void main() {\n\ + vec4 color = vec4(0.0);\n\ + //random average\n\ + for(int i = 0; i < 4; ++i)\n\ + for(int j = 0; j < 4; ++j)\n\ + {\n\ + color += texture2D(u_texture, vec2( u_samples_a[i][j], u_samples_b[i][j] ), u_mipmap_offset );\n\ + color += texture2D(u_texture, vec2( 1.0 - u_samples_a[i][j], 1.0 - u_samples_b[i][j] ), u_mipmap_offset );\n\ + }\n\ + gl_FragColor = color * 0.03125;\n\ + }\n\ + "; + + LiteGraph.registerNodeType("texture/average", LGraphTextureAverage); + + + + // Computes operation between pixels (max, min) ***************************************** + function LGraphTextureMinMax() { + this.addInput("Texture", "Texture"); + this.addOutput("min_t", "Texture"); + this.addOutput("max_t", "Texture"); + this.addOutput("min", "vec4"); + this.addOutput("max", "vec4"); + this.properties = { + mode: "max", + use_previous_frame: true, // to avoid stalls + }; + + this._uniforms = {u_texture: 0}; + + this._max = new Float32Array(4); + this._min = new Float32Array(4); + + this._textures_chain = []; + } + + LGraphTextureMinMax.widgets_info = {mode: { widget: "combo", values: ["min","max","avg"] }}; + + LGraphTextureMinMax.title = "MinMax"; + LGraphTextureMinMax.desc = "Compute the scene min max"; + + LGraphTextureMinMax.prototype.onExecute = function() { + if (!this.properties.use_previous_frame) { + this.update(); + } + + this.setOutputData(0, this._temp_texture); + this.setOutputData(1, this._luminance); + }; + + // executed before rendering the frame + LGraphTextureMinMax.prototype.onPreRenderExecute = function() { + this.update(); + }; + + LGraphTextureMinMax.prototype.update = function() { + var tex = this.getInputData(0); + if (!tex) { + return; + } + + if ( !this.isOutputConnected(0) && !this.isOutputConnected(1) ) { + return; + } // saves work + + if (!LGraphTextureMinMax._shader) { + LGraphTextureMinMax._shader = new GL.Shader( GL.Shader.SCREEN_VERTEX_SHADER, LGraphTextureMinMax.pixel_shader ); + } + + var temp = this._temp_texture; + var type = gl.UNSIGNED_BYTE; + if (tex.type != type) { + // force floats, half floats cannot be read with gl.readPixels + type = gl.FLOAT; + } + + var size = 512; + + if( !this._textures_chain.length || this._textures_chain[0].type != type ) { + var index = 0; + while(i) { + this._textures_chain[i] = new GL.Texture( size, size, { + type: type, + format: gl.RGBA, + filter: gl.NEAREST, + }); + size = size >> 2; + i++; + if(size == 1) + break; + } + } + + tex.copyTo( this._textures_chain[0] ); + var prev = this._textures_chain[0]; + for(var i = 1; i <= this._textures_chain.length; ++i) { + var tex = this._textures_chain[i]; + + prev = tex; + } + + var shader = LGraphTextureMinMax._shader; + var uniforms = this._uniforms; + uniforms.u_mipmap_offset = this.properties.mipmap_offset; + gl.disable(gl.DEPTH_TEST); + gl.disable(gl.BLEND); + this._temp_texture.drawTo(function() { + tex.toViewport(shader, uniforms); + }); + }; + + LGraphTextureMinMax.pixel_shader = + "precision highp float;\n\ + precision highp float;\n\ + uniform mat4 u_samples_a;\n\ + uniform mat4 u_samples_b;\n\ + uniform sampler2D u_texture;\n\ + uniform float u_mipmap_offset;\n\ + varying vec2 v_coord;\n\ + \n\ + void main() {\n\ + vec4 color = vec4(0.0);\n\ + //random average\n\ + for(int i = 0; i < 4; ++i)\n\ + for(int j = 0; j < 4; ++j)\n\ + {\n\ + color += texture2D(u_texture, vec2( u_samples_a[i][j], u_samples_b[i][j] ), u_mipmap_offset );\n\ + color += texture2D(u_texture, vec2( 1.0 - u_samples_a[i][j], 1.0 - u_samples_b[i][j] ), u_mipmap_offset );\n\ + }\n\ + gl_FragColor = color * 0.03125;\n\ + }\n\ + "; + + // LiteGraph.registerNodeType("texture/clustered_operation", LGraphTextureClusteredOperation); + + + function LGraphTextureTemporalSmooth() { + this.addInput("in", "Texture"); + this.addInput("factor", "Number"); + this.addOutput("out", "Texture"); + this.properties = { factor: 0.5 }; + this._uniforms = { + u_texture: 0, + u_textureB: 1, + u_factor: this.properties.factor, + }; + } + + LGraphTextureTemporalSmooth.title = "Smooth"; + LGraphTextureTemporalSmooth.desc = "Smooth texture over time"; + + LGraphTextureTemporalSmooth.prototype.onExecute = function() { + var tex = this.getInputData(0); + if (!tex || !this.isOutputConnected(0)) { + return; + } + + if (!LGraphTextureTemporalSmooth._shader) { + LGraphTextureTemporalSmooth._shader = new GL.Shader( + GL.Shader.SCREEN_VERTEX_SHADER, + LGraphTextureTemporalSmooth.pixel_shader, + ); + } + + var temp = this._temp_texture; + if ( + !temp || + temp.type != tex.type || + temp.width != tex.width || + temp.height != tex.height + ) { + var options = { + type: tex.type, + format: gl.RGBA, + filter: gl.NEAREST, + }; + this._temp_texture = new GL.Texture(tex.width, tex.height, options ); + this._temp_texture2 = new GL.Texture(tex.width, tex.height, options ); + tex.copyTo(this._temp_texture2); + } + + var tempA = this._temp_texture; + var tempB = this._temp_texture2; + + var shader = LGraphTextureTemporalSmooth._shader; + var uniforms = this._uniforms; + uniforms.u_factor = 1.0 - this.getInputOrProperty("factor"); + + gl.disable(gl.BLEND); + gl.disable(gl.DEPTH_TEST); + tempA.drawTo(function() { + tempB.bind(1); + tex.toViewport(shader, uniforms); + }); + + this.setOutputData(0, tempA); + + // swap + this._temp_texture = tempB; + this._temp_texture2 = tempA; + }; + + LGraphTextureTemporalSmooth.pixel_shader = + "precision highp float;\n\ + precision highp float;\n\ + uniform sampler2D u_texture;\n\ + uniform sampler2D u_textureB;\n\ + uniform float u_factor;\n\ + varying vec2 v_coord;\n\ + \n\ + void main() {\n\ + gl_FragColor = mix( texture2D( u_texture, v_coord ), texture2D( u_textureB, v_coord ), u_factor );\n\ + }\n\ + "; + + LiteGraph.registerNodeType( "texture/temporal_smooth", LGraphTextureTemporalSmooth ); + + + function LGraphTextureLinearAvgSmooth() { + this.addInput("in", "Texture"); + this.addOutput("avg", "Texture"); + this.addOutput("array", "Texture"); + this.properties = { samples: 64, frames_interval: 1 }; + this._uniforms = { + u_texture: 0, + u_textureB: 1, + u_samples: this.properties.samples, + u_isamples: 1/this.properties.samples, + }; + this.frame = 0; + } + + LGraphTextureLinearAvgSmooth.title = "Lineal Avg Smooth"; + LGraphTextureLinearAvgSmooth.desc = "Smooth texture linearly over time"; + + LGraphTextureLinearAvgSmooth["@samples"] = { type: "number", min: 1, max: 64, step: 1, precision: 1 }; + + LGraphTextureLinearAvgSmooth.prototype.getPreviewTexture = function() { + return this._temp_texture2; + } + + LGraphTextureLinearAvgSmooth.prototype.onExecute = function() { + + var tex = this.getInputData(0); + if (!tex || !this.isOutputConnected(0)) { + return; + } + + if (!LGraphTextureLinearAvgSmooth._shader) { + LGraphTextureLinearAvgSmooth._shader_copy = new GL.Shader( GL.Shader.SCREEN_VERTEX_SHADER, LGraphTextureLinearAvgSmooth.pixel_shader_copy ); + LGraphTextureLinearAvgSmooth._shader_avg = new GL.Shader( GL.Shader.SCREEN_VERTEX_SHADER, LGraphTextureLinearAvgSmooth.pixel_shader_avg ); + } + + var samples = clamp(this.properties.samples,0,64); + var frame = this.frame; + var interval = this.properties.frames_interval; + + if( interval == 0 || frame % interval == 0 ) { + var temp = this._temp_texture; + if ( !temp || temp.type != tex.type || temp.width != samples ) { + var options = { + type: tex.type, + format: gl.RGBA, + filter: gl.NEAREST, + }; + this._temp_texture = new GL.Texture( samples, 1, options ); + this._temp_texture2 = new GL.Texture( samples, 1, options ); + this._temp_texture_out = new GL.Texture( 1, 1, options ); + } + + var tempA = this._temp_texture; + var tempB = this._temp_texture2; + + var shader_copy = LGraphTextureLinearAvgSmooth._shader_copy; + var shader_avg = LGraphTextureLinearAvgSmooth._shader_avg; + var uniforms = this._uniforms; + uniforms.u_samples = samples; + uniforms.u_isamples = 1.0 / samples; + + gl.disable(gl.BLEND); + gl.disable(gl.DEPTH_TEST); + tempA.drawTo(function() { + tempB.bind(1); + tex.toViewport( shader_copy, uniforms ); + }); + + this._temp_texture_out.drawTo(function() { + tempA.toViewport( shader_avg, uniforms ); + }); + + this.setOutputData( 0, this._temp_texture_out ); + + // swap + this._temp_texture = tempB; + this._temp_texture2 = tempA; + } else + this.setOutputData(0, this._temp_texture_out); + this.setOutputData(1, this._temp_texture2); + this.frame++; + }; + + LGraphTextureLinearAvgSmooth.pixel_shader_copy = + "precision highp float;\n\ + precision highp float;\n\ + uniform sampler2D u_texture;\n\ + uniform sampler2D u_textureB;\n\ + uniform float u_isamples;\n\ + varying vec2 v_coord;\n\ + \n\ + void main() {\n\ + if( v_coord.x <= u_isamples )\n\ + gl_FragColor = texture2D( u_texture, vec2(0.5) );\n\ + else\n\ + gl_FragColor = texture2D( u_textureB, v_coord - vec2(u_isamples,0.0) );\n\ + }\n\ + "; + + LGraphTextureLinearAvgSmooth.pixel_shader_avg = + "precision highp float;\n\ + precision highp float;\n\ + uniform sampler2D u_texture;\n\ + uniform int u_samples;\n\ + uniform float u_isamples;\n\ + varying vec2 v_coord;\n\ + \n\ + void main() {\n\ + vec4 color = vec4(0.0);\n\ + for(int i = 0; i < 64; ++i)\n\ + {\n\ + color += texture2D( u_texture, vec2( float(i)*u_isamples,0.0) );\n\ + if(i == (u_samples - 1))\n\ + break;\n\ + }\n\ + gl_FragColor = color * u_isamples;\n\ + }\n\ + "; + + + LiteGraph.registerNodeType( "texture/linear_avg_smooth", LGraphTextureLinearAvgSmooth ); + + // Image To Texture ***************************************** + function LGraphImageToTexture() { + this.addInput("Image", "image"); + this.addOutput("", "Texture"); + this.properties = {}; + } + + LGraphImageToTexture.title = "Image to Texture"; + LGraphImageToTexture.desc = "Uploads an image to the GPU"; + // LGraphImageToTexture.widgets_info = { size: { widget:"combo", values:[0,32,64,128,256,512,1024,2048]} }; + + LGraphImageToTexture.prototype.onExecute = function() { + var img = this.getInputData(0); + if (!img) { + return; + } + + var width = img.videoWidth || img.width; + var height = img.videoHeight || img.height; + + // this is in case we are using a webgl canvas already, no need to reupload it + if (img.gltexture) { + this.setOutputData(0, img.gltexture); + return; + } + + var temp = this._temp_texture; + if (!temp || temp.width != width || temp.height != height) { + this._temp_texture = new GL.Texture(width, height, { + format: gl.RGBA, + filter: gl.LINEAR, + }); + } + + try { + this._temp_texture.uploadImage(img); + } catch (err) { + console.error("image comes from an unsafe location, cannot be uploaded to webgl: " + + err); + return; + } + + this.setOutputData(0, this._temp_texture); + }; + + LiteGraph.registerNodeType( + "texture/imageToTexture", + LGraphImageToTexture, + ); + + // Texture LUT ***************************************** + function LGraphTextureLUT() { + this.addInput("Texture", "Texture"); + this.addInput("LUT", "Texture"); + this.addInput("Intensity", "number"); + this.addOutput("", "Texture"); + this.properties = { enabled: true, intensity: 1, precision: LGraphTexture.DEFAULT, texture: null }; + + if (!LGraphTextureLUT._shader) { + LGraphTextureLUT._shader = new GL.Shader( Shader.SCREEN_VERTEX_SHADER, LGraphTextureLUT.pixel_shader ); + } + } + + LGraphTextureLUT.widgets_info = { + texture: { widget: "texture" }, + precision: { widget: "combo", values: LGraphTexture.MODE_VALUES }, + }; + + LGraphTextureLUT.title = "LUT"; + LGraphTextureLUT.desc = "Apply LUT to Texture"; + + LGraphTextureLUT.prototype.onExecute = function() { + if (!this.isOutputConnected(0)) { + return; + } // saves work + + var tex = this.getInputData(0); + + if (this.properties.precision === LGraphTexture.PASS_THROUGH || this.properties.enabled === false) { + this.setOutputData(0, tex); + return; + } + + if (!tex) { + return; + } + + var lut_tex = this.getInputData(1); + + if (!lut_tex) { + lut_tex = LGraphTexture.getTexture(this.properties.texture); + } + + if (!lut_tex) { + this.setOutputData(0, tex); + return; + } + + lut_tex.bind(0); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); + gl.texParameteri( + gl.TEXTURE_2D, + gl.TEXTURE_WRAP_S, + gl.CLAMP_TO_EDGE, + ); + gl.texParameteri( + gl.TEXTURE_2D, + gl.TEXTURE_WRAP_T, + gl.CLAMP_TO_EDGE, + ); + gl.bindTexture(gl.TEXTURE_2D, null); + + var intensity = this.properties.intensity; + if (this.isInputConnected(2)) { + this.properties.intensity = intensity = this.getInputData(2); + } + + this._tex = LGraphTexture.getTargetTexture( + tex, + this._tex, + this.properties.precision, + ); + + // var mesh = Mesh.getScreenQuad(); + + this._tex.drawTo(function() { + lut_tex.bind(1); + tex.toViewport(LGraphTextureLUT._shader, { + u_texture: 0, + u_textureB: 1, + u_amount: intensity, + }); + }); + + this.setOutputData(0, this._tex); + }; + + LGraphTextureLUT.pixel_shader = + "precision highp float;\n\ + precision highp float;\n\ + varying vec2 v_coord;\n\ + uniform sampler2D u_texture;\n\ + uniform sampler2D u_textureB;\n\ + uniform float u_amount;\n\ + \n\ + void main() {\n\ + lowp vec4 textureColor = clamp( texture2D(u_texture, v_coord), vec4(0.0), vec4(1.0) );\n\ + mediump float blueColor = textureColor.b * 63.0;\n\ + mediump vec2 quad1;\n\ + quad1.y = floor(floor(blueColor) / 8.0);\n\ + quad1.x = floor(blueColor) - (quad1.y * 8.0);\n\ + mediump vec2 quad2;\n\ + quad2.y = floor(ceil(blueColor) / 8.0);\n\ + quad2.x = ceil(blueColor) - (quad2.y * 8.0);\n\ + highp vec2 texPos1;\n\ + texPos1.x = (quad1.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.r);\n\ + texPos1.y = 1.0 - ((quad1.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.g));\n\ + highp vec2 texPos2;\n\ + texPos2.x = (quad2.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.r);\n\ + texPos2.y = 1.0 - ((quad2.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.g));\n\ + lowp vec4 newColor1 = texture2D(u_textureB, texPos1);\n\ + lowp vec4 newColor2 = texture2D(u_textureB, texPos2);\n\ + lowp vec4 newColor = mix(newColor1, newColor2, fract(blueColor));\n\ + gl_FragColor = vec4( mix( textureColor.rgb, newColor.rgb, u_amount), textureColor.w);\n\ + }\n\ + "; + + LiteGraph.registerNodeType("texture/LUT", LGraphTextureLUT); + + + // Texture LUT ***************************************** + function LGraphTextureEncode() { + this.addInput("Texture", "Texture"); + this.addInput("Atlas", "Texture"); + this.addOutput("", "Texture"); + this.properties = { enabled: true, num_row_symbols: 4, symbol_size: 16, brightness: 1, colorize: false, filter: false, invert: false, precision: LGraphTexture.DEFAULT, generate_mipmaps: false, texture: null }; + + if (!LGraphTextureEncode._shader) { + LGraphTextureEncode._shader = new GL.Shader( Shader.SCREEN_VERTEX_SHADER, LGraphTextureEncode.pixel_shader ); + } + + this._uniforms = { + u_texture: 0, + u_textureB: 1, + u_row_simbols: 4, + u_simbol_size: 16, + u_res: vec2.create(), + }; + } + + LGraphTextureEncode.widgets_info = { + texture: { widget: "texture" }, + precision: { widget: "combo", values: LGraphTexture.MODE_VALUES }, + }; + + LGraphTextureEncode.title = "Encode"; + LGraphTextureEncode.desc = "Apply a texture atlas to encode a texture"; + + LGraphTextureEncode.prototype.onExecute = function() { + if (!this.isOutputConnected(0)) { + return; + } // saves work + + var tex = this.getInputData(0); + + if (this.properties.precision === LGraphTexture.PASS_THROUGH || this.properties.enabled === false) { + this.setOutputData(0, tex); + return; + } + + if (!tex) { + return; + } + + var symbols_tex = this.getInputData(1); + + if (!symbols_tex) { + symbols_tex = LGraphTexture.getTexture(this.properties.texture); + } + + if (!symbols_tex) { + this.setOutputData(0, tex); + return; + } + + symbols_tex.bind(0); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, this.properties.filter ? gl.LINEAR : gl.NEAREST ); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, this.properties.filter ? gl.LINEAR : gl.NEAREST ); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE ); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE ); + gl.bindTexture(gl.TEXTURE_2D, null); + + var uniforms = this._uniforms; + uniforms.u_row_simbols = Math.floor(this.properties.num_row_symbols); + uniforms.u_symbol_size = this.properties.symbol_size; + uniforms.u_brightness = this.properties.brightness; + uniforms.u_invert = this.properties.invert ? 1 : 0; + uniforms.u_colorize = this.properties.colorize ? 1 : 0; + + this._tex = LGraphTexture.getTargetTexture( tex, this._tex, this.properties.precision ); + uniforms.u_res[0] = this._tex.width; + uniforms.u_res[1] = this._tex.height; + this._tex.bind(0); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST ); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST ); + + this._tex.drawTo(function() { + symbols_tex.bind(1); + tex.toViewport(LGraphTextureEncode._shader, uniforms); + }); + + if (this.properties.generate_mipmaps) { + this._tex.bind(0); + gl.generateMipmap(this._tex.texture_type); + this._tex.unbind(0); + } + + this.setOutputData(0, this._tex); + }; + + LGraphTextureEncode.pixel_shader = + "precision highp float;\n\ + precision highp float;\n\ + varying vec2 v_coord;\n\ + uniform sampler2D u_texture;\n\ + uniform sampler2D u_textureB;\n\ + uniform float u_row_simbols;\n\ + uniform float u_symbol_size;\n\ + uniform float u_brightness;\n\ + uniform float u_invert;\n\ + uniform float u_colorize;\n\ + uniform vec2 u_res;\n\ + \n\ + void main() {\n\ + vec2 total_symbols = u_res / u_symbol_size;\n\ + vec2 uv = floor(v_coord * total_symbols) / total_symbols; //pixelate \n\ + vec2 local_uv = mod(v_coord * u_res, u_symbol_size) / u_symbol_size;\n\ + lowp vec4 textureColor = texture2D(u_texture, uv );\n\ + float lum = clamp(u_brightness * (textureColor.x + textureColor.y + textureColor.z)/3.0,0.0,1.0);\n\ + if( u_invert == 1.0 ) lum = 1.0 - lum;\n\ + float index = floor( lum * (u_row_simbols * u_row_simbols - 1.0));\n\ + float col = mod( index, u_row_simbols );\n\ + float row = u_row_simbols - floor( index / u_row_simbols ) - 1.0;\n\ + vec2 simbol_uv = ( vec2( col, row ) + local_uv ) / u_row_simbols;\n\ + vec4 color = texture2D( u_textureB, simbol_uv );\n\ + if(u_colorize == 1.0)\n\ + color *= textureColor;\n\ + gl_FragColor = color;\n\ + }\n\ + "; + + LiteGraph.registerNodeType("texture/encode", LGraphTextureEncode); + + // Texture Channels ***************************************** + function LGraphTextureChannels() { + this.addInput("Texture", "Texture"); + + this.addOutput("R", "Texture"); + this.addOutput("G", "Texture"); + this.addOutput("B", "Texture"); + this.addOutput("A", "Texture"); + + // this.properties = { use_single_channel: true }; + if (!LGraphTextureChannels._shader) { + LGraphTextureChannels._shader = new GL.Shader( + Shader.SCREEN_VERTEX_SHADER, + LGraphTextureChannels.pixel_shader, + ); + } + } + + LGraphTextureChannels.title = "Texture to Channels"; + LGraphTextureChannels.desc = "Split texture channels"; + + LGraphTextureChannels.prototype.onExecute = function() { + var texA = this.getInputData(0); + if (!texA) { + return; + } + + if (!this._channels) { + this._channels = Array(4); + } + + // var format = this.properties.use_single_channel ? gl.LUMINANCE : gl.RGBA; //not supported by WebGL1 + var format = gl.RGB; + var connections = 0; + for (var i = 0; i < 4; i++) { + if (this.isOutputConnected(i)) { + if ( + !this._channels[i] || + this._channels[i].width != texA.width || + this._channels[i].height != texA.height || + this._channels[i].type != texA.type || + this._channels[i].format != format + ) { + this._channels[i] = new GL.Texture( + texA.width, + texA.height, + { + type: texA.type, + format: format, + filter: gl.LINEAR, + }, + ); + } + connections++; + } else { + this._channels[i] = null; + } + } + + if (!connections) { + return; + } + + gl.disable(gl.BLEND); + gl.disable(gl.DEPTH_TEST); + + var mesh = Mesh.getScreenQuad(); + var shader = LGraphTextureChannels._shader; + var masks = [ + [1, 0, 0, 0], + [0, 1, 0, 0], + [0, 0, 1, 0], + [0, 0, 0, 1], + ]; + + for (var i = 0; i < 4; i++) { + if (!this._channels[i]) { + continue; + } + + this._channels[i].drawTo(function() { + texA.bind(0); + shader + .uniforms({ u_texture: 0, u_mask: masks[i] }) + .draw(mesh); + }); + this.setOutputData(i, this._channels[i]); + } + }; + + LGraphTextureChannels.pixel_shader = + "precision highp float;\n\ + precision highp float;\n\ + varying vec2 v_coord;\n\ + uniform sampler2D u_texture;\n\ + uniform vec4 u_mask;\n\ + \n\ + void main() {\n\ + gl_FragColor = vec4( vec3( length( texture2D(u_texture, v_coord) * u_mask )), 1.0 );\n\ + }\n\ + "; + + LiteGraph.registerNodeType( + "texture/textureChannels", + LGraphTextureChannels, + ); + + // Texture Channels to Texture ***************************************** + function LGraphChannelsTexture() { + this.addInput("R", "Texture"); + this.addInput("G", "Texture"); + this.addInput("B", "Texture"); + this.addInput("A", "Texture"); + + this.addOutput("Texture", "Texture"); + + this.properties = { + precision: LGraphTexture.DEFAULT, + R: 1, + G: 1, + B: 1, + A: 1, + }; + this._color = vec4.create(); + this._uniforms = { + u_textureR: 0, + u_textureG: 1, + u_textureB: 2, + u_textureA: 3, + u_color: this._color, + }; + } + + LGraphChannelsTexture.title = "Channels to Texture"; + LGraphChannelsTexture.desc = "Split texture channels"; + LGraphChannelsTexture.widgets_info = {precision: { widget: "combo", values: LGraphTexture.MODE_VALUES }}; + + LGraphChannelsTexture.prototype.onExecute = function() { + var white = LGraphTexture.getWhiteTexture(); + var texR = this.getInputData(0) || white; + var texG = this.getInputData(1) || white; + var texB = this.getInputData(2) || white; + var texA = this.getInputData(3) || white; + + gl.disable(gl.BLEND); + gl.disable(gl.DEPTH_TEST); + + var mesh = Mesh.getScreenQuad(); + if (!LGraphChannelsTexture._shader) { + LGraphChannelsTexture._shader = new GL.Shader( + Shader.SCREEN_VERTEX_SHADER, + LGraphChannelsTexture.pixel_shader, + ); + } + var shader = LGraphChannelsTexture._shader; + + var w = Math.max(texR.width, texG.width, texB.width, texA.width); + var h = Math.max( + texR.height, + texG.height, + texB.height, + texA.height, + ); + var type = + this.properties.precision == LGraphTexture.HIGH + ? LGraphTexture.HIGH_PRECISION_FORMAT + : gl.UNSIGNED_BYTE; + + if ( + !this._texture || + this._texture.width != w || + this._texture.height != h || + this._texture.type != type + ) { + this._texture = new GL.Texture(w, h, { + type: type, + format: gl.RGBA, + filter: gl.LINEAR, + }); + } + + var color = this._color; + color[0] = this.properties.R; + color[1] = this.properties.G; + color[2] = this.properties.B; + color[3] = this.properties.A; + var uniforms = this._uniforms; + + this._texture.drawTo(function() { + texR.bind(0); + texG.bind(1); + texB.bind(2); + texA.bind(3); + shader.uniforms(uniforms).draw(mesh); + }); + this.setOutputData(0, this._texture); + }; + + LGraphChannelsTexture.pixel_shader = + "precision highp float;\n\ + precision highp float;\n\ + varying vec2 v_coord;\n\ + uniform sampler2D u_textureR;\n\ + uniform sampler2D u_textureG;\n\ + uniform sampler2D u_textureB;\n\ + uniform sampler2D u_textureA;\n\ + uniform vec4 u_color;\n\ + \n\ + void main() {\n\ + gl_FragColor = u_color * vec4( \ + texture2D(u_textureR, v_coord).r,\ + texture2D(u_textureG, v_coord).r,\ + texture2D(u_textureB, v_coord).r,\ + texture2D(u_textureA, v_coord).r);\n\ + }\n\ + "; + + LiteGraph.registerNodeType( + "texture/channelsTexture", + LGraphChannelsTexture, + ); + + // Texture Color ***************************************** + function LGraphTextureColor() { + this.addOutput("Texture", "Texture"); + + this._tex_color = vec4.create(); + this.properties = { + color: vec4.create(), + precision: LGraphTexture.DEFAULT, + }; + } + + LGraphTextureColor.title = "Color"; + LGraphTextureColor.desc = + "Generates a 1x1 texture with a constant color"; + + LGraphTextureColor.widgets_info = {precision: { widget: "combo", values: LGraphTexture.MODE_VALUES }}; + + LGraphTextureColor.prototype.onDrawBackground = function(ctx) { + var c = this.properties.color; + ctx.fillStyle = + "rgb(" + + Math.floor(clamp(c[0], 0, 1) * 255) + + "," + + Math.floor(clamp(c[1], 0, 1) * 255) + + "," + + Math.floor(clamp(c[2], 0, 1) * 255) + + ")"; + if (this.flags.collapsed) { + this.boxcolor = ctx.fillStyle; + } else { + ctx.fillRect(0, 0, this.size[0], this.size[1]); + } + }; + + LGraphTextureColor.prototype.onExecute = function() { + var type = + this.properties.precision == LGraphTexture.HIGH + ? LGraphTexture.HIGH_PRECISION_FORMAT + : gl.UNSIGNED_BYTE; + + if (!this._tex || this._tex.type != type) { + this._tex = new GL.Texture(1, 1, { + format: gl.RGBA, + type: type, + minFilter: gl.NEAREST, + }); + } + var color = this.properties.color; + + if (this.inputs) { + for (var i = 0; i < this.inputs.length; i++) { + var input = this.inputs[i]; + var v = this.getInputData(i); + if (v === undefined) { + continue; + } + switch (input.name) { + case "RGB": + case "RGBA": + color.set(v); + break; + case "R": + color[0] = v; + break; + case "G": + color[1] = v; + break; + case "B": + color[2] = v; + break; + case "A": + color[3] = v; + break; + } + } + } + + if (vec4.sqrDist(this._tex_color, color) > 0.001) { + this._tex_color.set(color); + this._tex.fill(color); + } + this.setOutputData(0, this._tex); + }; + + LGraphTextureColor.prototype.onGetInputs = function() { + return [ + ["RGB", "vec3"], + ["RGBA", "vec4"], + ["R", "number"], + ["G", "number"], + ["B", "number"], + ["A", "number"], + ]; + }; + + LiteGraph.registerNodeType("texture/color", LGraphTextureColor); + + // Texture Channels to Texture ***************************************** + function LGraphTextureGradient() { + this.addInput("A", "color"); + this.addInput("B", "color"); + this.addOutput("Texture", "Texture"); + + this.properties = { + angle: 0, + scale: 1, + A: [0, 0, 0], + B: [1, 1, 1], + texture_size: 32, + }; + if (!LGraphTextureGradient._shader) { + LGraphTextureGradient._shader = new GL.Shader( + Shader.SCREEN_VERTEX_SHADER, + LGraphTextureGradient.pixel_shader, + ); + } + + this._uniforms = { + u_angle: 0, + u_colorA: vec3.create(), + u_colorB: vec3.create(), + }; + } + + LGraphTextureGradient.title = "Gradient"; + LGraphTextureGradient.desc = "Generates a gradient"; + LGraphTextureGradient["@A"] = { type: "color" }; + LGraphTextureGradient["@B"] = { type: "color" }; + LGraphTextureGradient["@texture_size"] = { + type: "enum", + values: [32, 64, 128, 256, 512], + }; + + LGraphTextureGradient.prototype.onExecute = function() { + gl.disable(gl.BLEND); + gl.disable(gl.DEPTH_TEST); + + var mesh = GL.Mesh.getScreenQuad(); + var shader = LGraphTextureGradient._shader; + + var A = this.getInputData(0); + if (!A) { + A = this.properties.A; + } + var B = this.getInputData(1); + if (!B) { + B = this.properties.B; + } + + // angle and scale + for (var i = 2; i < this.inputs.length; i++) { + var input = this.inputs[i]; + var v = this.getInputData(i); + if (v === undefined) { + continue; + } + this.properties[input.name] = v; + } + + var uniforms = this._uniforms; + this._uniforms.u_angle = this.properties.angle * DEG2RAD; + this._uniforms.u_scale = this.properties.scale; + vec3.copy(uniforms.u_colorA, A); + vec3.copy(uniforms.u_colorB, B); + + var size = parseInt(this.properties.texture_size); + if (!this._tex || this._tex.width != size) { + this._tex = new GL.Texture(size, size, { + format: gl.RGB, + filter: gl.LINEAR, + }); + } + + this._tex.drawTo(function() { + shader.uniforms(uniforms).draw(mesh); + }); + this.setOutputData(0, this._tex); + }; + + LGraphTextureGradient.prototype.onGetInputs = function() { + return [["angle", "number"], ["scale", "number"]]; + }; + + LGraphTextureGradient.pixel_shader = + "precision highp float;\n\ + precision highp float;\n\ + varying vec2 v_coord;\n\ + uniform float u_angle;\n\ + uniform float u_scale;\n\ + uniform vec3 u_colorA;\n\ + uniform vec3 u_colorB;\n\ + \n\ + vec2 rotate(vec2 v, float angle)\n\ + {\n\ + vec2 result;\n\ + float _cos = cos(angle);\n\ + float _sin = sin(angle);\n\ + result.x = v.x * _cos - v.y * _sin;\n\ + result.y = v.x * _sin + v.y * _cos;\n\ + return result;\n\ + }\n\ + void main() {\n\ + float f = (rotate(u_scale * (v_coord - vec2(0.5)), u_angle) + vec2(0.5)).x;\n\ + vec3 color = mix(u_colorA,u_colorB,clamp(f,0.0,1.0));\n\ + gl_FragColor = vec4(color,1.0);\n\ + }\n\ + "; + + LiteGraph.registerNodeType("texture/gradient", LGraphTextureGradient); + + // Texture Mix ***************************************** + function LGraphTextureMix() { + this.addInput("A", "Texture"); + this.addInput("B", "Texture"); + this.addInput("Mixer", "Texture"); + + this.addOutput("Texture", "Texture"); + this.properties = { factor: 0.5, size_from_biggest: true, invert: false, precision: LGraphTexture.DEFAULT }; + this._uniforms = { + u_textureA: 0, + u_textureB: 1, + u_textureMix: 2, + u_mix: vec4.create(), + }; + } + + LGraphTextureMix.title = "Mix"; + LGraphTextureMix.desc = "Generates a texture mixing two textures"; + + LGraphTextureMix.widgets_info = {precision: { widget: "combo", values: LGraphTexture.MODE_VALUES }}; + + LGraphTextureMix.prototype.onExecute = function() { + var texA = this.getInputData(0); + + if (!this.isOutputConnected(0)) { + return; + } // saves work + + if (this.properties.precision === LGraphTexture.PASS_THROUGH) { + this.setOutputData(0, texA); + return; + } + + var texB = this.getInputData(1); + if (!texA || !texB) { + return; + } + + var texMix = this.getInputData(2); + + var factor = this.getInputData(3); + + this._tex = LGraphTexture.getTargetTexture( + this.properties.size_from_biggest && texB.width > texA.width ? texB : texA, + this._tex, + this.properties.precision, + ); + + gl.disable(gl.BLEND); + gl.disable(gl.DEPTH_TEST); + + var mesh = Mesh.getScreenQuad(); + var shader = null; + var uniforms = this._uniforms; + if (texMix) { + shader = LGraphTextureMix._shader_tex; + if (!shader) { + shader = LGraphTextureMix._shader_tex = new GL.Shader( + Shader.SCREEN_VERTEX_SHADER, + LGraphTextureMix.pixel_shader, + { MIX_TEX: "" }, + ); + } + } else { + shader = LGraphTextureMix._shader_factor; + if (!shader) { + shader = LGraphTextureMix._shader_factor = new GL.Shader( + Shader.SCREEN_VERTEX_SHADER, + LGraphTextureMix.pixel_shader, + ); + } + var f = factor == null ? this.properties.factor : factor; + uniforms.u_mix.set([f, f, f, f]); + } + + var invert = this.properties.invert; + + this._tex.drawTo(function() { + texA.bind( invert ? 1 : 0 ); + texB.bind( invert ? 0 : 1 ); + if (texMix) { + texMix.bind(2); + } + shader.uniforms(uniforms).draw(mesh); + }); + + this.setOutputData(0, this._tex); + }; + + LGraphTextureMix.prototype.onGetInputs = function() { + return [["factor", "number"]]; + }; + + LGraphTextureMix.pixel_shader = + "precision highp float;\n\ + precision highp float;\n\ + varying vec2 v_coord;\n\ + uniform sampler2D u_textureA;\n\ + uniform sampler2D u_textureB;\n\ + #ifdef MIX_TEX\n\ + uniform sampler2D u_textureMix;\n\ + #else\n\ + uniform vec4 u_mix;\n\ + #endif\n\ + \n\ + void main() {\n\ + #ifdef MIX_TEX\n\ + vec4 f = texture2D(u_textureMix, v_coord);\n\ + #else\n\ + vec4 f = u_mix;\n\ + #endif\n\ + gl_FragColor = mix( texture2D(u_textureA, v_coord), texture2D(u_textureB, v_coord), f );\n\ + }\n\ + "; + + LiteGraph.registerNodeType("texture/mix", LGraphTextureMix); + + // Texture Edges detection ***************************************** + function LGraphTextureEdges() { + this.addInput("Tex.", "Texture"); + + this.addOutput("Edges", "Texture"); + this.properties = { + invert: true, + threshold: false, + factor: 1, + precision: LGraphTexture.DEFAULT, + }; + + if (!LGraphTextureEdges._shader) { + LGraphTextureEdges._shader = new GL.Shader( + Shader.SCREEN_VERTEX_SHADER, + LGraphTextureEdges.pixel_shader, + ); + } + } + + LGraphTextureEdges.title = "Edges"; + LGraphTextureEdges.desc = "Detects edges"; + + LGraphTextureEdges.widgets_info = {precision: { widget: "combo", values: LGraphTexture.MODE_VALUES }}; + + LGraphTextureEdges.prototype.onExecute = function() { + if (!this.isOutputConnected(0)) { + return; + } // saves work + + var tex = this.getInputData(0); + + if (this.properties.precision === LGraphTexture.PASS_THROUGH) { + this.setOutputData(0, tex); + return; + } + + if (!tex) { + return; + } + + this._tex = LGraphTexture.getTargetTexture( + tex, + this._tex, + this.properties.precision, + ); + + gl.disable(gl.BLEND); + gl.disable(gl.DEPTH_TEST); + + var mesh = Mesh.getScreenQuad(); + var shader = LGraphTextureEdges._shader; + var invert = this.properties.invert; + var factor = this.properties.factor; + var threshold = this.properties.threshold ? 1 : 0; + + this._tex.drawTo(function() { + tex.bind(0); + shader + .uniforms({ + u_texture: 0, + u_isize: [1 / tex.width, 1 / tex.height], + u_factor: factor, + u_threshold: threshold, + u_invert: invert ? 1 : 0, + }) + .draw(mesh); + }); + + this.setOutputData(0, this._tex); + }; + + LGraphTextureEdges.pixel_shader = + "precision highp float;\n\ + precision highp float;\n\ + varying vec2 v_coord;\n\ + uniform sampler2D u_texture;\n\ + uniform vec2 u_isize;\n\ + uniform int u_invert;\n\ + uniform float u_factor;\n\ + uniform float u_threshold;\n\ + \n\ + void main() {\n\ + vec4 center = texture2D(u_texture, v_coord);\n\ + vec4 up = texture2D(u_texture, v_coord + u_isize * vec2(0.0,1.0) );\n\ + vec4 down = texture2D(u_texture, v_coord + u_isize * vec2(0.0,-1.0) );\n\ + vec4 left = texture2D(u_texture, v_coord + u_isize * vec2(1.0,0.0) );\n\ + vec4 right = texture2D(u_texture, v_coord + u_isize * vec2(-1.0,0.0) );\n\ + vec4 diff = abs(center - up) + abs(center - down) + abs(center - left) + abs(center - right);\n\ + diff *= u_factor;\n\ + if(u_invert == 1)\n\ + diff.xyz = vec3(1.0) - diff.xyz;\n\ + if( u_threshold == 0.0 )\n\ + gl_FragColor = vec4( diff.xyz, center.a );\n\ + else\n\ + gl_FragColor = vec4( diff.x > 0.5 ? 1.0 : 0.0, diff.y > 0.5 ? 1.0 : 0.0, diff.z > 0.5 ? 1.0 : 0.0, center.a );\n\ + }\n\ + "; + + LiteGraph.registerNodeType("texture/edges", LGraphTextureEdges); + + // Texture Depth ***************************************** + function LGraphTextureDepthRange() { + this.addInput("Texture", "Texture"); + this.addInput("Distance", "number"); + this.addInput("Range", "number"); + this.addOutput("Texture", "Texture"); + this.properties = { + distance: 100, + range: 50, + only_depth: false, + high_precision: false, + }; + this._uniforms = { + u_texture: 0, + u_distance: 100, + u_range: 50, + u_camera_planes: null, + }; + } + + LGraphTextureDepthRange.title = "Depth Range"; + LGraphTextureDepthRange.desc = "Generates a texture with a depth range"; + + LGraphTextureDepthRange.prototype.onExecute = function() { + if (!this.isOutputConnected(0)) { + return; + } // saves work + + var tex = this.getInputData(0); + if (!tex) { + return; + } + + var precision = gl.UNSIGNED_BYTE; + if (this.properties.high_precision) { + precision = gl.half_float_ext ? gl.HALF_FLOAT_OES : gl.FLOAT; + } + + if ( + !this._temp_texture || + this._temp_texture.type != precision || + this._temp_texture.width != tex.width || + this._temp_texture.height != tex.height + ) { + this._temp_texture = new GL.Texture(tex.width, tex.height, { + type: precision, + format: gl.RGBA, + filter: gl.LINEAR, + }); + } + + var uniforms = this._uniforms; + + // iterations + var distance = this.properties.distance; + if (this.isInputConnected(1)) { + distance = this.getInputData(1); + this.properties.distance = distance; + } + + var range = this.properties.range; + if (this.isInputConnected(2)) { + range = this.getInputData(2); + this.properties.range = range; + } + + uniforms.u_distance = distance; + uniforms.u_range = range; + + gl.disable(gl.BLEND); + gl.disable(gl.DEPTH_TEST); + var mesh = Mesh.getScreenQuad(); + if (!LGraphTextureDepthRange._shader) { + LGraphTextureDepthRange._shader = new GL.Shader( + Shader.SCREEN_VERTEX_SHADER, + LGraphTextureDepthRange.pixel_shader, + ); + LGraphTextureDepthRange._shader_onlydepth = new GL.Shader( + Shader.SCREEN_VERTEX_SHADER, + LGraphTextureDepthRange.pixel_shader, + { ONLY_DEPTH: "" }, + ); + } + var shader = this.properties.only_depth + ? LGraphTextureDepthRange._shader_onlydepth + : LGraphTextureDepthRange._shader; + + // NEAR AND FAR PLANES + var planes = null; + if (tex.near_far_planes) { + planes = tex.near_far_planes; + } else if (window.LS && LS.Renderer._main_camera) { + planes = LS.Renderer._main_camera._uniforms.u_camera_planes; + } else { + planes = [0.1, 1000]; + } // hardcoded + uniforms.u_camera_planes = planes; + + this._temp_texture.drawTo(function() { + tex.bind(0); + shader.uniforms(uniforms).draw(mesh); + }); + + this._temp_texture.near_far_planes = planes; + this.setOutputData(0, this._temp_texture); + }; + + LGraphTextureDepthRange.pixel_shader = + "precision highp float;\n\ + precision highp float;\n\ + varying vec2 v_coord;\n\ + uniform sampler2D u_texture;\n\ + uniform vec2 u_camera_planes;\n\ + uniform float u_distance;\n\ + uniform float u_range;\n\ + \n\ + float LinearDepth()\n\ + {\n\ + float zNear = u_camera_planes.x;\n\ + float zFar = u_camera_planes.y;\n\ + float depth = texture2D(u_texture, v_coord).x;\n\ + depth = depth * 2.0 - 1.0;\n\ + return zNear * (depth + 1.0) / (zFar + zNear - depth * (zFar - zNear));\n\ + }\n\ + \n\ + void main() {\n\ + float depth = LinearDepth();\n\ + #ifdef ONLY_DEPTH\n\ + gl_FragColor = vec4(depth);\n\ + #else\n\ + float diff = abs(depth * u_camera_planes.y - u_distance);\n\ + float dof = 1.0;\n\ + if(diff <= u_range)\n\ + dof = diff / u_range;\n\ + gl_FragColor = vec4(dof);\n\ + #endif\n\ + }\n\ + "; + + LiteGraph.registerNodeType( "texture/depth_range", LGraphTextureDepthRange ); + + + // Texture Depth ***************************************** + function LGraphTextureLinearDepth() { + this.addInput("Texture", "Texture"); + this.addOutput("Texture", "Texture"); + this.properties = { + precision: LGraphTexture.DEFAULT, + invert: false, + }; + this._uniforms = { + u_texture: 0, + u_camera_planes: null, // filled later + u_ires: vec2.create(), + }; + } + + LGraphTextureLinearDepth.widgets_info = {precision: { widget: "combo", values: LGraphTexture.MODE_VALUES }}; + + LGraphTextureLinearDepth.title = "Linear Depth"; + LGraphTextureLinearDepth.desc = "Creates a color texture with linear depth"; + + LGraphTextureLinearDepth.prototype.onExecute = function() { + if (!this.isOutputConnected(0)) { + return; + } // saves work + + var tex = this.getInputData(0); + if (!tex || (tex.format != gl.DEPTH_COMPONENT && tex.format != gl.DEPTH_STENCIL) ) { + return; + } + + var precision = this.properties.precision == LGraphTexture.HIGH ? gl.HIGH_PRECISION_FORMAT : gl.UNSIGNED_BYTE; + + if ( !this._temp_texture || this._temp_texture.type != precision || this._temp_texture.width != tex.width || this._temp_texture.height != tex.height ) { + this._temp_texture = new GL.Texture(tex.width, tex.height, { + type: precision, + format: gl.RGB, + filter: gl.LINEAR, + }); + } + + var uniforms = this._uniforms; + uniforms.u_invert = this.properties.invert ? 1 : 0; + + gl.disable(gl.BLEND); + gl.disable(gl.DEPTH_TEST); + var mesh = Mesh.getScreenQuad(); + if(!LGraphTextureLinearDepth._shader) + LGraphTextureLinearDepth._shader = new GL.Shader( GL.Shader.SCREEN_VERTEX_SHADER, LGraphTextureLinearDepth.pixel_shader); + var shader = LGraphTextureLinearDepth._shader; + + // NEAR AND FAR PLANES + var planes = null; + if (tex.near_far_planes) { + planes = tex.near_far_planes; + } else if (window.LS && LS.Renderer._main_camera) { + planes = LS.Renderer._main_camera._uniforms.u_camera_planes; + } else { + planes = [0.1, 1000]; + } // hardcoded + uniforms.u_camera_planes = planes; + // uniforms.u_ires.set([1/tex.width, 1/tex.height]); + uniforms.u_ires.set([0,0]); + + this._temp_texture.drawTo(function() { + tex.bind(0); + shader.uniforms(uniforms).draw(mesh); + }); + + this._temp_texture.near_far_planes = planes; + this.setOutputData(0, this._temp_texture); + }; + + LGraphTextureLinearDepth.pixel_shader = + "precision highp float;\n\ + precision highp float;\n\ + varying vec2 v_coord;\n\ + uniform sampler2D u_texture;\n\ + uniform vec2 u_camera_planes;\n\ + uniform int u_invert;\n\ + uniform vec2 u_ires;\n\ + \n\ + void main() {\n\ + float zNear = u_camera_planes.x;\n\ + float zFar = u_camera_planes.y;\n\ + float depth = texture2D(u_texture, v_coord + u_ires*0.5).x * 2.0 - 1.0;\n\ + float f = zNear * (depth + 1.0) / (zFar + zNear - depth * (zFar - zNear));\n\ + if( u_invert == 1 )\n\ + f = 1.0 - f;\n\ + gl_FragColor = vec4(vec3(f),1.0);\n\ + }\n\ + "; + + LiteGraph.registerNodeType( "texture/linear_depth", LGraphTextureLinearDepth ); + + // Texture Blur ***************************************** + function LGraphTextureBlur() { + this.addInput("Texture", "Texture"); + this.addInput("Iterations", "number"); + this.addInput("Intensity", "number"); + this.addOutput("Blurred", "Texture"); + this.properties = { + intensity: 1, + iterations: 1, + preserve_aspect: false, + scale: [1, 1], + precision: LGraphTexture.DEFAULT, + }; + } + + LGraphTextureBlur.title = "Blur"; + LGraphTextureBlur.desc = "Blur a texture"; + + LGraphTextureBlur.widgets_info = {precision: { widget: "combo", values: LGraphTexture.MODE_VALUES }}; + + LGraphTextureBlur.max_iterations = 20; + + LGraphTextureBlur.prototype.onExecute = function() { + var tex = this.getInputData(0); + if (!tex) { + return; + } + + if (!this.isOutputConnected(0)) { + return; + } // saves work + + var temp = this._final_texture; + + if ( + !temp || + temp.width != tex.width || + temp.height != tex.height || + temp.type != tex.type + ) { + // we need two textures to do the blurring + // this._temp_texture = new GL.Texture( tex.width, tex.height, { type: tex.type, format: gl.RGBA, filter: gl.LINEAR }); + temp = this._final_texture = new GL.Texture( + tex.width, + tex.height, + { type: tex.type, format: gl.RGBA, filter: gl.LINEAR }, + ); + } + + // iterations + var iterations = this.properties.iterations; + if (this.isInputConnected(1)) { + iterations = this.getInputData(1); + this.properties.iterations = iterations; + } + iterations = Math.min( + Math.floor(iterations), + LGraphTextureBlur.max_iterations, + ); + if (iterations == 0) { + // skip blurring + this.setOutputData(0, tex); + return; + } + + var intensity = this.properties.intensity; + if (this.isInputConnected(2)) { + intensity = this.getInputData(2); + this.properties.intensity = intensity; + } + + // blur sometimes needs an aspect correction + var aspect = LiteGraph.camera_aspect; + if (!aspect && window.gl !== undefined) { + aspect = gl.canvas.height / gl.canvas.width; + } + if (!aspect) { + aspect = 1; + } + aspect = this.properties.preserve_aspect ? aspect : 1; + + var scale = this.properties.scale || [1, 1]; + tex.applyBlur(aspect * scale[0], scale[1], intensity, temp); + for (var i = 1; i < iterations; ++i) { + temp.applyBlur( + aspect * scale[0] * (i + 1), + scale[1] * (i + 1), + intensity, + ); + } + + this.setOutputData(0, temp); + }; + + /* +LGraphTextureBlur.pixel_shader = "precision highp float;\n\ + precision highp float;\n\ + varying vec2 v_coord;\n\ + uniform sampler2D u_texture;\n\ + uniform vec2 u_offset;\n\ + uniform float u_intensity;\n\ + void main() {\n\ + vec4 sum = vec4(0.0);\n\ + vec4 center = texture2D(u_texture, v_coord);\n\ + sum += texture2D(u_texture, v_coord + u_offset * -4.0) * 0.05/0.98;\n\ + sum += texture2D(u_texture, v_coord + u_offset * -3.0) * 0.09/0.98;\n\ + sum += texture2D(u_texture, v_coord + u_offset * -2.0) * 0.12/0.98;\n\ + sum += texture2D(u_texture, v_coord + u_offset * -1.0) * 0.15/0.98;\n\ + sum += center * 0.16/0.98;\n\ + sum += texture2D(u_texture, v_coord + u_offset * 4.0) * 0.05/0.98;\n\ + sum += texture2D(u_texture, v_coord + u_offset * 3.0) * 0.09/0.98;\n\ + sum += texture2D(u_texture, v_coord + u_offset * 2.0) * 0.12/0.98;\n\ + sum += texture2D(u_texture, v_coord + u_offset * 1.0) * 0.15/0.98;\n\ + gl_FragColor = u_intensity * sum;\n\ + }\n\ + "; +*/ + + LiteGraph.registerNodeType("texture/blur", LGraphTextureBlur); + + // Independent glow FX + // based on https://catlikecoding.com/unity/tutorials/advanced-rendering/bloom/ + function FXGlow() { + this.intensity = 0.5; + this.persistence = 0.6; + this.iterations = 8; + this.threshold = 0.8; + this.scale = 1; + + this.dirt_texture = null; + this.dirt_factor = 0.5; + + this._textures = []; + this._uniforms = { + u_intensity: 1, + u_texture: 0, + u_glow_texture: 1, + u_threshold: 0, + u_texel_size: vec2.create(), + }; + } + + FXGlow.prototype.applyFX = function( tex, output_texture, glow_texture, average_texture ) { + + var width = tex.width; + var height = tex.height; + + var texture_info = { + format: tex.format, + type: tex.type, + minFilter: GL.LINEAR, + magFilter: GL.LINEAR, + wrap: gl.CLAMP_TO_EDGE, + }; + + var uniforms = this._uniforms; + var textures = this._textures; + + // cut + var shader = FXGlow._cut_shader; + if (!shader) { + shader = FXGlow._cut_shader = new GL.Shader( + GL.Shader.SCREEN_VERTEX_SHADER, + FXGlow.cut_pixel_shader, + ); + } + + gl.disable(gl.DEPTH_TEST); + gl.disable(gl.BLEND); + + uniforms.u_threshold = this.threshold; + var currentDestination = (textures[0] = GL.Texture.getTemporary( + width, + height, + texture_info, + )); + tex.blit( currentDestination, shader.uniforms(uniforms) ); + var currentSource = currentDestination; + + var iterations = this.iterations; + iterations = clamp(iterations, 1, 16) | 0; + var texel_size = uniforms.u_texel_size; + var intensity = this.intensity; + + uniforms.u_intensity = 1; + uniforms.u_delta = this.scale; // 1 + + // downscale/upscale shader + var shader = FXGlow._shader; + if (!shader) { + shader = FXGlow._shader = new GL.Shader( + GL.Shader.SCREEN_VERTEX_SHADER, + FXGlow.scale_pixel_shader, + ); + } + + var i = 1; + // downscale + for (; i < iterations; i++) { + width = width >> 1; + if ((height | 0) > 1) { + height = height >> 1; + } + if (width < 2) { + break; + } + currentDestination = textures[i] = GL.Texture.getTemporary( + width, + height, + texture_info, + ); + texel_size[0] = 1 / currentSource.width; + texel_size[1] = 1 / currentSource.height; + currentSource.blit( + currentDestination, + shader.uniforms(uniforms), + ); + currentSource = currentDestination; + } + + // average + if (average_texture) { + texel_size[0] = 1 / currentSource.width; + texel_size[1] = 1 / currentSource.height; + uniforms.u_intensity = intensity; + uniforms.u_delta = 1; + currentSource.blit(average_texture, shader.uniforms(uniforms)); + } + + // upscale and blend + gl.enable(gl.BLEND); + gl.blendFunc(gl.ONE, gl.ONE); + uniforms.u_intensity = this.persistence; + uniforms.u_delta = 0.5; + + // i-=2 => -1 to point to last element in array, -1 to go to texture above + for ( i -= 2; i >= 0; i-- ) { + currentDestination = textures[i]; + textures[i] = null; + texel_size[0] = 1 / currentSource.width; + texel_size[1] = 1 / currentSource.height; + currentSource.blit( + currentDestination, + shader.uniforms(uniforms), + ); + GL.Texture.releaseTemporary(currentSource); + currentSource = currentDestination; + } + gl.disable(gl.BLEND); + + // glow + if (glow_texture) { + currentSource.blit(glow_texture); + } + + // final composition + if ( output_texture ) { + var final_texture = output_texture; + var dirt_texture = this.dirt_texture; + var dirt_factor = this.dirt_factor; + uniforms.u_intensity = intensity; + + shader = dirt_texture + ? FXGlow._dirt_final_shader + : FXGlow._final_shader; + if (!shader) { + if (dirt_texture) { + shader = FXGlow._dirt_final_shader = new GL.Shader( + GL.Shader.SCREEN_VERTEX_SHADER, + FXGlow.final_pixel_shader, + { USE_DIRT: "" }, + ); + } else { + shader = FXGlow._final_shader = new GL.Shader( + GL.Shader.SCREEN_VERTEX_SHADER, + FXGlow.final_pixel_shader, + ); + } + } + + final_texture.drawTo(function() { + tex.bind(0); + currentSource.bind(1); + if (dirt_texture) { + shader.setUniform("u_dirt_factor", dirt_factor); + shader.setUniform( + "u_dirt_texture", + dirt_texture.bind(2), + ); + } + shader.toViewport(uniforms); + }); + } + + GL.Texture.releaseTemporary(currentSource); + }; + + FXGlow.cut_pixel_shader = + "precision highp float;\n\ + varying vec2 v_coord;\n\ + uniform sampler2D u_texture;\n\ + uniform float u_threshold;\n\ + void main() {\n\ + gl_FragColor = max( texture2D( u_texture, v_coord ) - vec4( u_threshold ), vec4(0.0) );\n\ + }"; + + FXGlow.scale_pixel_shader = + "precision highp float;\n\ + varying vec2 v_coord;\n\ + uniform sampler2D u_texture;\n\ + uniform vec2 u_texel_size;\n\ + uniform float u_delta;\n\ + uniform float u_intensity;\n\ + \n\ + vec4 sampleBox(vec2 uv) {\n\ + vec4 o = u_texel_size.xyxy * vec2(-u_delta, u_delta).xxyy;\n\ + vec4 s = texture2D( u_texture, uv + o.xy ) + texture2D( u_texture, uv + o.zy) + texture2D( u_texture, uv + o.xw) + texture2D( u_texture, uv + o.zw);\n\ + return s * 0.25;\n\ + }\n\ + void main() {\n\ + gl_FragColor = u_intensity * sampleBox( v_coord );\n\ + }"; + + FXGlow.final_pixel_shader = + "precision highp float;\n\ + varying vec2 v_coord;\n\ + uniform sampler2D u_texture;\n\ + uniform sampler2D u_glow_texture;\n\ + #ifdef USE_DIRT\n\ + uniform sampler2D u_dirt_texture;\n\ + #endif\n\ + uniform vec2 u_texel_size;\n\ + uniform float u_delta;\n\ + uniform float u_intensity;\n\ + uniform float u_dirt_factor;\n\ + \n\ + vec4 sampleBox(vec2 uv) {\n\ + vec4 o = u_texel_size.xyxy * vec2(-u_delta, u_delta).xxyy;\n\ + vec4 s = texture2D( u_glow_texture, uv + o.xy ) + texture2D( u_glow_texture, uv + o.zy) + texture2D( u_glow_texture, uv + o.xw) + texture2D( u_glow_texture, uv + o.zw);\n\ + return s * 0.25;\n\ + }\n\ + void main() {\n\ + vec4 glow = sampleBox( v_coord );\n\ + #ifdef USE_DIRT\n\ + glow = mix( glow, glow * texture2D( u_dirt_texture, v_coord ), u_dirt_factor );\n\ + #endif\n\ + gl_FragColor = texture2D( u_texture, v_coord ) + u_intensity * glow;\n\ + }"; + + + // Texture Glow ***************************************** + function LGraphTextureGlow() { + this.addInput("in", "Texture"); + this.addInput("dirt", "Texture"); + this.addOutput("out", "Texture"); + this.addOutput("glow", "Texture"); + this.properties = { + enabled: true, + intensity: 1, + persistence: 0.99, + iterations: 16, + threshold: 0, + scale: 1, + dirt_factor: 0.5, + precision: LGraphTexture.DEFAULT, + }; + + this.fx = new FXGlow(); + } + + LGraphTextureGlow.title = "Glow"; + LGraphTextureGlow.desc = "Filters a texture giving it a glow effect"; + + LGraphTextureGlow.widgets_info = { + iterations: { + type: "number", + min: 0, + max: 16, + step: 1, + precision: 0, + }, + threshold: { + type: "number", + min: 0, + max: 10, + step: 0.01, + precision: 2, + }, + precision: { widget: "combo", values: LGraphTexture.MODE_VALUES }, + }; + + LGraphTextureGlow.prototype.onGetInputs = function() { + return [ + ["enabled", "boolean"], + ["threshold", "number"], + ["intensity", "number"], + ["persistence", "number"], + ["iterations", "number"], + ["dirt_factor", "number"], + ]; + }; + + LGraphTextureGlow.prototype.onGetOutputs = function() { + return [["average", "Texture"]]; + }; + + LGraphTextureGlow.prototype.onExecute = function() { + var tex = this.getInputData(0); + if (!tex) { + return; + } + + if (!this.isAnyOutputConnected()) { + return; + } // saves work + + if ( + this.properties.precision === LGraphTexture.PASS_THROUGH || + this.getInputOrProperty("enabled") === false + ) { + this.setOutputData(0, tex); + return; + } + + var width = tex.width; + var height = tex.height; + + var fx = this.fx; + fx.threshold = this.getInputOrProperty("threshold"); + fx.iterations = this.getInputOrProperty("iterations"); + fx.intensity = this.getInputOrProperty("intensity"); + fx.persistence = this.getInputOrProperty("persistence"); + fx.dirt_texture = this.getInputData(1); + fx.dirt_factor = this.getInputOrProperty("dirt_factor"); + fx.scale = this.properties.scale; + + var type = LGraphTexture.getTextureType( this.properties.precision, tex ); + + var average_texture = null; + if (this.isOutputConnected(2)) { + average_texture = this._average_texture; + if ( + !average_texture || + average_texture.type != tex.type || + average_texture.format != tex.format + ) { + average_texture = this._average_texture = new GL.Texture( + 1, + 1, + { + type: tex.type, + format: tex.format, + filter: gl.LINEAR, + }, + ); + } + } + + var glow_texture = null; + if (this.isOutputConnected(1)) { + glow_texture = this._glow_texture; + if ( + !glow_texture || + glow_texture.width != tex.width || + glow_texture.height != tex.height || + glow_texture.type != type || + glow_texture.format != tex.format + ) { + glow_texture = this._glow_texture = new GL.Texture( + tex.width, + tex.height, + { type: type, format: tex.format, filter: gl.LINEAR }, + ); + } + } + + var final_texture = null; + if (this.isOutputConnected(0)) { + final_texture = this._final_texture; + if ( + !final_texture || + final_texture.width != tex.width || + final_texture.height != tex.height || + final_texture.type != type || + final_texture.format != tex.format + ) { + final_texture = this._final_texture = new GL.Texture( + tex.width, + tex.height, + { type: type, format: tex.format, filter: gl.LINEAR }, + ); + } + + } + + // apply FX + fx.applyFX(tex, final_texture, glow_texture, average_texture ); + + if (this.isOutputConnected(0)) + this.setOutputData(0, final_texture); + + if (this.isOutputConnected(1)) + this.setOutputData(1, average_texture); + + if (this.isOutputConnected(2)) + this.setOutputData(2, glow_texture); + }; + + LiteGraph.registerNodeType("texture/glow", LGraphTextureGlow); + + // Texture Filter ***************************************** + function LGraphTextureKuwaharaFilter() { + this.addInput("Texture", "Texture"); + this.addOutput("Filtered", "Texture"); + this.properties = { intensity: 1, radius: 5 }; + } + + LGraphTextureKuwaharaFilter.title = "Kuwahara Filter"; + LGraphTextureKuwaharaFilter.desc = + "Filters a texture giving an artistic oil canvas painting"; + + LGraphTextureKuwaharaFilter.max_radius = 10; + LGraphTextureKuwaharaFilter._shaders = []; + + LGraphTextureKuwaharaFilter.prototype.onExecute = function() { + var tex = this.getInputData(0); + if (!tex) { + return; + } + + if (!this.isOutputConnected(0)) { + return; + } // saves work + + var temp = this._temp_texture; + + if ( + !temp || + temp.width != tex.width || + temp.height != tex.height || + temp.type != tex.type + ) { + this._temp_texture = new GL.Texture(tex.width, tex.height, { + type: tex.type, + format: gl.RGBA, + filter: gl.LINEAR, + }); + } + + // iterations + var radius = this.properties.radius; + radius = Math.min( + Math.floor(radius), + LGraphTextureKuwaharaFilter.max_radius, + ); + if (radius == 0) { + // skip blurring + this.setOutputData(0, tex); + return; + } + + var intensity = this.properties.intensity; + + // blur sometimes needs an aspect correction + var aspect = LiteGraph.camera_aspect; + if (!aspect && window.gl !== undefined) { + aspect = gl.canvas.height / gl.canvas.width; + } + if (!aspect) { + aspect = 1; + } + aspect = this.properties.preserve_aspect ? aspect : 1; + + if (!LGraphTextureKuwaharaFilter._shaders[radius]) { + LGraphTextureKuwaharaFilter._shaders[radius] = new GL.Shader( + Shader.SCREEN_VERTEX_SHADER, + LGraphTextureKuwaharaFilter.pixel_shader, + { RADIUS: radius.toFixed(0) }, + ); + } + + var shader = LGraphTextureKuwaharaFilter._shaders[radius]; + var mesh = GL.Mesh.getScreenQuad(); + tex.bind(0); + + this._temp_texture.drawTo(function() { + shader + .uniforms({ + u_texture: 0, + u_intensity: intensity, + u_resolution: [tex.width, tex.height], + u_iResolution: [1 / tex.width, 1 / tex.height], + }) + .draw(mesh); + }); + + this.setOutputData(0, this._temp_texture); + }; + + // from https://www.shadertoy.com/view/MsXSz4 + LGraphTextureKuwaharaFilter.pixel_shader = + "\n\ +precision highp float;\n\ +varying vec2 v_coord;\n\ +uniform sampler2D u_texture;\n\ +uniform float u_intensity;\n\ +uniform vec2 u_resolution;\n\ +uniform vec2 u_iResolution;\n\ +#ifndef RADIUS\n\ + #define RADIUS 7\n\ +#endif\n\ +void main() {\n\ +\n\ + const int radius = RADIUS;\n\ + vec2 fragCoord = v_coord;\n\ + vec2 src_size = u_iResolution;\n\ + vec2 uv = v_coord;\n\ + float n = float((radius + 1) * (radius + 1));\n\ + int i;\n\ + int j;\n\ + vec3 m0 = vec3(0.0); vec3 m1 = vec3(0.0); vec3 m2 = vec3(0.0); vec3 m3 = vec3(0.0);\n\ + vec3 s0 = vec3(0.0); vec3 s1 = vec3(0.0); vec3 s2 = vec3(0.0); vec3 s3 = vec3(0.0);\n\ + vec3 c;\n\ + \n\ + for (int j = -radius; j <= 0; ++j) {\n\ + for (int i = -radius; i <= 0; ++i) {\n\ + c = texture2D(u_texture, uv + vec2(i,j) * src_size).rgb;\n\ + m0 += c;\n\ + s0 += c * c;\n\ + }\n\ + }\n\ + \n\ + for (int j = -radius; j <= 0; ++j) {\n\ + for (int i = 0; i <= radius; ++i) {\n\ + c = texture2D(u_texture, uv + vec2(i,j) * src_size).rgb;\n\ + m1 += c;\n\ + s1 += c * c;\n\ + }\n\ + }\n\ + \n\ + for (int j = 0; j <= radius; ++j) {\n\ + for (int i = 0; i <= radius; ++i) {\n\ + c = texture2D(u_texture, uv + vec2(i,j) * src_size).rgb;\n\ + m2 += c;\n\ + s2 += c * c;\n\ + }\n\ + }\n\ + \n\ + for (int j = 0; j <= radius; ++j) {\n\ + for (int i = -radius; i <= 0; ++i) {\n\ + c = texture2D(u_texture, uv + vec2(i,j) * src_size).rgb;\n\ + m3 += c;\n\ + s3 += c * c;\n\ + }\n\ + }\n\ + \n\ + float min_sigma2 = 1e+2;\n\ + m0 /= n;\n\ + s0 = abs(s0 / n - m0 * m0);\n\ + \n\ + float sigma2 = s0.r + s0.g + s0.b;\n\ + if (sigma2 < min_sigma2) {\n\ + min_sigma2 = sigma2;\n\ + gl_FragColor = vec4(m0, 1.0);\n\ + }\n\ + \n\ + m1 /= n;\n\ + s1 = abs(s1 / n - m1 * m1);\n\ + \n\ + sigma2 = s1.r + s1.g + s1.b;\n\ + if (sigma2 < min_sigma2) {\n\ + min_sigma2 = sigma2;\n\ + gl_FragColor = vec4(m1, 1.0);\n\ + }\n\ + \n\ + m2 /= n;\n\ + s2 = abs(s2 / n - m2 * m2);\n\ + \n\ + sigma2 = s2.r + s2.g + s2.b;\n\ + if (sigma2 < min_sigma2) {\n\ + min_sigma2 = sigma2;\n\ + gl_FragColor = vec4(m2, 1.0);\n\ + }\n\ + \n\ + m3 /= n;\n\ + s3 = abs(s3 / n - m3 * m3);\n\ + \n\ + sigma2 = s3.r + s3.g + s3.b;\n\ + if (sigma2 < min_sigma2) {\n\ + min_sigma2 = sigma2;\n\ + gl_FragColor = vec4(m3, 1.0);\n\ + }\n\ +}\n\ +"; + + LiteGraph.registerNodeType( + "texture/kuwahara", + LGraphTextureKuwaharaFilter, + ); + + // Texture ***************************************** + function LGraphTextureXDoGFilter() { + this.addInput("Texture", "Texture"); + this.addOutput("Filtered", "Texture"); + this.properties = { + sigma: 1.4, + k: 1.6, + p: 21.7, + epsilon: 79, + phi: 0.017, + }; + } + + LGraphTextureXDoGFilter.title = "XDoG Filter"; + LGraphTextureXDoGFilter.desc = + "Filters a texture giving an artistic ink style"; + + LGraphTextureXDoGFilter.max_radius = 10; + LGraphTextureXDoGFilter._shaders = []; + + LGraphTextureXDoGFilter.prototype.onExecute = function() { + var tex = this.getInputData(0); + if (!tex) { + return; + } + + if (!this.isOutputConnected(0)) { + return; + } // saves work + + var temp = this._temp_texture; + if ( + !temp || + temp.width != tex.width || + temp.height != tex.height || + temp.type != tex.type + ) { + this._temp_texture = new GL.Texture(tex.width, tex.height, { + type: tex.type, + format: gl.RGBA, + filter: gl.LINEAR, + }); + } + + if (!LGraphTextureXDoGFilter._xdog_shader) { + LGraphTextureXDoGFilter._xdog_shader = new GL.Shader( + Shader.SCREEN_VERTEX_SHADER, + LGraphTextureXDoGFilter.xdog_pixel_shader, + ); + } + var shader = LGraphTextureXDoGFilter._xdog_shader; + var mesh = GL.Mesh.getScreenQuad(); + + var sigma = this.properties.sigma; + var k = this.properties.k; + var p = this.properties.p; + var epsilon = this.properties.epsilon; + var phi = this.properties.phi; + tex.bind(0); + this._temp_texture.drawTo(function() { + shader + .uniforms({ + src: 0, + sigma: sigma, + k: k, + p: p, + epsilon: epsilon, + phi: phi, + cvsWidth: tex.width, + cvsHeight: tex.height, + }) + .draw(mesh); + }); + + this.setOutputData(0, this._temp_texture); + }; + + // from https://github.com/RaymondMcGuire/GPU-Based-Image-Processing-Tools/blob/master/lib_webgl/scripts/main.js + LGraphTextureXDoGFilter.xdog_pixel_shader = + "\n\ +precision highp float;\n\ +uniform sampler2D src;\n\n\ +uniform float cvsHeight;\n\ +uniform float cvsWidth;\n\n\ +uniform float sigma;\n\ +uniform float k;\n\ +uniform float p;\n\ +uniform float epsilon;\n\ +uniform float phi;\n\ +varying vec2 v_coord;\n\n\ +float cosh(float val)\n\ +{\n\ + float tmp = exp(val);\n\ + float cosH = (tmp + 1.0 / tmp) / 2.0;\n\ + return cosH;\n\ +}\n\n\ +float tanh(float val)\n\ +{\n\ + float tmp = exp(val);\n\ + float tanH = (tmp - 1.0 / tmp) / (tmp + 1.0 / tmp);\n\ + return tanH;\n\ +}\n\n\ +float sinh(float val)\n\ +{\n\ + float tmp = exp(val);\n\ + float sinH = (tmp - 1.0 / tmp) / 2.0;\n\ + return sinH;\n\ +}\n\n\ +void main(void){\n\ + vec3 destColor = vec3(0.0);\n\ + float tFrag = 1.0 / cvsHeight;\n\ + float sFrag = 1.0 / cvsWidth;\n\ + vec2 Frag = vec2(sFrag,tFrag);\n\ + vec2 uv = gl_FragCoord.st;\n\ + float twoSigmaESquared = 2.0 * sigma * sigma;\n\ + float twoSigmaRSquared = twoSigmaESquared * k * k;\n\ + int halfWidth = int(ceil( 1.0 * sigma * k ));\n\n\ + const int MAX_NUM_ITERATION = 99999;\n\ + vec2 sum = vec2(0.0);\n\ + vec2 norm = vec2(0.0);\n\n\ + for(int cnt=0;cnt (2*halfWidth+1)*(2*halfWidth+1)){break;}\n\ + int i = int(cnt / (2*halfWidth+1)) - halfWidth;\n\ + int j = cnt - halfWidth - int(cnt / (2*halfWidth+1)) * (2*halfWidth+1);\n\n\ + float d = length(vec2(i,j));\n\ + vec2 kernel = vec2( exp( -d * d / twoSigmaESquared ), \n\ + exp( -d * d / twoSigmaRSquared ));\n\n\ + vec2 L = texture2D(src, (uv + vec2(i,j)) * Frag).xx;\n\n\ + norm += kernel;\n\ + sum += kernel * L;\n\ + }\n\n\ + sum /= norm;\n\n\ + float H = 100.0 * ((1.0 + p) * sum.x - p * sum.y);\n\ + float edge = ( H > epsilon )? 1.0 : 1.0 + tanh( phi * (H - epsilon));\n\ + destColor = vec3(edge);\n\ + gl_FragColor = vec4(destColor, 1.0);\n\ +}"; + + LiteGraph.registerNodeType("texture/xDoG", LGraphTextureXDoGFilter); + + // Texture Webcam ***************************************** + function LGraphTextureWebcam() { + this.addOutput("Webcam", "Texture"); + this.properties = { texture_name: "", facingMode: "user" }; + this.boxcolor = "black"; + this.version = 0; + } + + LGraphTextureWebcam.title = "Webcam"; + LGraphTextureWebcam.desc = "Webcam texture"; + + LGraphTextureWebcam.is_webcam_open = false; + + LGraphTextureWebcam.prototype.openStream = function() { + 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. + var constraints = { + audio: false, + video: { facingMode: this.properties.facingMode }, + }; + navigator.mediaDevices + .getUserMedia(constraints) + .then(this.streamReady.bind(this)) + .catch(onFailSoHard); + + var that = this; + function onFailSoHard(e) { + LGraphTextureWebcam.is_webcam_open = false; + console.log("Webcam rejected", e); + that._webcam_stream = false; + that.boxcolor = "red"; + that.trigger("stream_error"); + } + }; + + LGraphTextureWebcam.prototype.closeStream = function() { + if (this._webcam_stream) { + var tracks = this._webcam_stream.getTracks(); + if (tracks.length) { + for (var i = 0; i < tracks.length; ++i) { + tracks[i].stop(); + } + } + LGraphTextureWebcam.is_webcam_open = false; + this._webcam_stream = null; + this._video = null; + this.boxcolor = "black"; + this.trigger("stream_closed"); + } + }; + + LGraphTextureWebcam.prototype.streamReady = function(localMediaStream) { + this._webcam_stream = localMediaStream; + // this._waiting_confirmation = false; + this.boxcolor = "green"; + var video = this._video; + if (!video) { + video = document.createElement("video"); + video.autoplay = true; + video.srcObject = 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. + LGraphTextureWebcam.is_webcam_open = true; + console.log(e); + }; + } + this.trigger("stream_ready", video); + }; + + LGraphTextureWebcam.prototype.onPropertyChanged = function( + name, + value, + ) { + if (name == "facingMode") { + this.properties.facingMode = value; + this.closeStream(); + this.openStream(); + } + }; + + LGraphTextureWebcam.prototype.onRemoved = function() { + if (!this._webcam_stream) { + return; + } + + var tracks = this._webcam_stream.getTracks(); + if (tracks.length) { + for (var i = 0; i < tracks.length; ++i) { + tracks[i].stop(); + } + } + + this._webcam_stream = null; + this._video = null; + }; + + LGraphTextureWebcam.prototype.onDrawBackground = function(ctx) { + if (this.flags.collapsed || this.size[1] <= 20) { + return; + } + + if (!this._video) { + return; + } + + // render to graph canvas + ctx.save(); + if (!ctx.webgl) { + // reverse image + ctx.drawImage(this._video, 0, 0, this.size[0], this.size[1]); + } else { + if (this._video_texture) { + ctx.drawImage( + this._video_texture, + 0, + 0, + this.size[0], + this.size[1], + ); + } + } + ctx.restore(); + }; + + LGraphTextureWebcam.prototype.onExecute = function() { + if (this._webcam_stream == null && !this._waiting_confirmation) { + this.openStream(); + } + + if (!this._video || !this._video.videoWidth) { + return; + } + + var width = this._video.videoWidth; + var height = this._video.videoHeight; + + var temp = this._video_texture; + if (!temp || temp.width != width || temp.height != height) { + this._video_texture = new GL.Texture(width, height, { + format: gl.RGB, + filter: gl.LINEAR, + }); + } + + this._video_texture.uploadImage(this._video); + this._video_texture.version = ++this.version; + + if (this.properties.texture_name) { + var container = LGraphTexture.getTexturesContainer(); + container[this.properties.texture_name] = this._video_texture; + } + + this.setOutputData(0, this._video_texture); + for (var i = 1; i < this.outputs.length; ++i) { + if (!this.outputs[i]) { + continue; + } + switch (this.outputs[i].name) { + case "width": + this.setOutputData(i, this._video.videoWidth); + break; + case "height": + this.setOutputData(i, this._video.videoHeight); + break; + } + } + }; + + LGraphTextureWebcam.prototype.onGetOutputs = function() { + return [ + ["width", "number"], + ["height", "number"], + ["stream_ready", LiteGraph.EVENT], + ["stream_closed", LiteGraph.EVENT], + ["stream_error", LiteGraph.EVENT], + ]; + }; + + LiteGraph.registerNodeType("texture/webcam", LGraphTextureWebcam); + + // from https://github.com/spite/Wagner + function LGraphLensFX() { + this.addInput("in", "Texture"); + this.addInput("f", "number"); + this.addOutput("out", "Texture"); + this.properties = { + enabled: true, + factor: 1, + precision: LGraphTexture.LOW, + }; + + this._uniforms = { u_texture: 0, u_factor: 1 }; + } + + LGraphLensFX.title = "Lens FX"; + LGraphLensFX.desc = "distortion and chromatic aberration"; + + LGraphLensFX.widgets_info = {precision: { widget: "combo", values: LGraphTexture.MODE_VALUES }}; + + LGraphLensFX.prototype.onGetInputs = function() { + return [["enabled", "boolean"]]; + }; + + LGraphLensFX.prototype.onExecute = function() { + var tex = this.getInputData(0); + if (!tex) { + return; + } + + if (!this.isOutputConnected(0)) { + return; + } // saves work + + if ( + this.properties.precision === LGraphTexture.PASS_THROUGH || + this.getInputOrProperty("enabled") === false + ) { + this.setOutputData(0, tex); + return; + } + + var temp = this._temp_texture; + if ( + !temp || + temp.width != tex.width || + temp.height != tex.height || + temp.type != tex.type + ) { + temp = this._temp_texture = new GL.Texture( + tex.width, + tex.height, + { type: tex.type, format: gl.RGBA, filter: gl.LINEAR }, + ); + } + + var shader = LGraphLensFX._shader; + if (!shader) { + shader = LGraphLensFX._shader = new GL.Shader( + GL.Shader.SCREEN_VERTEX_SHADER, + LGraphLensFX.pixel_shader, + ); + } + + var factor = this.getInputData(1); + if (factor == null) { + factor = this.properties.factor; + } + + var uniforms = this._uniforms; + uniforms.u_factor = factor; + + // apply shader + gl.disable(gl.DEPTH_TEST); + temp.drawTo(function() { + tex.bind(0); + shader.uniforms(uniforms).draw(GL.Mesh.getScreenQuad()); + }); + + this.setOutputData(0, temp); + }; + + LGraphLensFX.pixel_shader = + "precision highp float;\n\ + varying vec2 v_coord;\n\ + uniform sampler2D u_texture;\n\ + uniform float u_factor;\n\ + vec2 barrelDistortion(vec2 coord, float amt) {\n\ + vec2 cc = coord - 0.5;\n\ + float dist = dot(cc, cc);\n\ + return coord + cc * dist * amt;\n\ + }\n\ + \n\ + float sat( float t )\n\ + {\n\ + return clamp( t, 0.0, 1.0 );\n\ + }\n\ + \n\ + float linterp( float t ) {\n\ + return sat( 1.0 - abs( 2.0*t - 1.0 ) );\n\ + }\n\ + \n\ + float remap( float t, float a, float b ) {\n\ + return sat( (t - a) / (b - a) );\n\ + }\n\ + \n\ + vec4 spectrum_offset( float t ) {\n\ + vec4 ret;\n\ + float lo = step(t,0.5);\n\ + float hi = 1.0-lo;\n\ + float w = linterp( remap( t, 1.0/6.0, 5.0/6.0 ) );\n\ + ret = vec4(lo,1.0,hi, 1.) * vec4(1.0-w, w, 1.0-w, 1.);\n\ + \n\ + return pow( ret, vec4(1.0/2.2) );\n\ + }\n\ + \n\ + const float max_distort = 2.2;\n\ + const int num_iter = 12;\n\ + const float reci_num_iter_f = 1.0 / float(num_iter);\n\ + \n\ + void main()\n\ + { \n\ + vec2 uv=v_coord;\n\ + vec4 sumcol = vec4(0.0);\n\ + vec4 sumw = vec4(0.0); \n\ + for ( int i=0; i= res)\n\ + break;\n\ + iCount++;\n\ + }\n\ + float nf = n/normK;\n\ + return nf*nf*nf*nf;\n\ + }\n\ + void main() {\n\ + vec2 uv = v_coord * u_scale * u_viewport + u_offset * u_scale;\n\ + vec4 color = vec4( pNoise( uv, u_octaves ) * u_amplitude );\n\ + gl_FragColor = color;\n\ + }"; + + LiteGraph.registerNodeType("texture/perlin", LGraphTexturePerlin); + + function LGraphTextureCanvas2D() { + this.addInput("v"); + this.addOutput("out", "Texture"); + this.properties = { + code: LGraphTextureCanvas2D.default_code, + width: 512, + height: 512, + clear: true, + precision: LGraphTexture.DEFAULT, + use_html_canvas: false, + }; + this._func = null; + this._temp_texture = null; + this.compileCode(); + } + + LGraphTextureCanvas2D.title = "Canvas2D"; + LGraphTextureCanvas2D.desc = "Executes Canvas2D code inside a texture or the viewport."; + LGraphTextureCanvas2D.help = "Set width and height to 0 to match viewport size."; + + LGraphTextureCanvas2D.default_code = "//vars: canvas,ctx,time\nctx.fillStyle='red';\nctx.fillRect(0,0,50,50);\n"; + + LGraphTextureCanvas2D.widgets_info = { + precision: { widget: "combo", values: LGraphTexture.MODE_VALUES }, + code: { type: "code" }, + width: { type: "number", precision: 0, step: 1 }, + height: { type: "number", precision: 0, step: 1 }, + }; + + LGraphTextureCanvas2D.prototype.onPropertyChanged = function( name, value ) { + if (name == "code" ) + this.compileCode( value ); + } + + LGraphTextureCanvas2D.prototype.compileCode = function( code ) { + this._func = null; + if( !LiteGraph.allow_scripts ) + return; + + try { + this._func = new Function( "canvas", "ctx", "time", "script","v", code ); + this.boxcolor = "#00FF00"; + } catch (err) { + this.boxcolor = "#FF0000"; + console.error("Error parsing script"); + console.error(err); + } + }; + + LGraphTextureCanvas2D.prototype.onExecute = function() { + var func = this._func; + if (!func || !this.isOutputConnected(0)) { + return; + } + this.executeDraw( func ); + } + + LGraphTextureCanvas2D.prototype.executeDraw = function( func_context ) { + + var width = this.properties.width || gl.canvas.width; + var height = this.properties.height || gl.canvas.height; + var temp = this._temp_texture; + var type = LGraphTexture.getTextureType( this.properties.precision ); + if (!temp || temp.width != width || temp.height != height || temp.type != type ) { + temp = this._temp_texture = new GL.Texture(width, height, { + format: gl.RGBA, + filter: gl.LINEAR, + type: type, + }); + } + + var v = this.getInputData(0); + + var properties = this.properties; + var that = this; + var time = this.graph.getTime(); + var ctx = gl; + var canvas = gl.canvas; + if( this.properties.use_html_canvas || !global.enableWebGLCanvas ) { + if(!this._canvas) { + canvas = this._canvas = createCanvas(width.height); + ctx = this._ctx = canvas.getContext("2d"); + } else { + canvas = this._canvas; + ctx = this._ctx; + } + canvas.width = width; + canvas.height = height; + } + + if(ctx == gl) // using Canvas2DtoWebGL + temp.drawTo(function() { + gl.start2D(); + if(properties.clear) { + gl.clearColor(0,0,0,0); + gl.clear( gl.COLOR_BUFFER_BIT ); + } + + try { + if (func_context.draw) { + func_context.draw.call(that, canvas, ctx, time, func_context, v); + } else { + func_context.call(that, canvas, ctx, time, func_context,v); + } + that.boxcolor = "#00FF00"; + } catch (err) { + that.boxcolor = "#FF0000"; + console.error("Error executing script"); + console.error(err); + } + gl.finish2D(); + }); + else { // rendering to offscreen canvas and uploading to texture + if(properties.clear) + ctx.clearRect(0,0,canvas.width,canvas.height); + + try { + if (func_context.draw) { + func_context.draw.call(this, canvas, ctx, time, func_context, v); + } else { + func_context.call(this, canvas, ctx, time, func_context,v); + } + this.boxcolor = "#00FF00"; + } catch (err) { + this.boxcolor = "#FF0000"; + console.error("Error executing script"); + console.error(err); + } + temp.uploadImage( canvas ); + } + + this.setOutputData(0, temp); + }; + + LiteGraph.registerNodeType("texture/canvas2D", LGraphTextureCanvas2D); + + // To do chroma keying ***************** + + function LGraphTextureMatte() { + this.addInput("in", "Texture"); + + this.addOutput("out", "Texture"); + this.properties = { + key_color: vec3.fromValues(0, 1, 0), + threshold: 0.8, + slope: 0.2, + precision: LGraphTexture.DEFAULT, + }; + } + + LGraphTextureMatte.title = "Matte"; + LGraphTextureMatte.desc = "Extracts background"; + + LGraphTextureMatte.widgets_info = { + key_color: { widget: "color" }, + precision: { widget: "combo", values: LGraphTexture.MODE_VALUES }, + }; + + LGraphTextureMatte.prototype.onExecute = function() { + if (!this.isOutputConnected(0)) { + return; + } // saves work + + var tex = this.getInputData(0); + + if (this.properties.precision === LGraphTexture.PASS_THROUGH) { + this.setOutputData(0, tex); + return; + } + + if (!tex) { + return; + } + + this._tex = LGraphTexture.getTargetTexture( + tex, + this._tex, + this.properties.precision, + ); + + gl.disable(gl.BLEND); + gl.disable(gl.DEPTH_TEST); + + if (!this._uniforms) { + this._uniforms = { + u_texture: 0, + u_key_color: this.properties.key_color, + u_threshold: 1, + u_slope: 1, + }; + } + var uniforms = this._uniforms; + + var mesh = Mesh.getScreenQuad(); + var shader = LGraphTextureMatte._shader; + if (!shader) { + shader = LGraphTextureMatte._shader = new GL.Shader( + GL.Shader.SCREEN_VERTEX_SHADER, + LGraphTextureMatte.pixel_shader, + ); + } + + uniforms.u_key_color = this.properties.key_color; + uniforms.u_threshold = this.properties.threshold; + uniforms.u_slope = this.properties.slope; + + this._tex.drawTo(function() { + tex.bind(0); + shader.uniforms(uniforms).draw(mesh); + }); + + this.setOutputData(0, this._tex); + }; + + LGraphTextureMatte.pixel_shader = + "precision highp float;\n\ + varying vec2 v_coord;\n\ + uniform sampler2D u_texture;\n\ + uniform vec3 u_key_color;\n\ + uniform float u_threshold;\n\ + uniform float u_slope;\n\ + \n\ + void main() {\n\ + vec3 color = texture2D( u_texture, v_coord ).xyz;\n\ + float diff = length( normalize(color) - normalize(u_key_color) );\n\ + float edge = u_threshold * (1.0 - u_slope);\n\ + float alpha = smoothstep( edge, u_threshold, diff);\n\ + gl_FragColor = vec4( color, alpha );\n\ + }"; + + LiteGraph.registerNodeType("texture/matte", LGraphTextureMatte); + + //* ********************************** + function LGraphCubemapToTexture2D() { + this.addInput("in", "texture"); + this.addInput("yaw", "number"); + this.addOutput("out", "texture"); + this.properties = { yaw: 0 }; + } + + LGraphCubemapToTexture2D.title = "CubemapToTexture2D"; + LGraphCubemapToTexture2D.desc = "Transforms a CUBEMAP texture into a TEXTURE2D in Polar Representation"; + + LGraphCubemapToTexture2D.prototype.onExecute = function() { + if (!this.isOutputConnected(0)) + return; + + var tex = this.getInputData(0); + if ( !tex || tex.texture_type != GL.TEXTURE_CUBE_MAP ) + return; + if( this._last_tex && ( this._last_tex.height != tex.height || this._last_tex.type != tex.type )) + this._last_tex = null; + var yaw = this.getInputOrProperty("yaw"); + this._last_tex = GL.Texture.cubemapToTexture2D( tex, tex.height, this._last_tex, true, yaw ); + this.setOutputData( 0, this._last_tex ); + }; + + LiteGraph.registerNodeType( "texture/cubemapToTexture2D", LGraphCubemapToTexture2D ); +})(this); + +(function(global) { + + if (typeof GL == "undefined") + return; + + var LiteGraph = global.LiteGraph; + var LGraphCanvas = global.LGraphCanvas; + + var SHADERNODES_COLOR = "#345"; + + var LGShaders = LiteGraph.Shaders = {}; + + var GLSL_types = LGShaders.GLSL_types = ["float","vec2","vec3","vec4","mat3","mat4","sampler2D","samplerCube"]; + var GLSL_types_const = LGShaders.GLSL_types_const = ["float","vec2","vec3","vec4"]; + + var GLSL_functions_desc = { + "radians": "T radians(T degrees)", + "degrees": "T degrees(T radians)", + "sin": "T sin(T angle)", + "cos": "T cos(T angle)", + "tan": "T tan(T angle)", + "asin": "T asin(T x)", + "acos": "T acos(T x)", + "atan": "T atan(T x)", + "atan2": "T atan(T x,T y)", + "pow": "T pow(T x,T y)", + "exp": "T exp(T x)", + "log": "T log(T x)", + "exp2": "T exp2(T x)", + "log2": "T log2(T x)", + "sqrt": "T sqrt(T x)", + "inversesqrt": "T inversesqrt(T x)", + "abs": "T abs(T x)", + "sign": "T sign(T x)", + "floor": "T floor(T x)", + "round": "T round(T x)", + "ceil": "T ceil(T x)", + "fract": "T fract(T x)", + "mod": "T mod(T x,T y)", // "T mod(T x,float y)" + "min": "T min(T x,T y)", + "max": "T max(T x,T y)", + "clamp": "T clamp(T x,T minVal = 0.0,T maxVal = 1.0)", + "mix": "T mix(T x,T y,T a)", // "T mix(T x,T y,float a)" + "step": "T step(T edge, T edge2, T x)", // "T step(float edge, T x)" + "smoothstep": "T smoothstep(T edge, T edge2, T x)", // "T smoothstep(float edge, T x)" + "length": "float length(T x)", + "distance": "float distance(T p0, T p1)", + "normalize": "T normalize(T x)", + "dot": "float dot(T x,T y)", + "cross": "vec3 cross(vec3 x,vec3 y)", + "reflect": "vec3 reflect(vec3 V,vec3 N)", + "refract": "vec3 refract(vec3 V,vec3 N, float IOR)", + }; + + // parse them + var GLSL_functions = {}; + var GLSL_functions_name = []; + parseGLSLDescriptions(); + + LGShaders.ALL_TYPES = "float,vec2,vec3,vec4"; + + function parseGLSLDescriptions() { + GLSL_functions_name.length = 0; + + for(var i in GLSL_functions_desc) { + var op = GLSL_functions_desc[i]; + var index = op.indexOf(" "); + var return_type = op.substr(0,index); + var index2 = op.indexOf("(",index); + var func_name = op.substr(index,index2-index).trim(); + var params = op.substr(index2 + 1, op.length - index2 - 2).split(","); + for(var j in params) { + var p = params[j].split(" ").filter(function(a) { + return a; + }); + params[j] = { type: p[0].trim(), name: p[1].trim() }; + if(p[2] == "=") + params[j].value = p[3].trim(); + } + GLSL_functions[i] = { return_type: return_type, func: func_name, params: params }; + GLSL_functions_name.push( func_name ); + // console.log( GLSL_functions[i] ); + } + } + + // common actions to all shader node classes + function registerShaderNode( type, node_ctor ) { + // static attributes + node_ctor.color = SHADERNODES_COLOR; + node_ctor.filter = "shader"; + + // common methods + node_ctor.prototype.clearDestination = function() { + this.shader_destination = {}; + } + node_ctor.prototype.propagateDestination = function propagateDestination( dest_name ) { + this.shader_destination[dest_name] = true; + if(this.inputs) + for(var i = 0; i < this.inputs.length; ++i) { + var origin_node = this.getInputNode(i); + if(origin_node) + origin_node.propagateDestination( dest_name ); + } + } + if(!node_ctor.prototype.onPropertyChanged) + node_ctor.prototype.onPropertyChanged = function() { + if(this.graph) + this.graph._version++; + } + + /* + if(!node_ctor.prototype.onGetCode) + node_ctor.prototype.onGetCode = function() + { + //check destination to avoid lonely nodes + if(!this.shader_destination) + return; + //grab inputs with types + var inputs = []; + if(this.inputs) + for(var i = 0; i < this.inputs.length; ++i) + inputs.push({ type: this.getInputData(i), name: getInputLinkID(this,i) }); + var outputs = []; + if(this.outputs) + for(var i = 0; i < this.outputs.length; ++i) + outputs.push({ name: getOutputLinkID(this,i) }); + //pass to code func + var results = this.extractCode(inputs); + //grab output, pass to next + if(results) + for(var i = 0; i < results.length; ++i) + { + var r = results[i]; + if(!r) + continue; + this.setOutputData(i,r.value); + } + } + */ + + LiteGraph.registerNodeType( "shader::" + type, node_ctor ); + } + + function getShaderNodeVarName( node, name ) { + return "VAR_" + (name || "TEMP") + "_" + node.id; + } + + function getInputLinkID( node, slot ) { + if(!node.inputs) + return null; + var link = node.getInputLink( slot ); + if( !link ) + return null; + var origin_node = node.graph.getNodeById( link.origin_id ); + if( !origin_node ) + return null; + if(origin_node.getOutputVarName) + return origin_node.getOutputVarName(link.origin_slot); + // generate + return "link_" + origin_node.id + "_" + link.origin_slot; + } + + function getOutputLinkID( node, slot ) { + if (!node.isOutputConnected(slot)) + return null; + return "link_" + node.id + "_" + slot; + } + + LGShaders.registerShaderNode = registerShaderNode; + LGShaders.getInputLinkID = getInputLinkID; + LGShaders.getOutputLinkID = getOutputLinkID; + LGShaders.getShaderNodeVarName = getShaderNodeVarName; + LGShaders.parseGLSLDescriptions = parseGLSLDescriptions; + + // given a const number, it transform it to a string that matches a type + var valueToGLSL = LiteGraph.valueToGLSL = function valueToGLSL( v, type, precision ) { + var n = 5; // num decimals + if(precision != null) + n = precision; + if(!type) { + if(v.constructor === Number) + type = "float"; + else if(v.length) { + switch(v.length) { + case 2: type = "vec2"; break; + case 3: type = "vec3"; break; + case 4: type = "vec4"; break; + case 9: type = "mat3"; break; + case 16: type = "mat4"; break; + default: + throw("unknown type for glsl value size"); + } + } else + throw("unknown type for glsl value: " + v.constructor); + } + switch(type) { + case 'float': return v.toFixed(n); + case 'vec2': return "vec2(" + v[0].toFixed(n) + "," + v[1].toFixed(n) + ")"; + case 'color3': + case 'vec3': return "vec3(" + v[0].toFixed(n) + "," + v[1].toFixed(n) + "," + v[2].toFixed(n) + ")"; + case 'color4': + case 'vec4': return "vec4(" + v[0].toFixed(n) + "," + v[1].toFixed(n) + "," + v[2].toFixed(n) + "," + v[3].toFixed(n) + ")"; + case 'mat3': return "mat3(1.0,0.0,0.0,0.0,1.0,0.0,0.0,0.0,1.0)"; // not fully supported yet + case 'mat4': return "mat4(1.0,0.0,0.0,0.0,0.0,1.0,0.0,0.0,0.0,0.0,0.0,1.0,0.0,0.0,0.0,0.0,1.0)"; // not fully supported yet + default: + throw("unknown glsl type in valueToGLSL:", type); + } + } + + // makes sure that a var is of a type, and if not, it converts it + var varToTypeGLSL = LiteGraph.varToTypeGLSL = function varToTypeGLSL( v, input_type, output_type ) { + if(input_type == output_type) + return v; + if(v == null) + switch(output_type) { + case "float": return "0.0"; + case "vec2": return "vec2(0.0)"; + case "vec3": return "vec3(0.0)"; + case "vec4": return "vec4(0.0,0.0,0.0,1.0)"; + default: // null + return null; + } + + if(!output_type) + throw("error: no output type specified"); + if(output_type == "float") { + switch(input_type) { + // case "float": + case "vec2": + case "vec3": + case "vec4": + return v + ".x"; + default: // null + return "0.0"; + } + } else if(output_type == "vec2") { + switch(input_type) { + case "float": + return "vec2("+v+")"; + // case "vec2": + case "vec3": + case "vec4": + return v + ".xy"; + default: // null + return "vec2(0.0)"; + } + } else if(output_type == "vec3") { + switch(input_type) { + case "float": + return "vec3("+v+")"; + case "vec2": + return "vec3(" + v + ",0.0)"; + // case "vec3": + case "vec4": + return v + ".xyz"; + default: // null + return "vec3(0.0)"; + } + } else if(output_type == "vec4") { + switch(input_type) { + case "float": + return "vec4("+v+")"; + case "vec2": + return "vec4(" + v + ",0.0,1.0)"; + case "vec3": + return "vec4(" + v + ",1.0)"; + default: // null + return "vec4(0.0,0.0,0.0,1.0)"; + } + } + throw("type cannot be converted"); + } + + + // used to plug incompatible stuff + var convertVarToGLSLType = LiteGraph.convertVarToGLSLType = function convertVarToGLSLType( varname, type, target_type ) { + if(type == target_type) + return varname; + if(type == "float") + return target_type + "(" + varname + ")"; + if(target_type == "vec2") // works for vec2,vec3 and vec4 + return "vec2(" + varname + ".xy)"; + if(target_type == "vec3") { // works for vec2,vec3 and vec4 + if(type == "vec2") + return "vec3(" + varname + ",0.0)"; + if(type == "vec4") + return "vec4(" + varname + ".xyz)"; + } + if(target_type == "vec4") { + if(type == "vec2") + return "vec4(" + varname + ",0.0,0.0)"; + if(target_type == "vec3") + return "vec4(" + varname + ",1.0)"; + } + return null; + } + + // used to host a shader body ************************************** + function LGShaderContext() { + // to store the code template + this.vs_template = ""; + this.fs_template = ""; + + // required so nodes now where to fetch the input data + this.buffer_names = {uvs: "v_coord"}; + + this.extra = {}; // to store custom info from the nodes (like if this shader supports a feature, etc) + + this._functions = {}; + this._uniforms = {}; + this._codeparts = {}; + this._uniform_value = null; + } + + LGShaderContext.prototype.clear = function() { + this._uniforms = {}; + this._functions = {}; + this._codeparts = {}; + this._uniform_value = null; + + this.extra = {}; + } + + LGShaderContext.prototype.addUniform = function( name, type, value ) { + this._uniforms[name] = type; + if(value != null) { + if(!this._uniform_value) + this._uniform_value = {}; + this._uniform_value[name] = value; + } + } + + LGShaderContext.prototype.addFunction = function( name, code ) { + this._functions[name] = code; + } + + LGShaderContext.prototype.addCode = function( hook, code, destinations ) { + destinations = destinations || {"": ""}; + for(var i in destinations) { + var h = i ? i + "_" + hook : hook; + if(!this._codeparts[h]) + this._codeparts[h] = code + "\n"; + else + this._codeparts[h] += code + "\n"; + } + } + + // the system works by grabbing code fragments from every node and concatenating them in blocks depending on where must they be attached + LGShaderContext.prototype.computeCodeBlocks = function( graph, extra_uniforms ) { + // prepare context + this.clear(); + + // grab output nodes + var vertexout = graph.findNodesByType("shader::output/vertex"); + vertexout = vertexout && vertexout.length ? vertexout[0] : null; + var fragmentout = graph.findNodesByType("shader::output/fragcolor"); + fragmentout = fragmentout && fragmentout.length ? fragmentout[0] : null; + if(!fragmentout) // ?? + return null; + + // propagate back destinations + graph.sendEventToAllNodes( "clearDestination" ); + if(vertexout) + vertexout.propagateDestination("vs"); + if(fragmentout) + fragmentout.propagateDestination("fs"); + + // gets code from graph + graph.sendEventToAllNodes("onGetCode", this ); + + var uniforms = ""; + for(var i in this._uniforms) + uniforms += "uniform " + this._uniforms[i] + " " + i + ";\n"; + if(extra_uniforms) + for(var i in extra_uniforms) + uniforms += "uniform " + extra_uniforms[i] + " " + i + ";\n"; + + var functions = ""; + for(var i in this._functions) + functions += "//" + i + "\n" + this._functions[i] + "\n"; + + var blocks = this._codeparts; + blocks.uniforms = uniforms; + blocks.functions = functions; + return blocks; + } + + // replaces blocks using the vs and fs template and returns the final codes + LGShaderContext.prototype.computeShaderCode = function( graph ) { + var blocks = this.computeCodeBlocks( graph ); + var vs_code = GL.Shader.replaceCodeUsingContext( this.vs_template, blocks ); + var fs_code = GL.Shader.replaceCodeUsingContext( this.fs_template, blocks ); + return { + vs_code: vs_code, + fs_code: fs_code, + }; + } + + // generates the shader code from the template and the + LGShaderContext.prototype.computeShader = function( graph, shader ) { + var finalcode = this.computeShaderCode( graph ); + console.log( finalcode.vs_code, finalcode.fs_code ); + + if(!LiteGraph.catch_exceptions) { + this._shader_error = true; + if(shader) + shader.updateShader( finalcode.vs_code, finalcode.fs_code ); + else + shader = new GL.Shader( finalcode.vs_code, finalcode.fs_code ); + this._shader_error = false; + return shader; + } + + try { + if(shader) + shader.updateShader( finalcode.vs_code, finalcode.fs_code ); + else + shader = new GL.Shader( finalcode.vs_code, finalcode.fs_code ); + this._shader_error = false; + return shader; + } catch (err) { + if(!this._shader_error) { + console.error(err); + if(err.indexOf("Fragment shader") != -1) + console.log( finalcode.fs_code.split("\n").map(function(v,i) { + return i + ".- " + v; + }).join("\n") ); + else + console.log( finalcode.vs_code ); + } + this._shader_error = true; + return null; + } + } + + LGShaderContext.prototype.getShader = function( graph ) { + // if graph not changed? + if(this._shader && this._shader._version == graph._version) + return this._shader; + + // compile shader + var shader = this.computeShader( graph, this._shader ); + if(!shader) + return null; + + this._shader = shader; + shader._version = graph._version; + return shader; + } + + // some shader nodes could require to fill the box with some uniforms + LGShaderContext.prototype.fillUniforms = function( uniforms, param ) { + if(!this._uniform_value) + return; + + for(var i in this._uniform_value) { + var v = this._uniform_value[i]; + if(v == null) + continue; + if(v.constructor === Function) + uniforms[i] = v.call( this, param ); + else if(v.constructor === GL.Texture) { + // todo... + } else + uniforms[i] = v; + } + } + + LiteGraph.ShaderContext = LiteGraph.Shaders.Context = LGShaderContext; + + // LGraphShaderGraph ***************************** + // applies a shader graph to texture, it can be uses as an example + + function LGraphShaderGraph() { + + // before inputs + this.subgraph = new LiteGraph.LGraph(); + this.subgraph._subgraph_node = this; + this.subgraph._is_subgraph = true; + this.subgraph.filter = "shader"; + + this.addInput("in", "texture"); + this.addOutput("out", "texture"); + this.properties = { width: 0, height: 0, alpha: false, precision: typeof(LGraphTexture) != "undefined" ? LGraphTexture.DEFAULT : 2 }; + + var inputNode = this.subgraph.findNodesByType("shader::input/uniform")[0]; + inputNode.pos = [200,300]; + + var sampler = LiteGraph.createNode("shader::texture/sampler2D"); + sampler.pos = [400,300]; + this.subgraph.add( sampler ); + + var outnode = LiteGraph.createNode("shader::output/fragcolor"); + outnode.pos = [600,300]; + this.subgraph.add( outnode ); + + inputNode.connect( 0, sampler ); + sampler.connect( 0, outnode ); + + this.size = [180,60]; + this.redraw_on_mouse = true; // force redraw + + this._uniforms = {}; + this._shader = null; + this._context = new LGShaderContext(); + this._context.vs_template = "#define VERTEX\n" + GL.Shader.SCREEN_VERTEX_SHADER; + this._context.fs_template = LGraphShaderGraph.template; + } + + LGraphShaderGraph.template = "\n\ +#define FRAGMENT\n\ +precision highp float;\n\ +varying vec2 v_coord;\n\ +{{varying}}\n\ +{{uniforms}}\n\ +{{functions}}\n\ +{{fs_functions}}\n\ +void main() {\n\n\ +vec2 uv = v_coord;\n\ +vec4 fragcolor = vec4(0.0);\n\ +vec4 fragcolor1 = vec4(0.0);\n\ +{{fs_code}}\n\ +gl_FragColor = fragcolor;\n\ +}\n\ + "; + + LGraphShaderGraph.widgets_info = {precision: { widget: "combo", values: LGraphTexture.MODE_VALUES }}; + + LGraphShaderGraph.title = "ShaderGraph"; + LGraphShaderGraph.desc = "Builds a shader using a graph"; + LGraphShaderGraph.input_node_type = "input/uniform"; + LGraphShaderGraph.output_node_type = "output/fragcolor"; + LGraphShaderGraph.title_color = SHADERNODES_COLOR; + + LGraphShaderGraph.prototype.onSerialize = function(o) { + o.subgraph = this.subgraph.serialize(); + } + + LGraphShaderGraph.prototype.onConfigure = function(o) { + this.subgraph.configure(o.subgraph); + } + + LGraphShaderGraph.prototype.onExecute = function() { + if (!this.isOutputConnected(0)) + return; + + // read input texture + var intex = this.getInputData(0); + if(intex && intex.constructor != GL.Texture) + intex = null; + + var w = this.properties.width | 0; + var h = this.properties.height | 0; + if (w == 0) { + w = intex ? intex.width : gl.viewport_data[2]; + } // 0 means default + if (h == 0) { + h = intex ? intex.height : gl.viewport_data[3]; + } // 0 means default + + var type = LGraphTexture.getTextureType( this.properties.precision, intex ); + + var texture = this._texture; + if ( !texture || texture.width != w || texture.height != h || texture.type != type ) { + texture = this._texture = new GL.Texture(w, h, { + type: type, + format: this.alpha ? gl.RGBA : gl.RGB, + filter: gl.LINEAR, + }); + } + + var shader = this.getShader( this.subgraph ); + if(!shader) + return; + + var uniforms = this._uniforms; + this._context.fillUniforms( uniforms ); + + var tex_slot = 0; + if(this.inputs) + for(var i = 0; i < this.inputs.length; ++i) { + var input = this.inputs[i]; + var data = this.getInputData(i); + if(input.type == "texture") { + if(!data) + data = GL.Texture.getWhiteTexture(); + data = data.bind(tex_slot++); + } + + if(data != null) + uniforms["u_" + input.name] = data; + } + + var mesh = GL.Mesh.getScreenQuad(); + + gl.disable( gl.DEPTH_TEST ); + gl.disable( gl.BLEND ); + + texture.drawTo(function() { + shader.uniforms( uniforms ); + shader.draw( mesh ); + }); + + // use subgraph output + this.setOutputData(0, texture ); + }; + + // add input node inside subgraph + LGraphShaderGraph.prototype.onInputAdded = function( slot_info ) { + var subnode = LiteGraph.createNode("shader::input/uniform"); + subnode.setProperty("name",slot_info.name); + subnode.setProperty("type",slot_info.type); + this.subgraph.add( subnode ); } - logicOr.title = "OR"; - logicOr.desc = "Return true if at least one input is true"; - logicOr.prototype.onExecute = function() { - var ret = false; - for (var inX in this.inputs){ - if (this.getInputData(inX)){ - ret = true; - break; - } + + // remove all + LGraphShaderGraph.prototype.onInputRemoved = function( slot, slot_info ) { + var nodes = this.subgraph.findNodesByType("shader::input/uniform"); + for(var i = 0; i < nodes.length; ++i) { + var node = nodes[i]; + if(node.properties.name == slot_info.name ) + this.subgraph.remove( node ); } - this.setOutputData(0, ret); - }; - logicOr.prototype.onGetInputs = function() { - return [ - ["or", "boolean"] - ]; - }; - LiteGraph.registerNodeType("logic/OR", logicOr); - - - function logicNot(){ - this.properties = { }; - this.addInput("in", "boolean"); - this.addOutput("out", "boolean"); } - logicNot.title = "NOT"; - logicNot.desc = "Return the logical negation"; - logicNot.prototype.onExecute = function() { - var ret = !this.getInputData(0); - this.setOutputData(0, ret); - }; - LiteGraph.registerNodeType("logic/NOT", logicNot); - - - function logicCompare(){ - this.properties = { }; - this.addInput("a", "boolean"); - this.addInput("b", "boolean"); - this.addOutput("out", "boolean"); + + LGraphShaderGraph.prototype.computeSize = function() { + var num_inputs = this.inputs ? this.inputs.length : 0; + var num_outputs = this.outputs ? this.outputs.length : 0; + return [ 200, Math.max(num_inputs,num_outputs) * LiteGraph.NODE_SLOT_HEIGHT + LiteGraph.NODE_TITLE_HEIGHT + 10]; } - logicCompare.title = "bool == bool"; - logicCompare.desc = "Compare for logical equality"; - logicCompare.prototype.onExecute = function() { - var last = null; - var ret = true; - for (var inX in this.inputs){ - if (last === null) last = this.getInputData(inX); - else - if (last != this.getInputData(inX)){ - ret = false; - break; - } + + LGraphShaderGraph.prototype.getShader = function() { + var shader = this._context.getShader( this.subgraph ); + if(!shader) + this.boxcolor = "red"; + else + this.boxcolor = null; + return shader; + } + + LGraphShaderGraph.prototype.onDrawBackground = function(ctx, graphcanvas, canvas, pos) { + if(this.flags.collapsed) + return; + + // allows to preview the node if the canvas is a webgl canvas + var tex = this.getOutputData(0); + var inputs_y = this.inputs ? this.inputs.length * LiteGraph.NODE_SLOT_HEIGHT : 0; + if (tex && ctx == tex.gl && this.size[1] > inputs_y + LiteGraph.NODE_TITLE_HEIGHT ) { + ctx.drawImage( tex, 10,y, this.size[0] - 20, this.size[1] - inputs_y - LiteGraph.NODE_TITLE_HEIGHT ); } - this.setOutputData(0, ret); - }; - logicCompare.prototype.onGetInputs = function() { - return [ - ["bool", "boolean"] - ]; - }; - LiteGraph.registerNodeType("logic/CompareBool", logicCompare); - - - function logicBranch(){ - this.properties = { }; - this.addInput("onTrigger", LiteGraph.ACTION); - this.addInput("condition", "boolean"); - this.addOutput("true", LiteGraph.EVENT); - this.addOutput("false", LiteGraph.EVENT); - this.mode = LiteGraph.ON_TRIGGER; + + var y = this.size[1] - LiteGraph.NODE_TITLE_HEIGHT + 0.5; + + // button + var over = LiteGraph.isInsideRectangle(pos[0],pos[1],this.pos[0],this.pos[1] + y,this.size[0],LiteGraph.NODE_TITLE_HEIGHT); + ctx.fillStyle = over ? "#555" : "#222"; + ctx.beginPath(); + if (this._shape == LiteGraph.BOX_SHAPE) + ctx.rect(0, y, this.size[0]+1, LiteGraph.NODE_TITLE_HEIGHT); + else + ctx.roundRect( 0, y, this.size[0]+1, LiteGraph.NODE_TITLE_HEIGHT, 0, 8); + ctx.fill(); + + // button + ctx.textAlign = "center"; + ctx.font = "24px Arial"; + ctx.fillStyle = over ? "#DDD" : "#999"; + ctx.fillText( "+", this.size[0] * 0.5, y + 24 ); } - logicBranch.title = "Branch"; - logicBranch.desc = "Branch execution on condition"; - logicBranch.prototype.onExecute = function(param, options) { - var condtition = this.getInputData(1); - if (condtition){ - this.triggerSlot(0); - }else{ - this.triggerSlot(1); + + LGraphShaderGraph.prototype.onMouseDown = function(e, localpos, graphcanvas) { + var y = this.size[1] - LiteGraph.NODE_TITLE_HEIGHT + 0.5; + if(localpos[1] > y) { + graphcanvas.showSubgraphPropertiesDialog(this); } - }; - LiteGraph.registerNodeType("logic/IF", logicBranch); -})(this); + } -(function(global) { - var LiteGraph = global.LiteGraph; + LGraphShaderGraph.prototype.onDrawSubgraphBackground = function(graphcanvas) { + // TODO + } - function GraphicsPlot() { - this.addInput("A", "Number"); - this.addInput("B", "Number"); - this.addInput("C", "Number"); - this.addInput("D", "Number"); + LGraphShaderGraph.prototype.getExtraMenuOptions = function(graphcanvas) { + var that = this; + var options = [{ + content: "Print Code", callback: function() { + var code = that._context.computeShaderCode(); + console.log( code.vs_code, code.fs_code ); + }, + }]; - this.values = [[], [], [], []]; - this.properties = { scale: 2 }; + return options; } - GraphicsPlot.title = "Plot"; - GraphicsPlot.desc = "Plots data over time"; - GraphicsPlot.colors = ["#FFF", "#F99", "#9F9", "#99F"]; + LiteGraph.registerNodeType( "texture/shaderGraph", LGraphShaderGraph ); - GraphicsPlot.prototype.onExecute = function(ctx) { - if (this.flags.collapsed) { + function shaderNodeFromFunction( classname, params, return_type, code ) { + // TODO + } + + // Shader Nodes *********************************************************** + + // applies a shader graph to a code + function LGraphShaderUniform() { + this.addOutput("out", ""); + this.properties = { name: "", type: "" }; + } + + LGraphShaderUniform.title = "Uniform"; + LGraphShaderUniform.desc = "Input data for the shader"; + + LGraphShaderUniform.prototype.getTitle = function() { + if( this.properties.name && this.flags.collapsed) + return this.properties.type + " " + this.properties.name; + return "Uniform"; + } + + LGraphShaderUniform.prototype.onPropertyChanged = function(name,value) { + this.outputs[0].name = this.properties.type + " " + this.properties.name; + } + + LGraphShaderUniform.prototype.onGetCode = function( context ) { + if(!this.shader_destination) return; + + var type = this.properties.type; + if( !type ) { + if( !context.onGetPropertyInfo ) + return; + var info = context.onGetPropertyInfo( this.property.name ); + if(!info) + return; + type = info.type; } + if(type == "number") + type = "float"; + else if(type == "texture") + type = "sampler2D"; + if ( LGShaders.GLSL_types.indexOf(type) == -1 ) + return; - var size = this.size; + context.addUniform( "u_" + this.properties.name, type ); + this.setOutputData( 0, type ); + } - for (var i = 0; i < 4; ++i) { - var v = this.getInputData(i); - if (v == null) { - continue; + LGraphShaderUniform.prototype.getOutputVarName = function(slot) { + return "u_" + this.properties.name; + } + + registerShaderNode( "input/uniform", LGraphShaderUniform ); + + + function LGraphShaderAttribute() { + this.addOutput("out", "vec2"); + this.properties = { name: "coord", type: "vec2" }; + } + + LGraphShaderAttribute.title = "Attribute"; + LGraphShaderAttribute.desc = "Input data from mesh attribute"; + + LGraphShaderAttribute.prototype.getTitle = function() { + return "att. " + this.properties.name; + } + + LGraphShaderAttribute.prototype.onGetCode = function( context ) { + if(!this.shader_destination) + return; + + var type = this.properties.type; + if( !type || LGShaders.GLSL_types.indexOf(type) == -1 ) + return; + if(type == "number") + type = "float"; + if( this.properties.name != "coord") { + context.addCode( "varying", " varying " + type +" v_" + this.properties.name + ";" ); + // if( !context.varyings[ this.properties.name ] ) + // context.addCode( "vs_code", "v_" + this.properties.name + " = " + input_name + ";" ); + } + this.setOutputData( 0, type ); + } + + LGraphShaderAttribute.prototype.getOutputVarName = function(slot) { + return "v_" + this.properties.name; + } + + registerShaderNode( "input/attribute", LGraphShaderAttribute ); + + function LGraphShaderSampler2D() { + this.addInput("tex", "sampler2D"); + this.addInput("uv", "vec2"); + this.addOutput("rgba", "vec4"); + this.addOutput("rgb", "vec3"); + } + + LGraphShaderSampler2D.title = "Sampler2D"; + LGraphShaderSampler2D.desc = "Reads a pixel from a texture"; + + LGraphShaderSampler2D.prototype.onGetCode = function( context ) { + if(!this.shader_destination) + return; + + var texname = getInputLinkID( this, 0 ); + var varname = getShaderNodeVarName(this); + var code = "vec4 " + varname + " = vec4(0.0);\n"; + if(texname) { + var uvname = getInputLinkID( this, 1 ) || context.buffer_names.uvs; + code += varname + " = texture2D("+texname+","+uvname+");\n"; + } + + var link0 = getOutputLinkID( this, 0 ); + if(link0) + code += "vec4 " + getOutputLinkID( this, 0 ) + " = "+varname+";\n"; + + var link1 = getOutputLinkID( this, 1 ); + if(link1) + code += "vec3 " + getOutputLinkID( this, 1 ) + " = "+varname+".xyz;\n"; + + context.addCode( "code", code, this.shader_destination ); + this.setOutputData( 0, "vec4" ); + this.setOutputData( 1, "vec3" ); + } + + registerShaderNode( "texture/sampler2D", LGraphShaderSampler2D ); + + //* ******************************** + + function LGraphShaderConstant() { + this.addOutput("","float"); + + this.properties = { + type: "float", + value: 0, + }; + + this.addWidget("combo","type","float",null, { values: GLSL_types_const, property: "type" } ); + this.updateWidgets(); + } + + LGraphShaderConstant.title = "const"; + + LGraphShaderConstant.prototype.getTitle = function() { + if(this.flags.collapsed) + return valueToGLSL( this.properties.value, this.properties.type, 2 ); + return "Const"; + } + + LGraphShaderConstant.prototype.onPropertyChanged = function(name,value) { + var that = this; + if(name == "type") { + if(this.outputs[0].type != value) { + this.disconnectOutput(0); + this.outputs[0].type = value; } - var values = this.values[i]; - values.push(v); - if (values.length > size[0]) { - values.shift(); + this.widgets.length = 1; // remove extra widgets + this.updateWidgets(); + } + if(name == "value") { + if(!value.length) + this.widgets[1].value = value; + else { + this.widgets[1].value = value[1]; + if(value.length > 2) + this.widgets[2].value = value[2]; + if(value.length > 3) + this.widgets[3].value = value[3]; } } - }; + } - GraphicsPlot.prototype.onDrawBackground = function(ctx) { - if (this.flags.collapsed) { + LGraphShaderConstant.prototype.updateWidgets = function( old_value ) { + var that = this; + var old_value = this.properties.value; + var options = { step: 0.01 }; + switch(this.properties.type) { + case 'float': + this.properties.value = 0; + this.addWidget("number","v",0,{ step: 0.01, property: "value" }); + break; + case 'vec2': + this.properties.value = old_value && old_value.length == 2 ? [old_value[0],old_value[1]] : [0,0,0]; + this.addWidget("number","x",this.properties.value[0], function(v) { + that.properties.value[0] = v; + },options); + this.addWidget("number","y",this.properties.value[1], function(v) { + that.properties.value[1] = v; + },options); + break; + case 'vec3': + this.properties.value = old_value && old_value.length == 3 ? [old_value[0],old_value[1],old_value[2]] : [0,0,0]; + this.addWidget("number","x",this.properties.value[0], function(v) { + that.properties.value[0] = v; + },options); + this.addWidget("number","y",this.properties.value[1], function(v) { + that.properties.value[1] = v; + },options); + this.addWidget("number","z",this.properties.value[2], function(v) { + that.properties.value[2] = v; + },options); + break; + case 'vec4': + this.properties.value = old_value && old_value.length == 4 ? [old_value[0],old_value[1],old_value[2],old_value[3]] : [0,0,0,0]; + this.addWidget("number","x",this.properties.value[0], function(v) { + that.properties.value[0] = v; + },options); + this.addWidget("number","y",this.properties.value[1], function(v) { + that.properties.value[1] = v; + },options); + this.addWidget("number","z",this.properties.value[2], function(v) { + that.properties.value[2] = v; + },options); + this.addWidget("number","w",this.properties.value[3], function(v) { + that.properties.value[3] = v; + },options); + break; + default: + console.error("unknown type for constant"); + } + } + + LGraphShaderConstant.prototype.onGetCode = function( context ) { + if(!this.shader_destination) + return; + + var value = valueToGLSL( this.properties.value, this.properties.type ); + var link_name = getOutputLinkID(this,0); + if(!link_name) // not connected + return; + + var code = " " + this.properties.type + " " + link_name + " = " + value + ";"; + context.addCode( "code", code, this.shader_destination ); + + this.setOutputData( 0, this.properties.type ); + } + + registerShaderNode( "const/const", LGraphShaderConstant ); + + function LGraphShaderVec2() { + this.addInput("xy","vec2"); + this.addInput("x","float"); + this.addInput("y","float"); + this.addOutput("xy","vec2"); + this.addOutput("x","float"); + this.addOutput("y","float"); + + this.properties = { x: 0, y: 0 }; + } + + LGraphShaderVec2.title = "vec2"; + LGraphShaderVec2.varmodes = ["xy","x","y"]; + + LGraphShaderVec2.prototype.onPropertyChanged = function() { + if(this.graph) + this.graph._version++; + } + + LGraphShaderVec2.prototype.onGetCode = function( context ) { + if(!this.shader_destination) return; + + var props = this.properties; + + var varname = getShaderNodeVarName(this); + var code = " vec2 " + varname + " = " + valueToGLSL([props.x,props.y]) + ";\n"; + + for(var i = 0;i < LGraphShaderVec2.varmodes.length; ++i) { + var varmode = LGraphShaderVec2.varmodes[i]; + var inlink = getInputLinkID(this,i); + if(!inlink) + continue; + code += " " + varname + "."+varmode+" = " + inlink + ";\n"; } - var size = this.size; + for(var i = 0;i < LGraphShaderVec2.varmodes.length; ++i) { + var varmode = LGraphShaderVec2.varmodes[i]; + var outlink = getOutputLinkID(this,i); + if(!outlink) + continue; + var type = GLSL_types_const[varmode.length - 1]; + code += " "+type+" " + outlink + " = " + varname + "." + varmode + ";\n"; + this.setOutputData( i, type ); + } - var scale = (0.5 * size[1]) / this.properties.scale; - var colors = GraphicsPlot.colors; - var offset = size[1] * 0.5; + context.addCode( "code", code, this.shader_destination ); + } - ctx.fillStyle = "#000"; - ctx.fillRect(0, 0, size[0], size[1]); - ctx.strokeStyle = "#555"; - ctx.beginPath(); - ctx.moveTo(0, offset); - ctx.lineTo(size[0], offset); - ctx.stroke(); + registerShaderNode( "const/vec2", LGraphShaderVec2 ); + + function LGraphShaderVec3() { + this.addInput("xyz","vec3"); + this.addInput("x","float"); + this.addInput("y","float"); + this.addInput("z","float"); + this.addInput("xy","vec2"); + this.addInput("xz","vec2"); + this.addInput("yz","vec2"); + this.addOutput("xyz","vec3"); + this.addOutput("x","float"); + this.addOutput("y","float"); + this.addOutput("z","float"); + this.addOutput("xy","vec2"); + this.addOutput("xz","vec2"); + this.addOutput("yz","vec2"); - if (this.inputs) { - for (var i = 0; i < 4; ++i) { - var values = this.values[i]; - if (!this.inputs[i] || !this.inputs[i].link) { - continue; - } - ctx.strokeStyle = colors[i]; - ctx.beginPath(); - var v = values[0] * scale * -1 + offset; - ctx.moveTo(0, clamp(v, 0, size[1])); - for (var j = 1; j < values.length && j < size[0]; ++j) { - var v = values[j] * scale * -1 + offset; - ctx.lineTo(j, clamp(v, 0, size[1])); - } - ctx.stroke(); - } + this.properties = { x: 0, y: 0, z: 0 }; + } + + LGraphShaderVec3.title = "vec3"; + LGraphShaderVec3.varmodes = ["xyz","x","y","z","xy","xz","yz"]; + + LGraphShaderVec3.prototype.onPropertyChanged = function() { + if(this.graph) + this.graph._version++; + } + + LGraphShaderVec3.prototype.onGetCode = function( context ) { + if(!this.shader_destination) + return; + + var props = this.properties; + + var varname = getShaderNodeVarName(this); + var code = "vec3 " + varname + " = " + valueToGLSL([props.x,props.y,props.z]) + ";\n"; + + for(var i = 0;i < LGraphShaderVec3.varmodes.length; ++i) { + var varmode = LGraphShaderVec3.varmodes[i]; + var inlink = getInputLinkID(this,i); + if(!inlink) + continue; + code += " " + varname + "."+varmode+" = " + inlink + ";\n"; } - }; - LiteGraph.registerNodeType("graphics/plot", GraphicsPlot); + for(var i = 0; i < LGraphShaderVec3.varmodes.length; ++i) { + var varmode = LGraphShaderVec3.varmodes[i]; + var outlink = getOutputLinkID(this,i); + if(!outlink) + continue; + var type = GLSL_types_const[varmode.length - 1]; + code += " "+type+" " + outlink + " = " + varname + "." + varmode + ";\n"; + this.setOutputData( i, type ); + } + + context.addCode( "code", code, this.shader_destination ); + } + + registerShaderNode( "const/vec3", LGraphShaderVec3 ); + + + function LGraphShaderVec4() { + this.addInput("xyzw","vec4"); + this.addInput("xyz","vec3"); + this.addInput("x","float"); + this.addInput("y","float"); + this.addInput("z","float"); + this.addInput("w","float"); + this.addInput("xy","vec2"); + this.addInput("yz","vec2"); + this.addInput("zw","vec2"); + this.addOutput("xyzw","vec4"); + this.addOutput("xyz","vec3"); + this.addOutput("x","float"); + this.addOutput("y","float"); + this.addOutput("z","float"); + this.addOutput("xy","vec2"); + this.addOutput("yz","vec2"); + this.addOutput("zw","vec2"); + + this.properties = { x: 0, y: 0, z: 0, w: 0 }; + } + + LGraphShaderVec4.title = "vec4"; + LGraphShaderVec4.varmodes = ["xyzw","xyz","x","y","z","w","xy","yz","zw"]; - function GraphicsImage() { - this.addOutput("frame", "image"); - this.properties = { url: "" }; + LGraphShaderVec4.prototype.onPropertyChanged = function() { + if(this.graph) + this.graph._version++; } - GraphicsImage.title = "Image"; - GraphicsImage.desc = "Image loader"; - GraphicsImage.widgets = [{ name: "load", text: "Load", type: "button" }]; + LGraphShaderVec4.prototype.onGetCode = function( context ) { + if(!this.shader_destination) + return; - GraphicsImage.supported_extensions = ["jpg", "jpeg", "png", "gif"]; + var props = this.properties; - GraphicsImage.prototype.onAdded = function() { - if (this.properties["url"] != "" && this.img == null) { - this.loadImage(this.properties["url"]); - } - }; + var varname = getShaderNodeVarName(this); + var code = "vec4 " + varname + " = " + valueToGLSL([props.x,props.y,props.z,props.w]) + ";\n"; - GraphicsImage.prototype.onDrawBackground = function(ctx) { - if (this.flags.collapsed) { - return; - } - if (this.img && this.size[0] > 5 && this.size[1] > 5 && this.img.width) { - ctx.drawImage(this.img, 0, 0, this.size[0], this.size[1]); + for(var i = 0;i < LGraphShaderVec4.varmodes.length; ++i) { + var varmode = LGraphShaderVec4.varmodes[i]; + var inlink = getInputLinkID(this,i); + if(!inlink) + continue; + code += " " + varname + "."+varmode+" = " + inlink + ";\n"; } - }; - 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 && this.img.dirty) { - this.img.dirty = false; + for(var i = 0;i < LGraphShaderVec4.varmodes.length; ++i) { + var varmode = LGraphShaderVec4.varmodes[i]; + var outlink = getOutputLinkID(this,i); + if(!outlink) + continue; + var type = GLSL_types_const[varmode.length - 1]; + code += " "+type+" " + outlink + " = " + varname + "." + varmode + ";\n"; + this.setOutputData( i, type ); } - }; - GraphicsImage.prototype.onPropertyChanged = function(name, value) { - this.properties[name] = value; - if (name == "url" && value != "") { - this.loadImage(value); - } + context.addCode( "code", code, this.shader_destination ); - return true; - }; + } - GraphicsImage.prototype.loadImage = function(url, callback) { - if (url == "") { - this.img = null; + registerShaderNode( "const/vec4", LGraphShaderVec4 ); + + //* ******************************** + + function LGraphShaderFragColor() { + this.addInput("color", LGShaders.ALL_TYPES ); + this.block_delete = true; + } + + LGraphShaderFragColor.title = "FragColor"; + LGraphShaderFragColor.desc = "Pixel final color"; + + LGraphShaderFragColor.prototype.onGetCode = function( context ) { + var link_name = getInputLinkID( this, 0 ); + if(!link_name) return; - } + var type = this.getInputData(0); + var code = varToTypeGLSL( link_name, type, "vec4" ); + context.addCode("fs_code", "fragcolor = " + code + ";"); + } - this.img = document.createElement("img"); + registerShaderNode( "output/fragcolor", LGraphShaderFragColor ); - if (url.substr(0, 4) == "http" && LiteGraph.proxy) { - url = LiteGraph.proxy + url.substr(url.indexOf(":") + 3); - } - this.img.src = url; - this.boxcolor = "#F95"; - var that = this; - this.img.onload = function() { - if (callback) { - callback(this); - } - console.log( "Image loaded, size: " + that.img.width + "x" + that.img.height ); - this.dirty = true; - that.boxcolor = "#9F9"; - that.setDirtyCanvas(true); - }; - this.img.onerror = function() { - console.log("error loading the image:" + url); - } - }; + /* + function LGraphShaderDiscard() + { + this.addInput("v","T"); + this.addInput("min","T"); + this.properties = { min_value: 0.0 }; + this.addWidget("number","min",0,{ step: 0.01, property: "min_value" }); + } - GraphicsImage.prototype.onWidget = function(e, widget) { - if (widget.name == "load") { - this.loadImage(this.properties["url"]); - } - }; + LGraphShaderDiscard.title = "Discard"; - GraphicsImage.prototype.onDropFile = function(file) { - var that = this; - if (this._url) { - URL.revokeObjectURL(this._url); - } - this._url = URL.createObjectURL(file); - this.properties.url = this._url; - this.loadImage(this._url, function(img) { - that.size[1] = (img.height / img.width) * that.size[0]; - }); - }; + LGraphShaderDiscard.prototype.onGetCode = function( context ) + { + if(!this.isOutputConnected(0)) + return; - LiteGraph.registerNodeType("graphics/image", GraphicsImage); + var inlink = getInputLinkID(this,0); + var inlink1 = getInputLinkID(this,1); - function ColorPalette() { - this.addInput("f", "number"); - this.addOutput("Color", "color"); - this.properties = { - colorA: "#444444", - colorB: "#44AAFF", - colorC: "#44FFAA", - colorD: "#FFFFFF" - }; + if(!inlink && !inlink1) //not connected + return; + context.addCode("code", return_type + " " + outlink + " = ( (" + inlink + " - "+minv+") / ("+ maxv+" - "+minv+") ) * ("+ maxv2+" - "+minv2+") + " + minv2 + ";", this.shader_destination ); + this.setOutputData( 0, return_type ); + } + + registerShaderNode( "output/discard", LGraphShaderDiscard ); + */ + + + // ************************************************* + + function LGraphShaderOperation() { + this.addInput("A", LGShaders.ALL_TYPES ); + this.addInput("B", LGShaders.ALL_TYPES ); + this.addOutput("out",""); + this.properties = {operation: "*"}; + this.addWidget("combo","op.",this.properties.operation,{ property: "operation", values: LGraphShaderOperation.operations }); } - ColorPalette.title = "Palette"; - ColorPalette.desc = "Generates a color"; + LGraphShaderOperation.title = "Operation"; + LGraphShaderOperation.operations = ["+","-","*","/"]; - ColorPalette.prototype.onExecute = function() { - var c = []; + LGraphShaderOperation.prototype.getTitle = function() { + if(this.flags.collapsed) + return "A" + this.properties.operation + "B"; + else + return "Operation"; + } - 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)); - } + LGraphShaderOperation.prototype.onGetCode = function( context ) { + if(!this.shader_destination) + return; - 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(!this.isOutputConnected(0)) + return; - if (c.length == 0) { + var inlinks = []; + for(var i = 0; i < 3; ++i) + inlinks.push( { name: getInputLinkID(this,i), type: this.getInputData(i) || "float" } ); + + var outlink = getOutputLinkID(this,0); + if(!outlink) // not connected 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; - } + // func_desc + var base_type = inlinks[0].type; + var return_type = base_type; + var op = this.properties.operation; - /* - 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) ); - */ + var params = []; + for(var i = 0; i < 2; ++i) { + var param_code = inlinks[i].name; + if(param_code == null) { // not plugged + param_code = p.value != null ? p.value : "(1.0)"; + inlinks[i].type = "float"; + } - for (var i=0; i < result.length; i++) { - result[i] /= 255; + // convert + if( inlinks[i].type != base_type ) { + if( inlinks[i].type == "float" && (op == "*" || op == "/") ) { + // I find hard to create the opposite condition now, so I prefeer an else + } else + param_code = convertVarToGLSLType( param_code, inlinks[i].type, base_type ); + } + params.push( param_code ); } - this.boxcolor = colorToString(result); - this.setOutputData(0, result); - }; + context.addCode("code", return_type + " " + outlink + " = "+ params[0] + op + params[1] + ";", this.shader_destination ); + this.setOutputData( 0, return_type ); + } - LiteGraph.registerNodeType("color/palette", ColorPalette); + registerShaderNode( "math/operation", LGraphShaderOperation ); - function ImageFrame() { - this.addInput("", "image,canvas"); - 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" } - ]; + function LGraphShaderFunc() { + this.addInput("A", LGShaders.ALL_TYPES ); + this.addInput("B", LGShaders.ALL_TYPES ); + this.addOutput("out",""); + this.properties = {func: "floor"}; + this._current = "floor"; + this.addWidget("combo","func",this.properties.func,{ property: "func", values: GLSL_functions_name }); + } - ImageFrame.prototype.onDrawBackground = function(ctx) { - if (this.frame && !this.flags.collapsed) { - ctx.drawImage(this.frame, 0, 0, this.size[0], this.size[1]); - } - }; + LGraphShaderFunc.title = "Func"; - ImageFrame.prototype.onExecute = function() { - this.frame = this.getInputData(0); - this.setDirtyCanvas(true); - }; + LGraphShaderFunc.prototype.onPropertyChanged = function(name,value) { + if(this.graph) + this.graph._version++; - ImageFrame.prototype.onWidget = function(e, widget) { - if (widget.name == "resize" && this.frame) { - var width = this.frame.width; - var height = this.frame.height; + if(name == "func") { + var func_desc = GLSL_functions[value]; + if(!func_desc) + return; - if (!width && this.frame.videoWidth != null) { - width = this.frame.videoWidth; - height = this.frame.videoHeight; - } + // remove extra inputs + for(var i = func_desc.params.length; i < this.inputs.length; ++i) + this.removeInput(i); - if (width && height) { - this.size = [width, height]; + // add and update inputs + for(var i = 0; i < func_desc.params.length; ++i) { + var p = func_desc.params[i]; + if( this.inputs[i] ) + this.inputs[i].name = p.name + (p.value ? " (" + p.value + ")" : ""); + else + this.addInput( p.name, LGShaders.ALL_TYPES ); } - 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); + LGraphShaderFunc.prototype.getTitle = function() { + if(this.flags.collapsed) + return this.properties.func; + else + return "Func"; + } + + LGraphShaderFunc.prototype.onGetCode = function( context ) { + if(!this.shader_destination) + return; + + if(!this.isOutputConnected(0)) + return; + + var inlinks = []; + for(var i = 0; i < 3; ++i) + inlinks.push( { name: getInputLinkID(this,i), type: this.getInputData(i) || "float" } ); + + var outlink = getOutputLinkID(this,0); + if(!outlink) // not connected + return; + + var func_desc = GLSL_functions[this.properties.func]; + if(!func_desc) + return; + + // func_desc + var base_type = inlinks[0].type; + var return_type = func_desc.return_type; + if( return_type == "T" ) + return_type = base_type; + + var params = []; + for(var i = 0; i < func_desc.params.length; ++i) { + var p = func_desc.params[i]; + var param_code = inlinks[i].name; + if(param_code == null) { // not plugged + param_code = p.value != null ? p.value : "(1.0)"; + inlinks[i].type = "float"; + } + if( (p.type == "T" && inlinks[i].type != base_type) || + (p.type != "T" && inlinks[i].type != base_type) ) + param_code = convertVarToGLSLType( param_code, inlinks[i].type, base_type ); + params.push( param_code ); } - }; - LiteGraph.registerNodeType("graphics/frame", ImageFrame); + context.addFunction("round","float round(float v){ return floor(v+0.5); }\nvec2 round(vec2 v){ return floor(v+vec2(0.5));}\nvec3 round(vec3 v){ return floor(v+vec3(0.5));}\nvec4 round(vec4 v){ return floor(v+vec4(0.5)); }\n"); + context.addCode("code", return_type + " " + outlink + " = "+func_desc.func+"("+params.join(",")+");", this.shader_destination ); - function ImageFade() { - this.addInputs([ - ["img1", "image"], - ["img2", "image"], - ["fade", "number"] - ]); - this.addOutput("", "image"); - this.properties = { fade: 0.5, width: 512, height: 512 }; + this.setOutputData( 0, return_type ); } - 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" } - ]; + registerShaderNode( "math/func", LGraphShaderFunc ); - 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; + function LGraphShaderSnippet() { + this.addInput("A", LGShaders.ALL_TYPES ); + this.addInput("B", LGShaders.ALL_TYPES ); + this.addOutput("C","vec4"); + this.properties = { + code: "C = A+B", + type: "vec4", + } + this.addWidget("text","code",this.properties.code,{ property: "code" }); + this.addWidget("combo","type",this.properties.type,{ values: ["float","vec2","vec3","vec4"], property: "type" }); + } + + LGraphShaderSnippet.title = "Snippet"; + + LGraphShaderSnippet.prototype.onPropertyChanged = function(name,value) { + if(this.graph) + this.graph._version++; - var A = this.getInputData(0); - if (A != null) { - ctx.drawImage(A, 0, 0, this.canvas.width, this.canvas.height); + if(name == "type"&& this.outputs[0].type != value) { + this.disconnectOutput(0); + this.outputs[0].type = value; } + } - var fade = this.getInputData(2); - if (fade == null) { - fade = this.properties["fade"]; - } else { - this.properties["fade"] = fade; - } + LGraphShaderSnippet.prototype.getTitle = function() { + if(this.flags.collapsed) + return this.properties.code; + else + return "Snippet"; + } - ctx.globalAlpha = fade; - var B = this.getInputData(1); - if (B != null) { - ctx.drawImage(B, 0, 0, this.canvas.width, this.canvas.height); + LGraphShaderSnippet.prototype.onGetCode = function( context ) { + if(!this.shader_destination || !this.isOutputConnected(0)) + return; + + var inlinkA = getInputLinkID(this,0); + if(!inlinkA) + inlinkA = "1.0"; + var inlinkB = getInputLinkID(this,1); + if(!inlinkB) + inlinkB = "1.0"; + var outlink = getOutputLinkID(this,0); + if(!outlink) // not connected + return; + + var inA_type = this.getInputData(0) || "float"; + var inB_type = this.getInputData(1) || "float"; + var return_type = this.properties.type; + + // cannot resolve input + if(inA_type == "T" || inB_type == "T") { + return null; } - ctx.globalAlpha = 1.0; - this.setOutputData(0, this.canvas); - this.setDirtyCanvas(true); - }; + var funcname = "funcSnippet" + this.id; - LiteGraph.registerNodeType("graphics/imagefade", ImageFade); + var func_code = "\n" + return_type + " " + funcname + "( " + inA_type + " A, " + inB_type + " B) {\n"; + func_code += " " + return_type + " C = " + return_type + "(0.0);\n"; + func_code += " " + this.properties.code + ";\n"; + func_code += " return C;\n}\n"; - function ImageCrop() { - this.addInput("", "image"); - this.addOutput("", "image"); - this.properties = { width: 256, height: 256, x: 0, y: 0, scale: 1.0 }; - this.size = [50, 20]; + context.addCode("functions", func_code, this.shader_destination ); + context.addCode("code", return_type + " " + outlink + " = "+funcname+"("+inlinkA+","+inlinkB+");", this.shader_destination ); + + this.setOutputData( 0, return_type ); } - ImageCrop.title = "Crop"; - ImageCrop.desc = "Crop Image"; + registerShaderNode( "utils/snippet", LGraphShaderSnippet ); - 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"]; - }; + function LGraphShaderRand() { + this.addOutput("out","float"); + } - ImageCrop.prototype.onExecute = function() { - var input = this.getInputData(0); - if (!input) { + LGraphShaderRand.title = "Rand"; + + LGraphShaderRand.prototype.onGetCode = function( context ) { + if(!this.shader_destination || !this.isOutputConnected(0)) return; - } - if (input.width) { - var ctx = this.canvas.getContext("2d"); + var outlink = getOutputLinkID(this,0); - 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); - } - }; + context.addUniform( "u_rand" + this.id, "float", function() { + return Math.random(); + }); + context.addCode("code", "float " + outlink + " = u_rand" + this.id +";", this.shader_destination ); + this.setOutputData( 0, "float" ); + } - ImageCrop.prototype.onDrawBackground = function(ctx) { - if (this.flags.collapsed) { + registerShaderNode( "input/rand", LGraphShaderRand ); + + // noise + // https://gist.github.com/patriciogonzalezvivo/670c22f3966e662d2f83 + function LGraphShaderNoise() { + this.addInput("out", LGShaders.ALL_TYPES ); + this.addInput("scale", "float" ); + this.addOutput("out","float"); + this.properties = { + type: "noise", + scale: 1, + }; + this.addWidget("combo","type", this.properties.type, { property: "type", values: LGraphShaderNoise.NOISE_TYPES }); + this.addWidget("number","scale", this.properties.scale, { property: "scale" }); + } + + LGraphShaderNoise.NOISE_TYPES = ["noise","rand"]; + + LGraphShaderNoise.title = "noise"; + + LGraphShaderNoise.prototype.onGetCode = function( context ) { + if(!this.shader_destination || !this.isOutputConnected(0)) return; - } - if (this.canvas) { - ctx.drawImage( - this.canvas, - 0, - 0, - this.canvas.width, - this.canvas.height, - 0, - 0, - this.size[0], - this.size[1] - ); - } - }; - ImageCrop.prototype.onPropertyChanged = function(name, value) { - this.properties[name] = value; + var inlink = getInputLinkID(this,0); + var outlink = getOutputLinkID(this,0); - if (name == "scale") { - this.properties[name] = parseFloat(value); - if (this.properties[name] == 0) { - console.error("Error in scale"); - this.properties[name] = 1.0; - } - } else { - this.properties[name] = parseInt(value); + var intype = this.getInputData(0); + if(!inlink) { + intype = "vec2"; + inlink = context.buffer_names.uvs; } - this.createCanvas(); + context.addFunction("noise",LGraphShaderNoise.shader_functions); + context.addUniform( "u_noise_scale" + this.id, "float", this.properties.scale ); + if( intype == "float" ) + context.addCode("code", "float " + outlink + " = snoise( vec2(" + inlink +") * u_noise_scale" + this.id +");", this.shader_destination ); + else if( intype == "vec2" || intype == "vec3" ) + context.addCode("code", "float " + outlink + " = snoise(" + inlink +" * u_noise_scale" + this.id +");", this.shader_destination ); + else if( intype == "vec4" ) + context.addCode("code", "float " + outlink + " = snoise(" + inlink +".xyz * u_noise_scale" + this.id +");", this.shader_destination ); + this.setOutputData( 0, "float" ); + } - return true; - }; + registerShaderNode( "math/noise", LGraphShaderNoise ); - LiteGraph.registerNodeType("graphics/cropImage", ImageCrop); + LGraphShaderNoise.shader_functions = "\n\ +vec3 permute(vec3 x) { return mod(((x*34.0)+1.0)*x, 289.0); }\n\ +\n\ +float snoise(vec2 v){\n\ + const vec4 C = vec4(0.211324865405187, 0.366025403784439,-0.577350269189626, 0.024390243902439);\n\ + vec2 i = floor(v + dot(v, C.yy) );\n\ + vec2 x0 = v - i + dot(i, C.xx);\n\ + vec2 i1;\n\ + i1 = (x0.x > x0.y) ? vec2(1.0, 0.0) : vec2(0.0, 1.0);\n\ + vec4 x12 = x0.xyxy + C.xxzz;\n\ + x12.xy -= i1;\n\ + i = mod(i, 289.0);\n\ + vec3 p = permute( permute( i.y + vec3(0.0, i1.y, 1.0 ))\n\ + + i.x + vec3(0.0, i1.x, 1.0 ));\n\ + vec3 m = max(0.5 - vec3(dot(x0,x0), dot(x12.xy,x12.xy),dot(x12.zw,x12.zw)), 0.0);\n\ + m = m*m ;\n\ + m = m*m ;\n\ + vec3 x = 2.0 * fract(p * C.www) - 1.0;\n\ + vec3 h = abs(x) - 0.5;\n\ + vec3 ox = floor(x + 0.5);\n\ + vec3 a0 = x - ox;\n\ + m *= 1.79284291400159 - 0.85373472095314 * ( a0*a0 + h*h );\n\ + vec3 g;\n\ + g.x = a0.x * x0.x + h.x * x0.y;\n\ + g.yz = a0.yz * x12.xz + h.yz * x12.yw;\n\ + return 130.0 * dot(m, g);\n\ +}\n\ +vec4 permute(vec4 x){return mod(((x*34.0)+1.0)*x, 289.0);}\n\ +vec4 taylorInvSqrt(vec4 r){return 1.79284291400159 - 0.85373472095314 * r;}\n\ +\n\ +float snoise(vec3 v){ \n\ + const vec2 C = vec2(1.0/6.0, 1.0/3.0) ;\n\ + const vec4 D = vec4(0.0, 0.5, 1.0, 2.0);\n\ +\n\ +// First corner\n\ + vec3 i = floor(v + dot(v, C.yyy) );\n\ + vec3 x0 = v - i + dot(i, C.xxx) ;\n\ +\n\ +// Other corners\n\ + vec3 g = step(x0.yzx, x0.xyz);\n\ + vec3 l = 1.0 - g;\n\ + vec3 i1 = min( g.xyz, l.zxy );\n\ + vec3 i2 = max( g.xyz, l.zxy );\n\ +\n\ + // x0 = x0 - 0. + 0.0 * C \n\ + vec3 x1 = x0 - i1 + 1.0 * C.xxx;\n\ + vec3 x2 = x0 - i2 + 2.0 * C.xxx;\n\ + vec3 x3 = x0 - 1. + 3.0 * C.xxx;\n\ +\n\ +// Permutations\n\ + i = mod(i, 289.0 ); \n\ + vec4 p = permute( permute( permute( \n\ + i.z + vec4(0.0, i1.z, i2.z, 1.0 ))\n\ + + i.y + vec4(0.0, i1.y, i2.y, 1.0 )) \n\ + + i.x + vec4(0.0, i1.x, i2.x, 1.0 ));\n\ +\n\ +// Gradients\n\ +// ( N*N points uniformly over a square, mapped onto an octahedron.)\n\ + float n_ = 1.0/7.0; // N=7\n\ + vec3 ns = n_ * D.wyz - D.xzx;\n\ +\n\ + vec4 j = p - 49.0 * floor(p * ns.z *ns.z); // mod(p,N*N)\n\ +\n\ + vec4 x_ = floor(j * ns.z);\n\ + vec4 y_ = floor(j - 7.0 * x_ ); // mod(j,N)\n\ +\n\ + vec4 x = x_ *ns.x + ns.yyyy;\n\ + vec4 y = y_ *ns.x + ns.yyyy;\n\ + vec4 h = 1.0 - abs(x) - abs(y);\n\ +\n\ + vec4 b0 = vec4( x.xy, y.xy );\n\ + vec4 b1 = vec4( x.zw, y.zw );\n\ +\n\ + vec4 s0 = floor(b0)*2.0 + 1.0;\n\ + vec4 s1 = floor(b1)*2.0 + 1.0;\n\ + vec4 sh = -step(h, vec4(0.0));\n\ +\n\ + vec4 a0 = b0.xzyw + s0.xzyw*sh.xxyy ;\n\ + vec4 a1 = b1.xzyw + s1.xzyw*sh.zzww ;\n\ +\n\ + vec3 p0 = vec3(a0.xy,h.x);\n\ + vec3 p1 = vec3(a0.zw,h.y);\n\ + vec3 p2 = vec3(a1.xy,h.z);\n\ + vec3 p3 = vec3(a1.zw,h.w);\n\ +\n\ +//Normalise gradients\n\ + vec4 norm = taylorInvSqrt(vec4(dot(p0,p0), dot(p1,p1), dot(p2, p2), dot(p3,p3)));\n\ + p0 *= norm.x;\n\ + p1 *= norm.y;\n\ + p2 *= norm.z;\n\ + p3 *= norm.w;\n\ +\n\ +// Mix final noise value\n\ + vec4 m = max(0.6 - vec4(dot(x0,x0), dot(x1,x1), dot(x2,x2), dot(x3,x3)), 0.0);\n\ + m = m * m;\n\ + return 42.0 * dot( m*m, vec4( dot(p0,x0), dot(p1,x1),dot(p2,x2), dot(p3,x3) ) );\n\ +}\n\ +\n\ +vec3 hash3( vec2 p ){\n\ + vec3 q = vec3( dot(p,vec2(127.1,311.7)), \n\ + dot(p,vec2(269.5,183.3)), \n\ + dot(p,vec2(419.2,371.9)) );\n\ + return fract(sin(q)*43758.5453);\n\ +}\n\ +vec4 hash4( vec3 p ){\n\ + vec4 q = vec4( dot(p,vec3(127.1,311.7,257.3)), \n\ + dot(p,vec3(269.5,183.3,335.1)), \n\ + dot(p,vec3(314.5,235.1,467.3)), \n\ + dot(p,vec3(419.2,371.9,114.9)) );\n\ + return fract(sin(q)*43758.5453);\n\ +}\n\ +\n\ +float iqnoise( in vec2 x, float u, float v ){\n\ + vec2 p = floor(x);\n\ + vec2 f = fract(x);\n\ + \n\ + float k = 1.0+63.0*pow(1.0-v,4.0);\n\ + \n\ + float va = 0.0;\n\ + float wt = 0.0;\n\ + for( int j=-2; j<=2; j++ )\n\ + for( int i=-2; i<=2; i++ )\n\ + {\n\ + vec2 g = vec2( float(i),float(j) );\n\ + vec3 o = hash3( p + g )*vec3(u,u,1.0);\n\ + vec2 r = g - f + o.xy;\n\ + float d = dot(r,r);\n\ + float ww = pow( 1.0-smoothstep(0.0,1.414,sqrt(d)), k );\n\ + va += o.z*ww;\n\ + wt += ww;\n\ + }\n\ + \n\ + return va/wt;\n\ +}\n\ +" - //CANVAS stuff + function LGraphShaderTime() { + this.addOutput("out","float"); + } - function CanvasNode() { - this.addInput("clear", LiteGraph.ACTION); - this.addOutput("", "canvas"); - this.properties = { width: 512, height: 512, autoclear: true }; + LGraphShaderTime.title = "Time"; - this.canvas = document.createElement("canvas"); - this.ctx = this.canvas.getContext("2d"); + LGraphShaderTime.prototype.onGetCode = function( context ) { + if(!this.shader_destination || !this.isOutputConnected(0)) + return; + + var outlink = getOutputLinkID(this,0); + + context.addUniform( "u_time" + this.id, "float", function() { + return getTime() * 0.001; + }); + context.addCode("code", "float " + outlink + " = u_time" + this.id +";", this.shader_destination ); + this.setOutputData( 0, "float" ); } - CanvasNode.title = "Canvas"; - CanvasNode.desc = "Canvas to render stuff"; + registerShaderNode( "input/time", LGraphShaderTime ); - CanvasNode.prototype.onExecute = function() { - var canvas = this.canvas; - var w = this.properties.width | 0; - var h = this.properties.height | 0; - if (canvas.width != w) { - canvas.width = w; - } - if (canvas.height != h) { - canvas.height = h; - } - if (this.properties.autoclear) { - this.ctx.clearRect(0, 0, canvas.width, canvas.height); - } - this.setOutputData(0, canvas); - }; + function LGraphShaderDither() { + this.addInput("in","T"); + this.addOutput("out","float"); + } - CanvasNode.prototype.onAction = function(action, param) { - if (action == "clear") { - this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height); - } - }; + LGraphShaderDither.title = "Dither"; - LiteGraph.registerNodeType("graphics/canvas", CanvasNode); + LGraphShaderDither.prototype.onGetCode = function( context ) { + if(!this.shader_destination || !this.isOutputConnected(0)) + return; - function DrawImageNode() { - this.addInput("canvas", "canvas"); - this.addInput("img", "image,canvas"); - this.addInput("x", "number"); - this.addInput("y", "number"); - this.properties = { x: 0, y: 0, opacity: 1 }; + var inlink = getInputLinkID(this,0); + var return_type = "float"; + var outlink = getOutputLinkID(this,0); + var intype = this.getInputData(0); + inlink = varToTypeGLSL( inlink, intype, "float" ); + context.addFunction("dither8x8", LGraphShaderDither.dither_func); + context.addCode("code", return_type + " " + outlink + " = dither8x8("+ inlink +");", this.shader_destination ); + this.setOutputData( 0, return_type ); } - DrawImageNode.title = "DrawImage"; - DrawImageNode.desc = "Draws image into a canvas"; + LGraphShaderDither.dither_values = [0.515625,0.140625,0.640625,0.046875,0.546875,0.171875,0.671875,0.765625,0.265625,0.890625,0.390625,0.796875,0.296875,0.921875,0.421875,0.203125,0.703125,0.078125,0.578125,0.234375,0.734375,0.109375,0.609375,0.953125,0.453125,0.828125,0.328125,0.984375,0.484375,0.859375,0.359375,0.0625,0.5625,0.1875,0.6875,0.03125,0.53125,0.15625,0.65625,0.8125,0.3125,0.9375,0.4375,0.78125,0.28125,0.90625,0.40625,0.25,0.75,0.125,0.625,0.21875,0.71875,0.09375,0.59375,1.0001,0.5,0.875,0.375,0.96875,0.46875,0.84375,0.34375]; + + LGraphShaderDither.dither_func = "\n\ + float dither8x8(float brightness) {\n\ + vec2 position = vec2(0.0);\n\ + #ifdef FRAGMENT\n\ + position = gl_FragCoord.xy;\n\ + #endif\n\ + int x = int(mod(position.x, 8.0));\n\ + int y = int(mod(position.y, 8.0));\n\ + int index = x + y * 8;\n\ + float limit = 0.0;\n\ + if (x < 8) {\n\ + if(index==0) limit = 0.015625;\n\ + "+(LGraphShaderDither.dither_values.map( function(v,i) { + return "else if(index== "+(i+1)+") limit = " + v + ";" + }).join("\n"))+"\n\ + }\n\ + return brightness < limit ? 0.0 : 1.0;\n\ + }\n", + + registerShaderNode( "math/dither", LGraphShaderDither ); + + function LGraphShaderRemap() { + this.addInput("", LGShaders.ALL_TYPES ); + this.addOutput("",""); + this.properties = { + min_value: 0, + max_value: 1, + min_value2: 0, + max_value2: 1, + }; + this.addWidget("number","min",0,{ step: 0.1, property: "min_value" }); + this.addWidget("number","max",1,{ step: 0.1, property: "max_value" }); + this.addWidget("number","min2",0,{ step: 0.1, property: "min_value2"}); + this.addWidget("number","max2",1,{ step: 0.1, property: "max_value2"}); + } - DrawImageNode.prototype.onExecute = function() { - var canvas = this.getInputData(0); - if (!canvas) { + LGraphShaderRemap.title = "Remap"; + + LGraphShaderRemap.prototype.onPropertyChanged = function() { + if(this.graph) + this.graph._version++; + } + + LGraphShaderRemap.prototype.onConnectionsChange = function() { + var return_type = this.getInputDataType(0); + this.outputs[0].type = return_type || "T"; + } + + LGraphShaderRemap.prototype.onGetCode = function( context ) { + if(!this.shader_destination || !this.isOutputConnected(0)) + return; + + var inlink = getInputLinkID(this,0); + var outlink = getOutputLinkID(this,0); + if(!inlink && !outlink) // not connected + return; + + var return_type = this.getInputDataType(0); + this.outputs[0].type = return_type; + if(return_type == "T") { + console.warn("node type is T and cannot be resolved"); return; } - var img = this.getInputOrProperty("img"); - if (!img) { + if(!inlink) { + context.addCode("code"," " + return_type + " " + outlink + " = " + return_type + "(0.0);\n"); return; } - var x = this.getInputOrProperty("x"); - var y = this.getInputOrProperty("y"); - var ctx = canvas.getContext("2d"); - ctx.drawImage(img, x, y); + var minv = valueToGLSL( this.properties.min_value ); + var maxv = valueToGLSL( this.properties.max_value ); + var minv2 = valueToGLSL( this.properties.min_value2 ); + var maxv2 = valueToGLSL( this.properties.max_value2 ); + + context.addCode("code", return_type + " " + outlink + " = ( (" + inlink + " - "+minv+") / ("+ maxv+" - "+minv+") ) * ("+ maxv2+" - "+minv2+") + " + minv2 + ";", this.shader_destination ); + this.setOutputData( 0, return_type ); + } + + registerShaderNode( "math/remap", LGraphShaderRemap ); + +})(this); + + + +(function(global) { + var LiteGraph = global.LiteGraph; + + var view_matrix = new Float32Array(16); + var projection_matrix = new Float32Array(16); + var viewprojection_matrix = new Float32Array(16); + var model_matrix = new Float32Array(16); + var global_uniforms = { + u_view: view_matrix, + u_projection: projection_matrix, + u_viewprojection: viewprojection_matrix, + u_model: model_matrix, }; - LiteGraph.registerNodeType("graphics/drawImage", DrawImageNode); + LiteGraph.LGraphRender = {onRequestCameraMatrices: null }; // overwrite with your 3D engine specifics, it will receive (view_matrix, projection_matrix,viewprojection_matrix) and must be filled - function DrawRectangleNode() { - this.addInput("canvas", "canvas"); - this.addInput("x", "number"); - this.addInput("y", "number"); - this.addInput("w", "number"); - this.addInput("h", "number"); - this.properties = { - x: 0, - y: 0, - w: 10, - h: 10, - color: "white", - opacity: 1 - }; + function generateGeometryId() { + return (Math.random() * 100000)|0; } - DrawRectangleNode.title = "DrawRectangle"; - DrawRectangleNode.desc = "Draws rectangle in canvas"; + function LGraphPoints3D() { - DrawRectangleNode.prototype.onExecute = function() { - var canvas = this.getInputData(0); - if (!canvas) { - return; - } + this.addInput("obj", ""); + this.addInput("radius", "number"); - var x = this.getInputOrProperty("x"); - var y = this.getInputOrProperty("y"); - var w = this.getInputOrProperty("w"); - var h = this.getInputOrProperty("h"); - var ctx = canvas.getContext("2d"); - ctx.fillRect(x, y, w, h); - }; + this.addOutput("out", "geometry"); + this.addOutput("points", "[vec3]"); + this.properties = { + radius: 1, + num_points: 4096, + generate_normals: true, + regular: false, + mode: LGraphPoints3D.SPHERE, + force_update: false, + }; - LiteGraph.registerNodeType("graphics/drawRectangle", DrawRectangleNode); + this.points = new Float32Array( this.properties.num_points * 3 ); + this.normals = new Float32Array( this.properties.num_points * 3 ); + this.must_update = true; + this.version = 0; - function ImageVideo() { - this.addInput("t", "number"); - this.addOutputs([["frame", "image"], ["t", "number"], ["d", "number"]]); - this.properties = { url: "", use_proxy: true }; + var that = this; + this.addWidget("button","update",null, function() { + that.must_update = true; + }); + + this.geometry = { + vertices: null, + _id: generateGeometryId(), + } + + this._old_obj = null; + this._last_radius = null; } - 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" } - ]; + global.LGraphPoints3D = LGraphPoints3D; + + LGraphPoints3D.RECTANGLE = 1; + LGraphPoints3D.CIRCLE = 2; - ImageVideo.prototype.onExecute = function() { - if (!this.properties.url) { - return; - } + LGraphPoints3D.CUBE = 10; + LGraphPoints3D.SPHERE = 11; + LGraphPoints3D.HEMISPHERE = 12; + LGraphPoints3D.INSIDE_SPHERE = 13; - if (this.properties.url != this._video_url) { - this.loadVideo(this.properties.url); + LGraphPoints3D.OBJECT = 20; + LGraphPoints3D.OBJECT_UNIFORMLY = 21; + LGraphPoints3D.OBJECT_INSIDE = 22; + + LGraphPoints3D.MODE_VALUES = { "rectangle": LGraphPoints3D.RECTANGLE, "circle": LGraphPoints3D.CIRCLE, "cube": LGraphPoints3D.CUBE, "sphere": LGraphPoints3D.SPHERE, "hemisphere": LGraphPoints3D.HEMISPHERE, "inside_sphere": LGraphPoints3D.INSIDE_SPHERE, "object": LGraphPoints3D.OBJECT, "object_uniformly": LGraphPoints3D.OBJECT_UNIFORMLY, "object_inside": LGraphPoints3D.OBJECT_INSIDE }; + + LGraphPoints3D.widgets_info = {mode: { widget: "combo", values: LGraphPoints3D.MODE_VALUES }}; + + LGraphPoints3D.title = "list of points"; + LGraphPoints3D.desc = "returns an array of points"; + + LGraphPoints3D.prototype.onPropertyChanged = function(name,value) { + this.must_update = true; + } + + LGraphPoints3D.prototype.onExecute = function() { + + var obj = this.getInputData(0); + if( obj != this._old_obj || (obj && obj._version != this._old_obj_version) ) { + this._old_obj = obj; + this.must_update = true; } - if (!this._video || this._video.width == 0) { - return; + var radius = this.getInputData(1); + if(radius == null) + radius = this.properties.radius; + if( this._last_radius != radius ) { + this._last_radius = radius; + this.must_update = true; } - var t = this.getInputData(0); - if (t && t >= 0 && t <= 1.0) { - this._video.currentTime = t * this._video.duration; - this._video.pause(); + if(this.must_update || this.properties.force_update ) { + this.must_update = false; + this.updatePoints(); } - this._video.dirty = true; - this.setOutputData(0, this._video); - this.setOutputData(1, this._video.currentTime); - this.setOutputData(2, this._video.duration); - this.setDirtyCanvas(true); - }; + this.geometry.vertices = this.points; + this.geometry.normals = this.normals; + this.geometry._version = this.version; - ImageVideo.prototype.onStart = function() { - this.play(); - }; + this.setOutputData( 0, this.geometry ); + } - ImageVideo.prototype.onStop = function() { - this.stop(); - }; + LGraphPoints3D.prototype.updatePoints = function() { + var num_points = this.properties.num_points|0; + if(num_points < 1) + num_points = 1; - ImageVideo.prototype.loadVideo = function(url) { - this._video_url = url; + if(!this.points || this.points.length != num_points * 3) + this.points = new Float32Array( num_points * 3 ); - var pos = url.substr(0,10).indexOf(":"); - var protocol = ""; - if(pos != -1) - protocol = url.substr(0,pos); + if(this.properties.generate_normals) { + if (!this.normals || this.normals.length != this.points.length) + this.normals = new Float32Array( this.points.length ); + } else + this.normals = null; - var host = ""; - if(protocol) - { - host = url.substr(0,url.indexOf("/",protocol.length + 3)); - host = host.substr(protocol.length+3); - } + var radius = this._last_radius || this.properties.radius; + var mode = this.properties.mode; - if ( - this.properties.use_proxy && - protocol && - LiteGraph.proxy && - host != location.host - ) { - url = LiteGraph.proxy + url.substr(url.indexOf(":") + 3); - } + var obj = this.getInputData(0); + this._old_obj_version = obj ? obj._version : null; - this._video = document.createElement("video"); - this._video.src = url; - this._video.type = "type=video/mp4"; + this.points = LGraphPoints3D.generatePoints( radius, num_points, mode, this.points, this.normals, this.properties.regular, obj ); - this._video.muted = true; - this._video.autoplay = true; + this.version++; + } - var that = this; - this._video.addEventListener("loadedmetadata", function(e) { - //onload - console.log("Duration: " + this.duration + " seconds"); - console.log("Size: " + this.videoWidth + "," + this.videoHeight); - that.setDirtyCanvas(true); - this.width = this.videoWidth; - this.height = this.videoHeight; - }); - this._video.addEventListener("progress", function(e) { - //onload - console.log("video loading..."); - }); - this._video.addEventListener("error", function(e) { - console.error("Error loading video: " + this.src); - if (this.error) { - switch (this.error.code) { - case this.error.MEDIA_ERR_ABORTED: - console.error("You stopped the video."); - break; - case this.error.MEDIA_ERR_NETWORK: - console.error("Network error - please try again later."); - break; - case this.error.MEDIA_ERR_DECODE: - console.error("Video is broken.."); - break; - case this.error.MEDIA_ERR_SRC_NOT_SUPPORTED: - console.error("Sorry, your browser can't play this video."); - break; + // global + LGraphPoints3D.generatePoints = function( radius, num_points, mode, points, normals, regular, obj ) { + var size = num_points * 3; + if(!points || points.length != size) + points = new Float32Array( size ); + var temp = new Float32Array(3); + var UP = new Float32Array([0,1,0]); + + if(regular) { + if( mode == LGraphPoints3D.RECTANGLE) { + var side = Math.floor(Math.sqrt(num_points)); + for(var i = 0; i < side; ++i) + for(var j = 0; j < side; ++j) { + var pos = i*3 + j*3*side; + points[pos] = ((i/side) - 0.5) * radius * 2; + points[pos+1] = 0; + points[pos+2] = ((j/side) - 0.5) * radius * 2; + } + points = new Float32Array( points.subarray(0,side*side*3) ); + if(normals) { + for(var i = 0; i < normals.length; i+=3) + normals.set(UP, i); + } + } else if( mode == LGraphPoints3D.SPHERE) { + var side = Math.floor(Math.sqrt(num_points)); + for(var i = 0; i < side; ++i) + for(var j = 0; j < side; ++j) { + var pos = i*3 + j*3*side; + polarToCartesian( temp, (i/side) * 2 * Math.PI, ((j/side) - 0.5) * 2 * Math.PI, radius ); + points[pos] = temp[0]; + points[pos+1] = temp[1]; + points[pos+2] = temp[2]; + } + points = new Float32Array( points.subarray(0,side*side*3) ); + if(normals) + LGraphPoints3D.generateSphericalNormals( points, normals ); + } else if( mode == LGraphPoints3D.CIRCLE) { + for(var i = 0; i < size; i+=3) { + var angle = 2 * Math.PI * (i/size); + points[i] = Math.cos( angle ) * radius; + points[i+1] = 0; + points[i+2] = Math.sin( angle ) * radius; + } + if(normals) { + for(var i = 0; i < normals.length; i+=3) + normals.set(UP, i); } } - }); - - this._video.addEventListener("ended", function(e) { - console.log("Video Ended."); - this.play(); //loop - }); + } else { // non regular + if( mode == LGraphPoints3D.RECTANGLE) { + for(var i = 0; i < size; i+=3) { + points[i] = (Math.random() - 0.5) * radius * 2; + points[i+1] = 0; + points[i+2] = (Math.random() - 0.5) * radius * 2; + } + if(normals) { + for(var i = 0; i < normals.length; i+=3) + normals.set(UP, i); + } + } else if( mode == LGraphPoints3D.CUBE) { + for(var i = 0; i < size; i+=3) { + points[i] = (Math.random() - 0.5) * radius * 2; + points[i+1] = (Math.random() - 0.5) * radius * 2; + points[i+2] = (Math.random() - 0.5) * radius * 2; + } + if(normals) { + for(var i = 0; i < normals.length; i+=3) + normals.set(UP, i); + } + } else if( mode == LGraphPoints3D.SPHERE) { + LGraphPoints3D.generateSphere( points, size, radius ); + if(normals) + LGraphPoints3D.generateSphericalNormals( points, normals ); + } else if( mode == LGraphPoints3D.HEMISPHERE) { + LGraphPoints3D.generateHemisphere( points, size, radius ); + if(normals) + LGraphPoints3D.generateSphericalNormals( points, normals ); + } else if( mode == LGraphPoints3D.CIRCLE) { + LGraphPoints3D.generateInsideCircle( points, size, radius ); + if(normals) + LGraphPoints3D.generateSphericalNormals( points, normals ); + } else if( mode == LGraphPoints3D.INSIDE_SPHERE) { + LGraphPoints3D.generateInsideSphere( points, size, radius ); + if(normals) + LGraphPoints3D.generateSphericalNormals( points, normals ); + } else if( mode == LGraphPoints3D.OBJECT) { + LGraphPoints3D.generateFromObject( points, normals, size, obj, false ); + } else if( mode == LGraphPoints3D.OBJECT_UNIFORMLY) { + LGraphPoints3D.generateFromObject( points, normals, size, obj, true ); + } else if( mode == LGraphPoints3D.OBJECT_INSIDE) { + LGraphPoints3D.generateFromInsideObject( points, size, obj ); + // if(normals) + // LGraphPoints3D.generateSphericalNormals( points, normals ); + } else + console.warn("wrong mode in LGraphPoints3D"); + } + + return points; + } - //document.body.appendChild(this.video); - }; + LGraphPoints3D.generateSphericalNormals = function(points, normals) { + var temp = new Float32Array(3); + for(var i = 0; i < normals.length; i+=3) { + temp[0] = points[i]; + temp[1] = points[i+1]; + temp[2] = points[i+2]; + vec3.normalize(temp,temp); + normals.set(temp,i); + } + } - ImageVideo.prototype.onPropertyChanged = function(name, value) { - this.properties[name] = value; - if (name == "url" && value != "") { - this.loadVideo(value); + LGraphPoints3D.generateSphere = function (points, size, radius) { + for(var i = 0; i < size; i+=3) { + var r1 = Math.random(); + var r2 = Math.random(); + var x = 2 * Math.cos( 2 * Math.PI * r1 ) * Math.sqrt( r2 * (1-r2) ); + var y = 1 - 2 * r2; + var z = 2 * Math.sin( 2 * Math.PI * r1 ) * Math.sqrt( r2 * (1-r2) ); + points[i] = x * radius; + points[i+1] = y * radius; + points[i+2] = z * radius; } + } - return true; - }; + LGraphPoints3D.generateHemisphere = function (points, size, radius) { + for(var i = 0; i < size; i+=3) { + var r1 = Math.random(); + var r2 = Math.random(); + var x = Math.cos( 2 * Math.PI * r1 ) * Math.sqrt(1 - r2*r2 ); + var y = r2; + var z = Math.sin( 2 * Math.PI * r1 ) * Math.sqrt(1 - r2*r2 ); + points[i] = x * radius; + points[i+1] = y * radius; + points[i+2] = z * radius; + } + } - ImageVideo.prototype.play = function() { - if (this._video && this._video.videoWidth ) { //is loaded - this._video.play(); + LGraphPoints3D.generateInsideCircle = function (points, size, radius) { + for(var i = 0; i < size; i+=3) { + var r1 = Math.random(); + var r2 = Math.random(); + var x = Math.cos( 2 * Math.PI * r1 ) * Math.sqrt(1 - r2*r2 ); + var y = r2; + var z = Math.sin( 2 * Math.PI * r1 ) * Math.sqrt(1 - r2*r2 ); + points[i] = x * radius; + points[i+1] = 0; + points[i+2] = z * radius; } - }; + } - ImageVideo.prototype.playPause = function() { - if (!this._video) { - return; + LGraphPoints3D.generateInsideSphere = function (points, size, radius) { + for(var i = 0; i < size; i+=3) { + var u = Math.random(); + var v = Math.random(); + var theta = u * 2.0 * Math.PI; + var phi = Math.acos(2.0 * v - 1.0); + var r = Math.cbrt(Math.random()) * radius; + var sinTheta = Math.sin(theta); + var cosTheta = Math.cos(theta); + var sinPhi = Math.sin(phi); + var cosPhi = Math.cos(phi); + points[i] = r * sinPhi * cosTheta; + points[i+1] = r * sinPhi * sinTheta; + points[i+2] = r * cosPhi; } - if (this._video.paused) { - this.play(); - } else { - this.pause(); + } + + function findRandomTriangle( areas, f ) { + var l = areas.length; + var imin = 0; + var imid = 0; + var imax = l; + + if(l == 0) + return -1; + if(l == 1) + return 0; + // dichotomic search + while (imax >= imin) { + imid = ((imax + imin)*0.5)|0; + var t = areas[imid]; + if( t == f ) + return imid; + if( imin == (imax - 1) ) + return imin; + if (t < f) + imin = imid; + else + imax = imid; } - }; + return imid; + } - ImageVideo.prototype.stop = function() { - if (!this._video) { + LGraphPoints3D.generateFromObject = function( points, normals, size, obj, evenly ) { + if(!obj) return; + + var vertices = null; + var mesh_normals = null; + var indices = null; + var areas = null; + if( obj.constructor === GL.Mesh ) { + vertices = obj.vertexBuffers.vertices.data; + mesh_normals = obj.vertexBuffers.normals ? obj.vertexBuffers.normals.data : null; + indices = obj.indexBuffers.indices ? obj.indexBuffers.indices.data : null; + if(!indices) + indices = obj.indexBuffers.triangles ? obj.indexBuffers.triangles.data : null; + } + if(!vertices) + return null; + var num_triangles = indices ? indices.length / 3 : vertices.length / (3*3); + var total_area = 0; // sum of areas of all triangles + + if(evenly) { + areas = new Float32Array(num_triangles); // accum + for(var i = 0; i < num_triangles; ++i) { + if(indices) { + a = indices[i*3]*3; + b = indices[i*3+1]*3; + c = indices[i*3+2]*3; + } else { + a = i*9; + b = i*9+3; + c = i*9+6; + } + var P1 = vertices.subarray(a,a+3); + var P2 = vertices.subarray(b,b+3); + var P3 = vertices.subarray(c,c+3); + var aL = vec3.distance( P1, P2 ); + var bL = vec3.distance( P2, P3 ); + var cL = vec3.distance( P3, P1 ); + var s = (aL + bL+ cL) / 2; + total_area += Math.sqrt(s * (s - aL) * (s - bL) * (s - cL)); + areas[i] = total_area; + } + for(var i = 0; i < num_triangles; ++i) // normalize + areas[i] /= total_area; + } + + for(var i = 0; i < size; i+=3) { + var r = Math.random(); + var index = evenly ? findRandomTriangle( areas, r ) : Math.floor(r * num_triangles ); + // get random triangle + var a = 0; + var b = 0; + var c = 0; + if(indices) { + a = indices[index*3]*3; + b = indices[index*3+1]*3; + c = indices[index*3+2]*3; + } else { + a = index*9; + b = index*9+3; + c = index*9+6; + } + var s = Math.random(); + var t = Math.random(); + var sqrt_s = Math.sqrt(s); + var af = 1 - sqrt_s; + var bf = sqrt_s * ( 1 - t); + var cf = t * sqrt_s; + points[i] = af * vertices[a] + bf*vertices[b] + cf*vertices[c]; + points[i+1] = af * vertices[a+1] + bf*vertices[b+1] + cf*vertices[c+1]; + points[i+2] = af * vertices[a+2] + bf*vertices[b+2] + cf*vertices[c+2]; + if(normals && mesh_normals) { + normals[i] = af * mesh_normals[a] + bf*mesh_normals[b] + cf*mesh_normals[c]; + normals[i+1] = af * mesh_normals[a+1] + bf*mesh_normals[b+1] + cf*mesh_normals[c+1]; + normals[i+2] = af * mesh_normals[a+2] + bf*mesh_normals[b+2] + cf*mesh_normals[c+2]; + var N = normals.subarray(i,i+3); + vec3.normalize(N,N); + } } - this._video.pause(); - this._video.currentTime = 0; - }; + } - ImageVideo.prototype.pause = function() { - if (!this._video) { + LGraphPoints3D.generateFromInsideObject = function( points, size, mesh ) { + if(!mesh || mesh.constructor !== GL.Mesh) return; + + var aabb = mesh.getBoundingBox(); + if(!mesh.octree) + mesh.octree = new GL.Octree( mesh ); + var octree = mesh.octree; + var origin = vec3.create(); + var direction = vec3.fromValues(1,0,0); + var temp = vec3.create(); + var i = 0; + var tries = 0; + while(i < size && tries < points.length * 10) { // limit to avoid problems + tries += 1 + var r = vec3.random(temp); // random point inside the aabb + r[0] = (r[0] * 2 - 1) * aabb[3] + aabb[0]; + r[1] = (r[1] * 2 - 1) * aabb[4] + aabb[1]; + r[2] = (r[2] * 2 - 1) * aabb[5] + aabb[2]; + origin.set(r); + var hit = octree.testRay( origin, direction, 0, 10000, true, GL.Octree.ALL ); + if(!hit || hit.length % 2 == 0) // not inside + continue; + points.set( r, i ); + i+=3; } - console.log("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( "geometry/points3D", LGraphPoints3D ); - LiteGraph.registerNodeType("graphics/video", ImageVideo); - // Texture Webcam ***************************************** - function ImageWebcam() { - this.addOutput("Webcam", "image"); - this.properties = { filterFacingMode: false, facingMode: "user" }; - this.boxcolor = "black"; - this.frame = 0; + + function LGraphPointsToInstances() { + this.addInput("points", "geometry"); + this.addOutput("instances", "[mat4]"); + this.properties = { + mode: 1, + autoupdate: true, + }; + + this.must_update = true; + this.matrices = []; + this.first_time = true; } - ImageWebcam.title = "Webcam"; - ImageWebcam.desc = "Webcam image"; - ImageWebcam.is_webcam_open = false; + LGraphPointsToInstances.NORMAL = 0; + LGraphPointsToInstances.VERTICAL = 1; + LGraphPointsToInstances.SPHERICAL = 2; + LGraphPointsToInstances.RANDOM = 3; + LGraphPointsToInstances.RANDOM_VERTICAL = 4; - ImageWebcam.prototype.openStream = function() { - if (!navigator.mediaDevices.getUserMedia) { - console.log('getUserMedia() is not supported in your browser, use chrome and enable WebRTC from about://flags'); + LGraphPointsToInstances.modes = {"normal": 0,"vertical": 1,"spherical": 2,"random": 3,"random_vertical": 4}; + LGraphPointsToInstances.widgets_info = {mode: { widget: "combo", values: LGraphPointsToInstances.modes }}; + + LGraphPointsToInstances.title = "points to inst"; + + LGraphPointsToInstances.prototype.onExecute = function() { + var geo = this.getInputData(0); + if( !geo ) { + this.setOutputData(0,null); return; } - this._waiting_confirmation = true; + if( !this.isOutputConnected(0) ) + return; - // Not showing vendor prefixes. - var constraints = { - audio: false, - video: !this.properties.filterFacingMode ? true : { facingMode: this.properties.facingMode } - }; - navigator.mediaDevices - .getUserMedia(constraints) - .then(this.streamReady.bind(this)) - .catch(onFailSoHard); + var has_changed = (geo._version != this._version || geo._id != this._geometry_id); - var that = this; - function onFailSoHard(e) { - console.log("Webcam rejected", e); - that._webcam_stream = false; - ImageWebcam.is_webcam_open = false; - that.boxcolor = "red"; - that.trigger("stream_error"); + if( has_changed && this.properties.autoupdate || this.first_time ) { + this.first_time = false; + this.updateInstances( geo ); } - }; - ImageWebcam.prototype.closeStream = function() { - if (this._webcam_stream) { - var tracks = this._webcam_stream.getTracks(); - if (tracks.length) { - for (var i = 0; i < tracks.length; ++i) { - tracks[i].stop(); - } + this.setOutputData( 0, this.matrices ); + } + + LGraphPointsToInstances.prototype.updateInstances = function( geometry ) { + var vertices = geometry.vertices; + if(!vertices) + return null; + var normals = geometry.normals; + + var matrices = this.matrices; + var num_points = vertices.length / 3; + if( matrices.length != num_points) + matrices.length = num_points; + var identity = mat4.create(); + var temp = vec3.create(); + var zero = vec3.create(); + var UP = vec3.fromValues(0,1,0); + var FRONT = vec3.fromValues(0,0,-1); + var RIGHT = vec3.fromValues(1,0,0); + var R = quat.create(); + + var front = vec3.create(); + var right = vec3.create(); + var top = vec3.create(); + + for(var i = 0; i < vertices.length; i += 3) { + var index = i/3; + var m = matrices[index]; + if(!m) + m = matrices[index] = mat4.create(); + m.set( identity ); + var point = vertices.subarray(i,i+3); + + switch(this.properties.mode) { + case LGraphPointsToInstances.NORMAL: + mat4.setTranslation( m, point ); + if(normals) { + var normal = normals.subarray(i,i+3); + top.set( normal ); + vec3.normalize( top, top ); + vec3.cross( right, FRONT, top ); + vec3.normalize( right, right ); + vec3.cross( front, right, top ); + vec3.normalize( front, front ); + m.set(right,0); + m.set(top,4); + m.set(front,8); + mat4.setTranslation( m, point ); + } + break; + case LGraphPointsToInstances.VERTICAL: + mat4.setTranslation( m, point ); + break; + case LGraphPointsToInstances.SPHERICAL: + front.set( point ); + vec3.normalize( front, front ); + vec3.cross( right, UP, front ); + vec3.normalize( right, right ); + vec3.cross( top, front, right ); + vec3.normalize( top, top ); + m.set(right,0); + m.set(top,4); + m.set(front,8); + mat4.setTranslation( m, point ); + break; + case LGraphPointsToInstances.RANDOM: + temp[0] = Math.random()*2 - 1; + temp[1] = Math.random()*2 - 1; + temp[2] = Math.random()*2 - 1; + vec3.normalize( temp, temp ); + quat.setAxisAngle( R, temp, Math.random() * 2 * Math.PI ); + mat4.fromQuat(m, R); + mat4.setTranslation( m, point ); + break; + case LGraphPointsToInstances.RANDOM_VERTICAL: + quat.setAxisAngle( R, UP, Math.random() * 2 * Math.PI ); + mat4.fromQuat(m, R); + mat4.setTranslation( m, point ); + break; } - ImageWebcam.is_webcam_open = false; - this._webcam_stream = null; - this._video = null; - this.boxcolor = "black"; - this.trigger("stream_closed"); } - }; - ImageWebcam.prototype.onPropertyChanged = function(name, value) { - if (name == "facingMode") { - this.properties.facingMode = value; - this.closeStream(); - this.openStream(); - } - }; + this._version = geometry._version; + this._geometry_id = geometry._id; + } - ImageWebcam.prototype.onRemoved = function() { - this.closeStream(); - }; + LiteGraph.registerNodeType( "geometry/points_to_instances", LGraphPointsToInstances ); - ImageWebcam.prototype.streamReady = function(localMediaStream) { - this._webcam_stream = localMediaStream; - //this._waiting_confirmation = false; - this.boxcolor = "green"; - var video = this._video; - if (!video) { - video = document.createElement("video"); - video.autoplay = true; - video.srcObject = 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.is_webcam_open = true; - }; + function LGraphGeometryTransform() { + this.addInput("in", "geometry,[mat4]"); + this.addInput("mat4", "mat4"); + this.addOutput("out", "geometry"); + this.properties = {}; + + this.geometry = { + type: "triangles", + vertices: null, + _id: generateGeometryId(), + _version: 0, + }; + + this._last_geometry_id = -1; + this._last_version = -1; + this._last_key = ""; + + this.must_update = true; + } + + LGraphGeometryTransform.title = "Transform"; + + LGraphGeometryTransform.prototype.onExecute = function() { + + var input = this.getInputData(0); + var model = this.getInputData(1); + + if(!input) + return; + + // array of matrices + if(input.constructor === Array) { + if(input.length == 0) + return; + this.outputs[0].type = "[mat4]"; + if( !this.isOutputConnected(0) ) + return; + + if(!model) { + this.setOutputData(0,input); + return; + } + + if(!this._output) + this._output = new Array(); + if(this._output.length != input.length) + this._output.length = input.length; + for(var i = 0; i < input.length; ++i) { + var m = this._output[i]; + if(!m) + m = this._output[i] = mat4.create(); + mat4.multiply(m,input[i],model); + } + this.setOutputData(0,this._output); + return; + } + + // geometry + if(!input.vertices || !input.vertices.length) + return; + var geo = input; + this.outputs[0].type = "geometry"; + if( !this.isOutputConnected(0) ) + return; + if(!model) { + this.setOutputData(0,geo); + return; } - this.trigger("stream_ready", video); - }; + var key = typedArrayToArray(model).join(","); - ImageWebcam.prototype.onExecute = function() { - if (this._webcam_stream == null && !this._waiting_confirmation) { - this.openStream(); + if( this.must_update || geo._id != this._last_geometry_id || geo._version != this._last_version || key != this._last_key ) { + this.updateGeometry(geo, model); + this._last_key = key; + this._last_version = geo._version; + this._last_geometry_id = geo._id; + this.must_update = false; } - if (!this._video || !this._video.videoWidth) { - return; + this.setOutputData(0,this.geometry); + } + + LGraphGeometryTransform.prototype.updateGeometry = function(geometry, model) { + var old_vertices = geometry.vertices; + var vertices = this.geometry.vertices; + if( !vertices || vertices.length != old_vertices.length ) + vertices = this.geometry.vertices = new Float32Array( old_vertices.length ); + var temp = vec3.create(); + + for(var i = 0, l = vertices.length; i < l; i+=3) { + temp[0] = old_vertices[i]; temp[1] = old_vertices[i+1]; temp[2] = old_vertices[i+2]; + mat4.multiplyVec3( temp, model, temp ); + vertices[i] = temp[0]; vertices[i+1] = temp[1]; vertices[i+2] = temp[2]; } - this._video.frame = ++this.frame; - this._video.width = this._video.videoWidth; - this._video.height = this._video.videoHeight; - this.setOutputData(0, this._video); - for (var i = 1; i < this.outputs.length; ++i) { - if (!this.outputs[i]) { - continue; - } - switch (this.outputs[i].name) { - case "width": - this.setOutputData(i, this._video.videoWidth); - break; - case "height": - this.setOutputData(i, this._video.videoHeight); - break; + if(geometry.normals) { + if( !this.geometry.normals || this.geometry.normals.length != geometry.normals.length ) + this.geometry.normals = new Float32Array( geometry.normals.length ); + var normals = this.geometry.normals; + var normal_model = mat4.invert(mat4.create(), model); + if(normal_model) + mat4.transpose(normal_model, normal_model); + var old_normals = geometry.normals; + for(var i = 0, l = normals.length; i < l; i+=3) { + temp[0] = old_normals[i]; temp[1] = old_normals[i+1]; temp[2] = old_normals[i+2]; + mat4.multiplyVec3( temp, normal_model, temp ); + normals[i] = temp[0]; normals[i+1] = temp[1]; normals[i+2] = temp[2]; } } - }; - ImageWebcam.prototype.getExtraMenuOptions = function(graphcanvas) { - var that = this; - var txt = !that.properties.show ? "Show Frame" : "Hide Frame"; - return [ - { - content: txt, - callback: function() { - that.properties.show = !that.properties.show; - } - } - ]; - }; + this.geometry.type = geometry.type; + this.geometry._version++; + } - ImageWebcam.prototype.onDrawBackground = function(ctx) { - if ( - this.flags.collapsed || - this.size[1] <= 20 || - !this.properties.show - ) { + LiteGraph.registerNodeType( "geometry/transform", LGraphGeometryTransform ); + + + function LGraphGeometryPolygon() { + this.addInput("sides", "number"); + this.addInput("radius", "number"); + this.addOutput("out", "geometry"); + this.properties = { sides: 6, radius: 1, uvs: false } + + this.geometry = { + type: "line_loop", + vertices: null, + _id: generateGeometryId(), + }; + this.geometry_id = -1; + this.version = -1; + this.must_update = true; + + this.last_info = { sides: -1, radius: -1 }; + } + + LGraphGeometryPolygon.title = "Polygon"; + + LGraphGeometryPolygon.prototype.onExecute = function() { + + if( !this.isOutputConnected(0) ) return; - } - if (!this._video) { + var sides = this.getInputOrProperty("sides"); + var radius = this.getInputOrProperty("radius"); + sides = Math.max(3,sides)|0; + + // update + if( this.last_info.sides != sides || this.last_info.radius != radius ) + this.updateGeometry(sides, radius); + + this.setOutputData(0,this.geometry); + } + + LGraphGeometryPolygon.prototype.updateGeometry = function(sides, radius) { + var num = 3*sides; + var vertices = this.geometry.vertices; + if( !vertices || vertices.length != num ) + vertices = this.geometry.vertices = new Float32Array( 3*sides ); + var delta = (Math.PI * 2) / sides; + var gen_uvs = this.properties.uvs; + if(gen_uvs) { + uvs = this.geometry.coords = new Float32Array( 3*sides ); + } + + + for(var i = 0; i < sides; ++i) { + var angle = delta * -i; + var x = Math.cos( angle ) * radius; + var y = 0; + var z = Math.sin( angle ) * radius; + vertices[i*3] = x; + vertices[i*3+1] = y; + vertices[i*3+2] = z; + } + this.geometry._id = ++this.geometry_id; + this.geometry._version = ++this.version; + this.last_info.sides = sides; + this.last_info.radius = radius; + } + + LiteGraph.registerNodeType( "geometry/polygon", LGraphGeometryPolygon ); + + + function LGraphGeometryExtrude() { + + this.addInput("", "geometry"); + this.addOutput("", "geometry"); + this.properties = { top_cap: true, bottom_cap: true, offset: [0,100,0] }; + this.version = -1; + + this._last_geo_version = -1; + this._must_update = true; + } + + LGraphGeometryExtrude.title = "extrude"; + + LGraphGeometryExtrude.prototype.onPropertyChanged = function(name, value) { + this._must_update = true; + } + + LGraphGeometryExtrude.prototype.onExecute = function() { + var geo = this.getInputData(0); + if( !geo || !this.isOutputConnected(0) ) return; + + if(geo.version != this._last_geo_version || this._must_update) { + this._geo = this.extrudeGeometry( geo, this._geo ); + if(this._geo) + this._geo.version = this.version++; + this._must_update = false; } - //render to graph canvas - ctx.save(); - ctx.drawImage(this._video, 0, 0, this.size[0], this.size[1]); - ctx.restore(); - }; + this.setOutputData(0, this._geo); + } - ImageWebcam.prototype.onGetOutputs = function() { - return [ - ["width", "number"], - ["height", "number"], - ["stream_ready", LiteGraph.EVENT], - ["stream_closed", LiteGraph.EVENT], - ["stream_error", LiteGraph.EVENT] - ]; - }; + LGraphGeometryExtrude.prototype.extrudeGeometry = function( geo ) { + // for every pair of vertices + var vertices = geo.vertices; + var num_points = vertices.length / 3; + + var tempA = vec3.create(); + var tempB = vec3.create(); + var tempC = vec3.create(); + var tempD = vec3.create(); + var offset = new Float32Array( this.properties.offset ); + + if(geo.type == "line_loop") { + var new_vertices = new Float32Array( num_points * 6 * 3 ); // every points become 6 ( caps not included ) + var npos = 0; + for(var i = 0, l = vertices.length; i < l; i += 3) { + tempA[0] = vertices[i]; tempA[1] = vertices[i+1]; tempA[2] = vertices[i+2]; + + if( i+3 < l ) { // loop + tempB[0] = vertices[i+3]; tempB[1] = vertices[i+4]; tempB[2] = vertices[i+5]; + } else { + tempB[0] = vertices[0]; tempB[1] = vertices[1]; tempB[2] = vertices[2]; + } - LiteGraph.registerNodeType("graphics/webcam", ImageWebcam); -})(this); + vec3.add( tempC, tempA, offset ); + vec3.add( tempD, tempB, offset ); -(function(global) { - var LiteGraph = global.LiteGraph; - var LGraphCanvas = global.LGraphCanvas; + new_vertices.set( tempA, npos ); npos += 3; + new_vertices.set( tempB, npos ); npos += 3; + new_vertices.set( tempC, npos ); npos += 3; - //Works with Litegl.js to create WebGL nodes - global.LGraphTexture = null; + new_vertices.set( tempB, npos ); npos += 3; + new_vertices.set( tempD, npos ); npos += 3; + new_vertices.set( tempC, npos ); npos += 3; + } + } - if (typeof GL == "undefined") - return; - - LGraphCanvas.link_type_colors["Texture"] = "#987"; - - function LGraphTexture() { - this.addOutput("tex", "Texture"); - this.addOutput("name", "string"); - this.properties = { name: "", filter: true }; - this.size = [ - LGraphTexture.image_preview_size, - LGraphTexture.image_preview_size - ]; - } + var out_geo = { + _id: generateGeometryId(), + type: "triangles", + vertices: new_vertices, + }; - global.LGraphTexture = LGraphTexture; + return out_geo; + } - LGraphTexture.title = "Texture"; - LGraphTexture.desc = "Texture"; - LGraphTexture.widgets_info = { - name: { widget: "texture" }, - filter: { widget: "checkbox" } - }; + LiteGraph.registerNodeType( "geometry/extrude", LGraphGeometryExtrude ); - //REPLACE THIS TO INTEGRATE WITH YOUR FRAMEWORK - LGraphTexture.loadTextureCallback = null; //function in charge of loading textures when not present in the container - LGraphTexture.image_preview_size = 256; - - //flags to choose output texture type - LGraphTexture.UNDEFINED = 0; //not specified - LGraphTexture.PASS_THROUGH = 1; //do not apply FX (like disable but passing the in to the out) - LGraphTexture.COPY = 2; //create new texture with the same properties as the origin texture - LGraphTexture.LOW = 3; //create new texture with low precision (byte) - LGraphTexture.HIGH = 4; //create new texture with high precision (half-float) - LGraphTexture.REUSE = 5; //reuse input texture - LGraphTexture.DEFAULT = 2; //use the default - - LGraphTexture.MODE_VALUES = { - "undefined": LGraphTexture.UNDEFINED, - "pass through": LGraphTexture.PASS_THROUGH, - copy: LGraphTexture.COPY, - low: LGraphTexture.LOW, - high: LGraphTexture.HIGH, - reuse: LGraphTexture.REUSE, - default: LGraphTexture.DEFAULT - }; - //returns the container where all the loaded textures are stored (overwrite if you have a Resources Manager) - LGraphTexture.getTexturesContainer = function() { - return gl.textures; - }; + function LGraphGeometryEval() { + this.addInput("in", "geometry"); + this.addOutput("out", "geometry"); - //process the loading of a texture (overwrite it if you have a Resources Manager) - LGraphTexture.loadTexture = function(name, options) { - options = options || {}; - var url = name; - if (url.substr(0, 7) == "http://") { - if (LiteGraph.proxy) { - //proxy external files - url = LiteGraph.proxy + url.substr(7); - } - } + this.properties = { + code: "V[1] += 0.01 * Math.sin(I + T*0.001);", + execute_every_frame: false, + }; - var container = LGraphTexture.getTexturesContainer(); - var tex = (container[name] = GL.Texture.fromURL(url, options)); - return tex; - }; + this.geometry = null; + this.geometry_id = -1; + this.version = -1; + this.must_update = true; - LGraphTexture.getTexture = function(name) { - var container = this.getTexturesContainer(); + this.vertices = null; + this.func = null; + } - if (!container) { - throw "Cannot load texture, container of textures not found"; - } + LGraphGeometryEval.title = "geoeval"; + LGraphGeometryEval.desc = "eval code"; - var tex = container[name]; - if (!tex && name && name[0] != ":") { - return this.loadTexture(name); - } + LGraphGeometryEval.widgets_info = {code: { widget: "code" }}; - return tex; - }; + LGraphGeometryEval.prototype.onConfigure = function(o) { + this.compileCode(); + } - //used to compute the appropiate output texture - LGraphTexture.getTargetTexture = function(origin, target, mode) { - if (!origin) { - throw "LGraphTexture.getTargetTexture expects a reference texture"; - } + LGraphGeometryEval.prototype.compileCode = function() { + if(!this.properties.code) + return; - var tex_type = null; - - switch (mode) { - case LGraphTexture.LOW: - tex_type = gl.UNSIGNED_BYTE; - break; - case LGraphTexture.HIGH: - tex_type = gl.HIGH_PRECISION_FORMAT; - break; - case LGraphTexture.REUSE: - return origin; - break; - case LGraphTexture.COPY: - default: - tex_type = origin ? origin.type : gl.UNSIGNED_BYTE; - break; - } + try { + this.func = new Function("V","I","T", this.properties.code); + this.boxcolor = "#AFA"; + this.must_update = true; + } catch (err) { + this.boxcolor = "red"; + } + } - if ( - !target || - target.width != origin.width || - target.height != origin.height || - target.type != tex_type || - target.format != origin.format - ) { - target = new GL.Texture(origin.width, origin.height, { - type: tex_type, - format: origin.format, - filter: gl.LINEAR - }); - } + LGraphGeometryEval.prototype.onPropertyChanged = function(name, value) { + if(name == "code") { + this.properties.code = value; + this.compileCode(); + } + } - return target; - }; + LGraphGeometryEval.prototype.onExecute = function() { + var geometry = this.getInputData(0); + if(!geometry) + return; - LGraphTexture.getTextureType = function(precision, ref_texture) { - var type = ref_texture ? ref_texture.type : gl.UNSIGNED_BYTE; - switch (precision) { - case LGraphTexture.HIGH: - type = gl.HIGH_PRECISION_FORMAT; - break; - case LGraphTexture.LOW: - type = gl.UNSIGNED_BYTE; - break; - //no default - } - return type; - }; + if(!this.func) { + this.setOutputData(0,geometry); + return; + } - LGraphTexture.getWhiteTexture = function() { - if (this._white_texture) { - return this._white_texture; - } - var texture = (this._white_texture = GL.Texture.fromMemory( - 1, - 1, - [255, 255, 255, 255], - { format: gl.RGBA, wrap: gl.REPEAT, filter: gl.NEAREST } - )); - return texture; - }; + if( this.geometry_id != geometry._id || this.version != geometry._version || this.must_update || this.properties.execute_every_frame ) { + this.must_update = false; + this.geometry_id = geometry._id; + if(this.properties.execute_every_frame) + this.version++; + else + this.version = geometry._version; + var func = this.func; + var T = getTime(); + + // clone + if(!this.geometry) + this.geometry = {}; + for(var i in geometry) { + if(geometry[i] == null) + continue; + if( geometry[i].constructor == Float32Array ) + this.geometry[i] = new Float32Array( geometry[i] ); + else + this.geometry[i] = geometry[i]; + } + this.geometry._id = geometry._id; + if(this.properties.execute_every_frame) + this.geometry._version = this.version; + else + this.geometry._version = geometry._version + 1; - LGraphTexture.getNoiseTexture = function() { - if (this._noise_texture) { - return this._noise_texture; - } + var V = vec3.create(); + var vertices = this.vertices; + if(!vertices || this.vertices.length != geometry.vertices.length) + vertices = this.vertices = new Float32Array( geometry.vertices ); + else + vertices.set( geometry.vertices ); + for(var i = 0; i < vertices.length; i+=3) { + V[0] = vertices[i]; + V[1] = vertices[i+1]; + V[2] = vertices[i+2]; + func(V,i/3,T); + vertices[i] = V[0]; + vertices[i+1] = V[1]; + vertices[i+2] = V[2]; + } + this.geometry.vertices = vertices; + } - var noise = new Uint8Array(512 * 512 * 4); - for (var i = 0; i < 512 * 512 * 4; ++i) { - noise[i] = Math.random() * 255; - } + this.setOutputData(0,this.geometry); + } - var texture = GL.Texture.fromMemory(512, 512, noise, { - format: gl.RGBA, - wrap: gl.REPEAT, - filter: gl.NEAREST - }); - this._noise_texture = texture; - return texture; - }; + LiteGraph.registerNodeType( "geometry/eval", LGraphGeometryEval ); - LGraphTexture.prototype.onDropFile = function(data, filename, file) { - if (!data) { - this._drop_texture = null; - this.properties.name = ""; - } else { - var texture = null; - if (typeof data == "string") { - texture = GL.Texture.fromURL(data); - } else if (filename.toLowerCase().indexOf(".dds") != -1) { - texture = GL.Texture.fromDDSInMemory(data); - } else { - var blob = new Blob([file]); - var url = URL.createObjectURL(blob); - texture = GL.Texture.fromURL(url); - } + /* +function LGraphGeometryDisplace() { + this.addInput("in", "geometry"); + this.addInput("img", "image"); + this.addOutput("out", "geometry"); - this._drop_texture = texture; - this.properties.name = filename; - } - }; + this.properties = { + grid_size: 1 + }; + + this.geometry = null; + this.geometry_id = -1; + this.version = -1; + this.must_update = true; + + this.vertices = null; + } + + LGraphGeometryDisplace.title = "displace"; + LGraphGeometryDisplace.desc = "displace points"; + + LGraphGeometryDisplace.prototype.onExecute = function() { + var geometry = this.getInputData(0); + var image = this.getInputData(1); + if(!geometry) + return; - LGraphTexture.prototype.getExtraMenuOptions = function(graphcanvas) { - var that = this; - if (!this._drop_texture) { + if(!image) + { + this.setOutputData(0,geometry); return; } - return [ + + if( this.geometry_id != geometry._id || this.version != geometry._version || this.must_update ) + { + this.must_update = false; + this.geometry_id = geometry._id; + this.version = geometry._version; + + //copy + this.geometry = {}; + for(var i in geometry) + this.geometry[i] = geometry[i]; + this.geometry._id = geometry._id; + this.geometry._version = geometry._version + 1; + + var grid_size = this.properties.grid_size; + if(grid_size != 0) { - content: "Clear", - callback: function() { - that._drop_texture = null; - that.properties.name = ""; + var vertices = this.vertices; + if(!vertices || this.vertices.length != this.geometry.vertices.length) + vertices = this.vertices = new Float32Array( this.geometry.vertices ); + for(var i = 0; i < vertices.length; i+=3) + { + vertices[i] = Math.round(vertices[i]/grid_size) * grid_size; + vertices[i+1] = Math.round(vertices[i+1]/grid_size) * grid_size; + vertices[i+2] = Math.round(vertices[i+2]/grid_size) * grid_size; } + this.geometry.vertices = vertices; } - ]; - }; - - LGraphTexture.prototype.onExecute = function() { - var tex = null; - if (this.isOutputConnected(1)) { - tex = this.getInputData(0); } - if (!tex && this._drop_texture) { - tex = this._drop_texture; - } + this.setOutputData(0,this.geometry); + } - if (!tex && this.properties.name) { - tex = LGraphTexture.getTexture(this.properties.name); - } + LiteGraph.registerNodeType( "geometry/displace", LGraphGeometryDisplace ); +*/ - if (!tex) { - this.setOutputData( 0, null ); - this.setOutputData( 1, "" ); - return; - } + function LGraphConnectPoints() { + this.addInput("in", "geometry"); + this.addOutput("out", "geometry"); + + this.properties = { + min_dist: 0.4, + max_dist: 0.5, + max_connections: 0, + probability: 1, + }; - this._last_tex = tex; + this.geometry_id = -1; + this.version = -1; + this.my_version = 1; + this.must_update = true; + } - if (this.properties.filter === false) { - tex.setParameter(gl.TEXTURE_MAG_FILTER, gl.NEAREST); - } else { - tex.setParameter(gl.TEXTURE_MAG_FILTER, gl.LINEAR); - } + LGraphConnectPoints.title = "connect points"; + LGraphConnectPoints.desc = "adds indices between near points"; - this.setOutputData( 0, tex ); - this.setOutputData( 1, tex.fullpath || tex.filename ); + LGraphConnectPoints.prototype.onPropertyChanged = function(name,value) { + this.must_update = true; + } - for (var i = 2; i < this.outputs.length; i++) { - var output = this.outputs[i]; - if (!output) { - continue; - } - var v = null; - if (output.name == "width") { - v = tex.width; - } else if (output.name == "height") { - v = tex.height; - } else if (output.name == "aspect") { - v = tex.width / tex.height; - } - this.setOutputData(i, v); - } - }; + LGraphConnectPoints.prototype.onExecute = function() { + var geometry = this.getInputData(0); + if(!geometry) + return; - LGraphTexture.prototype.onResourceRenamed = function( - old_name, - new_name - ) { - if (this.properties.name == old_name) { - this.properties.name = new_name; - } - }; + if( this.geometry_id != geometry._id || this.version != geometry._version || this.must_update ) { + this.must_update = false; + this.geometry_id = geometry._id; + this.version = geometry._version; + + // copy + this.geometry = {}; + for(var i in geometry) + this.geometry[i] = geometry[i]; + this.geometry._id = generateGeometryId(); + this.geometry._version = this.my_version++; + + var vertices = geometry.vertices; + var l = vertices.length; + var min_dist = this.properties.min_dist; + var max_dist = this.properties.max_dist; + var probability = this.properties.probability; + var max_connections = this.properties.max_connections; + var indices = []; + + for(var i = 0; i < l; i+=3) { + var x = vertices[i]; + var y = vertices[i+1]; + var z = vertices[i+2]; + var connections = 0; + for(var j = i+3; j < l; j+=3) { + var x2 = vertices[j]; + var y2 = vertices[j+1]; + var z2 = vertices[j+2]; + var dist = Math.sqrt( (x-x2)*(x-x2) + (y-y2)*(y-y2) + (z-z2)*(z-z2)); + if(dist > max_dist || dist < min_dist || (probability < 1 && probability < Math.random()) ) + continue; + indices.push(i/3,j/3); + connections += 1; + if(max_connections && connections > max_connections) + break; + } + } + this.geometry.indices = this.indices = new Uint32Array(indices); + } - LGraphTexture.prototype.onDrawBackground = function(ctx) { - if (this.flags.collapsed || this.size[1] <= 20) { - return; - } + if(this.indices && this.indices.length) { + this.geometry.indices = this.indices; + this.setOutputData( 0, this.geometry ); + } else + this.setOutputData( 0, null ); + } - if (this._drop_texture && ctx.webgl) { - ctx.drawImage( - this._drop_texture, - 0, - 0, - this.size[0], - this.size[1] - ); - //this._drop_texture.renderQuad(this.pos[0],this.pos[1],this.size[0],this.size[1]); - return; - } + LiteGraph.registerNodeType( "geometry/connectPoints", LGraphConnectPoints ); - //Different texture? then get it from the GPU - if (this._last_preview_tex != this._last_tex) { - if (ctx.webgl) { - this._canvas = this._last_tex; - } else { - var tex_canvas = LGraphTexture.generateLowResTexturePreview( - this._last_tex - ); - if (!tex_canvas) { - return; - } + // Works with Litegl.js to create WebGL nodes + if (typeof GL == "undefined") // LiteGL RELATED ********************************************** + return; - this._last_preview_tex = this._last_tex; - this._canvas = cloneCanvas(tex_canvas); - } - } + function LGraphToGeometry() { + this.addInput("mesh", "mesh"); + this.addOutput("out", "geometry"); - if (!this._canvas) { - return; - } + this.geometry = {}; + this.last_mesh = null; + } - //render to graph canvas - ctx.save(); - if (!ctx.webgl) { - //reverse image - ctx.translate(0, this.size[1]); - ctx.scale(1, -1); - } - ctx.drawImage(this._canvas, 0, 0, this.size[0], this.size[1]); - ctx.restore(); - }; + LGraphToGeometry.title = "to geometry"; + LGraphToGeometry.desc = "converts a mesh to geometry"; - //very slow, used at your own risk - LGraphTexture.generateLowResTexturePreview = function(tex) { - if (!tex) { - return null; - } + LGraphToGeometry.prototype.onExecute = function() { + var mesh = this.getInputData(0); + if(!mesh) + return; - var size = LGraphTexture.image_preview_size; - var temp_tex = tex; + if(mesh != this.last_mesh) { + this.last_mesh = mesh; + for(i in mesh.vertexBuffers) { + var buffer = mesh.vertexBuffers[i]; + this.geometry[i] = buffer.data + } + if(mesh.indexBuffers["triangles"]) + this.geometry.indices = mesh.indexBuffers["triangles"].data; - if (tex.format == gl.DEPTH_COMPONENT) { - return null; - } //cannot generate from depth + this.geometry._id = generateGeometryId(); + this.geometry._version = 0; + } - //Generate low-level version in the GPU to speed up - if (tex.width > size || tex.height > size) { - temp_tex = this._preview_temp_tex; - if (!this._preview_temp_tex) { - temp_tex = new GL.Texture(size, size, { - minFilter: gl.NEAREST - }); - this._preview_temp_tex = temp_tex; - } + this.setOutputData(0,this.geometry); + if(this.geometry) + this.setOutputData(1,this.geometry.vertices); + } - //copy - tex.copyTo(temp_tex); - tex = temp_tex; - } + LiteGraph.registerNodeType( "geometry/toGeometry", LGraphToGeometry ); - //create intermediate canvas with lowquality version - var tex_canvas = this._preview_canvas; - if (!tex_canvas) { - tex_canvas = createCanvas(size, size); - this._preview_canvas = tex_canvas; - } + function LGraphGeometryToMesh() { + this.addInput("in", "geometry"); + this.addOutput("mesh", "mesh"); + this.properties = {}; + this.version = -1; + this.mesh = null; + } - if (temp_tex) { - temp_tex.toCanvas(tex_canvas); - } - return tex_canvas; - }; + LGraphGeometryToMesh.title = "Geo to Mesh"; + + LGraphGeometryToMesh.prototype.updateMesh = function(geometry) { + if(!this.mesh) + this.mesh = new GL.Mesh(); + + for(var i in geometry) { + if(i[0] == "_") + continue; + + var buffer_data = geometry[i]; + + var info = GL.Mesh.common_buffers[i]; + if(!info && i != "indices") // unknown buffer + continue; + var spacing = info ? info.spacing : 3; + var mesh_buffer = this.mesh.vertexBuffers[i]; + + if(!mesh_buffer || mesh_buffer.data.length != buffer_data.length) { + mesh_buffer = new GL.Buffer( i == "indices" ? GL.ELEMENT_ARRAY_BUFFER : GL.ARRAY_BUFFER, buffer_data, spacing, GL.DYNAMIC_DRAW ); + } else { + mesh_buffer.data.set( buffer_data ); + mesh_buffer.upload(GL.DYNAMIC_DRAW); + } + + this.mesh.addBuffer( i, mesh_buffer ); + } + + if(this.mesh.vertexBuffers.normals &&this.mesh.vertexBuffers.normals.data.length != this.mesh.vertexBuffers.vertices.data.length ) { + var n = new Float32Array([0,1,0]); + var normals = new Float32Array( this.mesh.vertexBuffers.vertices.data.length ); + for(var i = 0; i < normals.length; i+= 3) + normals.set( n, i ); + mesh_buffer = new GL.Buffer( GL.ARRAY_BUFFER, normals, 3 ); + this.mesh.addBuffer( "normals", mesh_buffer ); + } + + this.mesh.updateBoundingBox(); + this.geometry_id = this.mesh.id = geometry._id; + this.version = this.mesh.version = geometry._version; + return this.mesh; + } + + LGraphGeometryToMesh.prototype.onExecute = function() { + + var geometry = this.getInputData(0); + if(!geometry) + return; + if( this.version != geometry._version || this.geometry_id != geometry._id ) + this.updateMesh( geometry ); + this.setOutputData(0, this.mesh); + } + + LiteGraph.registerNodeType( "geometry/toMesh", LGraphGeometryToMesh ); + + function LGraphRenderMesh() { + this.addInput("mesh", "mesh"); + this.addInput("mat4", "mat4"); + this.addInput("tex", "texture"); - LGraphTexture.prototype.getResources = function(res) { - if(this.properties.name) - res[this.properties.name] = GL.Texture; - return res; - }; + this.properties = { + enabled: true, + primitive: GL.TRIANGLES, + additive: false, + color: [1,1,1], + opacity: 1, + }; - LGraphTexture.prototype.onGetInputs = function() { - return [["in", "Texture"]]; - }; + this.color = vec4.create([1,1,1,1]); + this.model_matrix = mat4.create(); + this.uniforms = { + u_color: this.color, + u_model: this.model_matrix, + }; + } - LGraphTexture.prototype.onGetOutputs = function() { - return [ - ["width", "number"], - ["height", "number"], - ["aspect", "number"] - ]; - }; + LGraphRenderMesh.title = "Render Mesh"; + LGraphRenderMesh.desc = "renders a mesh flat"; - //used to replace shader code - LGraphTexture.replaceCode = function( code, context ) - { - return code.replace(/\{\{[a-zA-Z0-9_]*\}\}/g, function(v){ - v = v.replace( /[\{\}]/g, "" ); - return context[v] || ""; - }); - } + LGraphRenderMesh.PRIMITIVE_VALUES = { "points": GL.POINTS, "lines": GL.LINES, "line_loop": GL.LINE_LOOP,"line_strip": GL.LINE_STRIP, "triangles": GL.TRIANGLES, "triangle_fan": GL.TRIANGLE_FAN, "triangle_strip": GL.TRIANGLE_STRIP }; - LiteGraph.registerNodeType("texture/texture", LGraphTexture); + LGraphRenderMesh.widgets_info = { + primitive: { widget: "combo", values: LGraphRenderMesh.PRIMITIVE_VALUES }, + color: { widget: "color" }, + }; - //************************** - function LGraphTexturePreview() { - this.addInput("Texture", "Texture"); - this.properties = { flipY: false }; - this.size = [ - LGraphTexture.image_preview_size, - LGraphTexture.image_preview_size - ]; - } + LGraphRenderMesh.prototype.onExecute = function() { - LGraphTexturePreview.title = "Preview"; - LGraphTexturePreview.desc = "Show a texture in the graph canvas"; - LGraphTexturePreview.allow_preview = false; + if(!this.properties.enabled) + return; - LGraphTexturePreview.prototype.onDrawBackground = function(ctx) { - if (this.flags.collapsed) { - return; - } + var mesh = this.getInputData(0); + if(!mesh) + return; - if (!ctx.webgl && !LGraphTexturePreview.allow_preview) { - return; - } //not working well + if(!LiteGraph.LGraphRender.onRequestCameraMatrices) { + console.warn("cannot render geometry, LiteGraph.onRequestCameraMatrices is null, remember to fill this with a callback(view_matrix, projection_matrix,viewprojection_matrix) to use 3D rendering from the graph"); + return; + } - var tex = this.getInputData(0); - if (!tex) { - return; - } + LiteGraph.LGraphRender.onRequestCameraMatrices( view_matrix, projection_matrix,viewprojection_matrix ); + var shader = null; + var texture = this.getInputData(2); + if(texture) { + shader = gl.shaders["textured"]; + if(!shader) + shader = gl.shaders["textured"] = new GL.Shader( LGraphRenderPoints.vertex_shader_code, LGraphRenderPoints.fragment_shader_code, { USE_TEXTURE: "" }); + } else { + shader = gl.shaders["flat"]; + if(!shader) + shader = gl.shaders["flat"] = new GL.Shader( LGraphRenderPoints.vertex_shader_code, LGraphRenderPoints.fragment_shader_code ); + } - var tex_canvas = null; + this.color.set( this.properties.color ); + this.color[3] = this.properties.opacity; - if (!tex.handle && ctx.webgl) { - tex_canvas = tex; - } else { - tex_canvas = LGraphTexture.generateLowResTexturePreview(tex); - } + var model_matrix = this.model_matrix; + var m = this.getInputData(1); + if(m) + model_matrix.set(m); + else + mat4.identity( model_matrix ); - //render to graph canvas - ctx.save(); - if (this.properties.flipY) { - ctx.translate(0, this.size[1]); - ctx.scale(1, -1); - } - ctx.drawImage(tex_canvas, 0, 0, this.size[0], this.size[1]); - ctx.restore(); - }; + this.uniforms.u_point_size = 1; + var primitive = this.properties.primitive; - LiteGraph.registerNodeType("texture/preview", LGraphTexturePreview); + shader.uniforms( global_uniforms ); + shader.uniforms( this.uniforms ); - //************************************** + if(this.properties.opacity >= 1) + gl.disable( gl.BLEND ); + else + gl.enable( gl.BLEND ); + gl.enable( gl.DEPTH_TEST ); + if( this.properties.additive ) { + gl.blendFunc( gl.SRC_ALPHA, gl.ONE ); + gl.depthMask( false ); + } else + gl.blendFunc( gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA ); + + var indices = "indices"; + if( mesh.indexBuffers.triangles ) + indices = "triangles"; + shader.draw( mesh, primitive, indices ); + gl.disable( gl.BLEND ); + gl.depthMask( true ); + } - function LGraphTextureSave() { - this.addInput("Texture", "Texture"); - this.addOutput("tex", "Texture"); - this.addOutput("name", "string"); - this.properties = { name: "", generate_mipmaps: false }; - } + LiteGraph.registerNodeType( "geometry/render_mesh", LGraphRenderMesh ); - LGraphTextureSave.title = "Save"; - LGraphTextureSave.desc = "Save a texture in the repository"; + //* ************************* - LGraphTextureSave.prototype.getPreviewTexture = function() - { - return this._texture; - } - LGraphTextureSave.prototype.onExecute = function() { - var tex = this.getInputData(0); - if (!tex) { - return; - } + function LGraphGeometryPrimitive() { + this.addInput("size", "number"); + this.addOutput("out", "mesh"); + this.properties = { type: 1, size: 1, subdivisions: 32 }; - if (this.properties.generate_mipmaps) { - tex.bind(0); - tex.setParameter( gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR ); - gl.generateMipmap(tex.texture_type); - tex.unbind(0); - } + this.version = (Math.random() * 100000)|0; + this.last_info = { type: -1, size: -1, subdivisions: -1 }; + } - if (this.properties.name) { - //for cases where we want to perform something when storing it - if (LGraphTexture.storeTexture) { - LGraphTexture.storeTexture(this.properties.name, tex); - } else { - var container = LGraphTexture.getTexturesContainer(); - container[this.properties.name] = tex; - } - } + LGraphGeometryPrimitive.title = "Primitive"; - this._texture = tex; - this.setOutputData(0, tex); - this.setOutputData(1, this.properties.name); - }; + LGraphGeometryPrimitive.VALID = { "CUBE": 1, "PLANE": 2, "CYLINDER": 3, "SPHERE": 4, "CIRCLE": 5, "HEMISPHERE": 6, "ICOSAHEDRON": 7, "CONE": 8, "QUAD": 9 }; + LGraphGeometryPrimitive.widgets_info = {type: { widget: "combo", values: LGraphGeometryPrimitive.VALID }}; - LiteGraph.registerNodeType("texture/save", LGraphTextureSave); + LGraphGeometryPrimitive.prototype.onExecute = function() { - //**************************************************** + if( !this.isOutputConnected(0) ) + return; - function LGraphTextureOperation() { - this.addInput("Texture", "Texture"); - this.addInput("TextureB", "Texture"); - this.addInput("value", "number"); - this.addOutput("Texture", "Texture"); - this.help = "

pixelcode must be vec3, uvcode must be vec2, is optional

\ -

uv: tex. coords

color: texture colorB: textureB

time: scene time value: input value

For multiline you must type: result = ...

"; + var size = this.getInputOrProperty("size"); - this.properties = { - value: 1, - pixelcode: "color + colorB * value", - uvcode: "", - precision: LGraphTexture.DEFAULT - }; + // update + if( this.last_info.type != this.properties.type || this.last_info.size != size || this.last_info.subdivisions != this.properties.subdivisions ) + this.updateMesh( this.properties.type, size, this.properties.subdivisions ); - this.has_error = false; - } + this.setOutputData(0,this._mesh); + } - LGraphTextureOperation.widgets_info = { - uvcode: { widget: "code" }, - pixelcode: { widget: "code" }, - precision: { widget: "combo", values: LGraphTexture.MODE_VALUES } - }; + LGraphGeometryPrimitive.prototype.updateMesh = function(type, size, subdivisions) { + subdivisions = Math.max(0,subdivisions)|0; - LGraphTextureOperation.title = "Operation"; - LGraphTextureOperation.desc = "Texture shader operation"; + switch (type) { + case 1: // CUBE: + this._mesh = GL.Mesh.cube({size: size, normals: true,coords: true}); + break; + case 2: // PLANE: + this._mesh = GL.Mesh.plane({size: size, xz: true, detail: subdivisions, normals: true,coords: true}); + break; + case 3: // CYLINDER: + this._mesh = GL.Mesh.cylinder({size: size, subdivisions: subdivisions, normals: true,coords: true}); + break; + case 4: // SPHERE: + this._mesh = GL.Mesh.sphere({size: size, "long": subdivisions, lat: subdivisions, normals: true,coords: true}); + break; + case 5: // CIRCLE: + this._mesh = GL.Mesh.circle({size: size, slices: subdivisions, normals: true, coords: true}); + break; + case 6: // HEMISPHERE: + this._mesh = GL.Mesh.sphere({size: size, "long": subdivisions, lat: subdivisions, normals: true, coords: true, hemi: true}); + break; + case 7: // ICOSAHEDRON: + this._mesh = GL.Mesh.icosahedron({size: size, subdivisions: subdivisions }); + break; + case 8: // CONE: + this._mesh = GL.Mesh.cone({radius: size, height: size, subdivisions: subdivisions }); + break; + case 9: // QUAD: + this._mesh = GL.Mesh.plane({size: size, xz: false, detail: subdivisions, normals: true, coords: true }); + break; + } - LGraphTextureOperation.presets = {}; + this.last_info.type = type; + this.last_info.size = size; + this.last_info.subdivisions = subdivisions; + this._mesh.version = this.version++; + } - LGraphTextureOperation.prototype.getExtraMenuOptions = function( - graphcanvas - ) { - var that = this; - var txt = !that.properties.show ? "Show Texture" : "Hide Texture"; - return [ - { - content: txt, - callback: function() { - that.properties.show = !that.properties.show; - } - } - ]; - }; + LiteGraph.registerNodeType( "geometry/mesh_primitive", LGraphGeometryPrimitive ); - LGraphTextureOperation.prototype.onPropertyChanged = function() - { - this.has_error = false; - } - LGraphTextureOperation.prototype.onDrawBackground = function(ctx) { - if ( - this.flags.collapsed || - this.size[1] <= 20 || - !this.properties.show - ) { - return; - } + function LGraphRenderPoints() { + this.addInput("in", "geometry"); + this.addInput("mat4", "mat4"); + this.addInput("tex", "texture"); + this.properties = { + enabled: true, + point_size: 0.1, + fixed_size: false, + additive: true, + color: [1,1,1], + opacity: 1, + }; - if (!this._tex) { - return; - } + this.color = vec4.create([1,1,1,1]); - //only works if using a webgl renderer - if (this._tex.gl != ctx) { - return; - } + this.uniforms = { + u_point_size: 1, + u_perspective: 1, + u_point_perspective: 1, + u_color: this.color, + }; - //render to graph canvas - ctx.save(); - ctx.drawImage(this._tex, 0, 0, this.size[0], this.size[1]); - ctx.restore(); - }; + this.geometry_id = -1; + this.version = -1; + this.mesh = null; + } - LGraphTextureOperation.prototype.onExecute = function() { - var tex = this.getInputData(0); + LGraphRenderPoints.title = "renderPoints"; + LGraphRenderPoints.desc = "render points with a texture"; - if (!this.isOutputConnected(0)) { - return; - } //saves work + LGraphRenderPoints.widgets_info = {color: { widget: "color" }}; - if (this.properties.precision === LGraphTexture.PASS_THROUGH) { - this.setOutputData(0, tex); - return; - } + LGraphRenderPoints.prototype.updateMesh = function(geometry) { + var buffer = this.buffer; + if(!this.buffer || !this.buffer.data || this.buffer.data.length != geometry.vertices.length) + this.buffer = new GL.Buffer( GL.ARRAY_BUFFER, geometry.vertices,3,GL.DYNAMIC_DRAW); + else { + this.buffer.data.set( geometry.vertices ); + this.buffer.upload(GL.DYNAMIC_DRAW); + } - var texB = this.getInputData(1); + if(!this.mesh) + this.mesh = new GL.Mesh(); - if (!this.properties.uvcode && !this.properties.pixelcode) { - return; - } + this.mesh.addBuffer("vertices",this.buffer); + this.geometry_id = this.mesh.id = geometry._id; + this.version = this.mesh.version = geometry._version; + } - var width = 512; - var height = 512; - if (tex) { - width = tex.width; - height = tex.height; - } else if (texB) { - width = texB.width; - height = texB.height; - } + LGraphRenderPoints.prototype.onExecute = function() { - if(!texB) - texB = GL.Texture.getWhiteTexture(); + if(!this.properties.enabled) + return; - var type = LGraphTexture.getTextureType( this.properties.precision, tex ); + var geometry = this.getInputData(0); + if(!geometry) + return; + if(this.version != geometry._version || this.geometry_id != geometry._id ) + this.updateMesh( geometry ); - if (!tex && !this._tex) { - this._tex = new GL.Texture(width, height, { type: type, format: gl.RGBA, filter: gl.LINEAR }); - } else { - this._tex = LGraphTexture.getTargetTexture( tex || this._tex, this._tex, this.properties.precision ); - } + if(!LiteGraph.LGraphRender.onRequestCameraMatrices) { + console.warn("cannot render geometry, LiteGraph.onRequestCameraMatrices is null, remember to fill this with a callback(view_matrix, projection_matrix,viewprojection_matrix) to use 3D rendering from the graph"); + return; + } - var uvcode = ""; - if (this.properties.uvcode) { - uvcode = "uv = " + this.properties.uvcode; - if (this.properties.uvcode.indexOf(";") != -1) { - //there are line breaks, means multiline code - uvcode = this.properties.uvcode; - } - } + LiteGraph.LGraphRender.onRequestCameraMatrices( view_matrix, projection_matrix,viewprojection_matrix ); + var shader = null; - var pixelcode = ""; - if (this.properties.pixelcode) { - pixelcode = "result = " + this.properties.pixelcode; - if (this.properties.pixelcode.indexOf(";") != -1) { - //there are line breaks, means multiline code - pixelcode = this.properties.pixelcode; - } - } + var texture = this.getInputData(2); - var shader = this._shader; + if(texture) { + shader = gl.shaders["textured_points"]; + if(!shader) + shader = gl.shaders["textured_points"] = new GL.Shader( LGraphRenderPoints.vertex_shader_code, LGraphRenderPoints.fragment_shader_code, { USE_TEXTURED_POINTS: "" }); + } else { + shader = gl.shaders["points"]; + if(!shader) + shader = gl.shaders["points"] = new GL.Shader( LGraphRenderPoints.vertex_shader_code, LGraphRenderPoints.fragment_shader_code, { USE_POINTS: "" }); + } - if ( !this.has_error && (!shader || this._shader_code != uvcode + "|" + pixelcode) ) { + this.color.set( this.properties.color ); + this.color[3] = this.properties.opacity; - var final_pixel_code = LGraphTexture.replaceCode( LGraphTextureOperation.pixel_shader, { UV_CODE:uvcode, PIXEL_CODE:pixelcode }); + var m = this.getInputData(1); + if(m) + model_matrix.set(m); + else + mat4.identity( model_matrix ); - try { - shader = new GL.Shader( Shader.SCREEN_VERTEX_SHADER, final_pixel_code ); - this.boxcolor = "#00FF00"; - } catch (err) { - //console.log("Error compiling shader: ", err, final_pixel_code ); - GL.Shader.dumpErrorToConsole(err,Shader.SCREEN_VERTEX_SHADER, final_pixel_code); - this.boxcolor = "#FF0000"; - this.has_error = true; - return; - } - this._shader = shader; - this._shader_code = uvcode + "|" + pixelcode; - } + this.uniforms.u_point_size = this.properties.point_size; + this.uniforms.u_point_perspective = this.properties.fixed_size ? 0 : 1; + this.uniforms.u_perspective = gl.viewport_data[3] * projection_matrix[5]; - if(!this._shader) - return; + shader.uniforms( global_uniforms ); + shader.uniforms( this.uniforms ); - var value = this.getInputData(2); - if (value != null) { - this.properties.value = value; - } else { - value = parseFloat(this.properties.value); - } + if(this.properties.opacity >= 1) + gl.disable( gl.BLEND ); + else + gl.enable( gl.BLEND ); - var time = this.graph.getTime(); + gl.enable( gl.DEPTH_TEST ); + if( this.properties.additive ) { + gl.blendFunc( gl.SRC_ALPHA, gl.ONE ); + gl.depthMask( false ); + } else + gl.blendFunc( gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA ); - this._tex.drawTo(function() { - gl.disable(gl.DEPTH_TEST); - gl.disable(gl.CULL_FACE); - gl.disable(gl.BLEND); - if (tex) { - tex.bind(0); - } - if (texB) { - texB.bind(1); - } - var mesh = Mesh.getScreenQuad(); - shader - .uniforms({ - u_texture: 0, - u_textureB: 1, - value: value, - texSize: [width, height,1/width,1/height], - time: time - }) - .draw(mesh); - }); + shader.draw( this.mesh, GL.POINTS ); - this.setOutputData(0, this._tex); - }; + gl.disable( gl.BLEND ); + gl.depthMask( true ); + } - LGraphTextureOperation.pixel_shader = - "precision highp float;\n\ - \n\ - uniform sampler2D u_texture;\n\ - uniform sampler2D u_textureB;\n\ + LiteGraph.registerNodeType( "geometry/render_points", LGraphRenderPoints ); + + LGraphRenderPoints.vertex_shader_code = '\ + precision mediump float;\n\ + attribute vec3 a_vertex;\n\ + varying vec3 v_vertex;\n\ + attribute vec3 a_normal;\n\ + varying vec3 v_normal;\n\ + #ifdef USE_COLOR\n\ + attribute vec4 a_color;\n\ + varying vec4 v_color;\n\ + #endif\n\ + attribute vec2 a_coord;\n\ varying vec2 v_coord;\n\ - uniform vec4 texSize;\n\ - uniform float time;\n\ - uniform float value;\n\ - \n\ - void main() {\n\ - vec2 uv = v_coord;\n\ - {{UV_CODE}};\n\ - vec4 color4 = texture2D(u_texture, uv);\n\ - vec3 color = color4.rgb;\n\ - vec4 color4B = texture2D(u_textureB, uv);\n\ - vec3 colorB = color4B.rgb;\n\ - vec3 result = color;\n\ - float alpha = 1.0;\n\ - {{PIXEL_CODE}};\n\ - gl_FragColor = vec4(result, alpha);\n\ + #ifdef USE_SIZE\n\ + attribute float a_extra;\n\ + #endif\n\ + #ifdef USE_INSTANCING\n\ + attribute mat4 u_model;\n\ + #else\n\ + uniform mat4 u_model;\n\ + #endif\n\ + uniform mat4 u_viewprojection;\n\ + uniform float u_point_size;\n\ + uniform float u_perspective;\n\ + uniform float u_point_perspective;\n\ + float computePointSize(float radius, float w)\n\ + {\n\ + if(radius < 0.0)\n\ + return -radius;\n\ + return u_perspective * radius / w;\n\ }\n\ - "; + void main() {\n\ + v_coord = a_coord;\n\ + #ifdef USE_COLOR\n\ + v_color = a_color;\n\ + #endif\n\ + v_vertex = ( u_model * vec4( a_vertex, 1.0 )).xyz;\n\ + v_normal = ( u_model * vec4( a_normal, 0.0 )).xyz;\n\ + gl_Position = u_viewprojection * vec4(v_vertex,1.0);\n\ + gl_PointSize = u_point_size;\n\ + #ifdef USE_SIZE\n\ + gl_PointSize = a_extra;\n\ + #endif\n\ + if(u_point_perspective != 0.0)\n\ + gl_PointSize = computePointSize( gl_PointSize, gl_Position.w );\n\ + }\ + '; - LGraphTextureOperation.registerPreset = function ( name, code ) - { - LGraphTextureOperation.presets[name] = code; - } + LGraphRenderPoints.fragment_shader_code = '\ + precision mediump float;\n\ + uniform vec4 u_color;\n\ + #ifdef USE_COLOR\n\ + varying vec4 v_color;\n\ + #endif\n\ + varying vec2 v_coord;\n\ + uniform sampler2D u_texture;\n\ + void main() {\n\ + vec4 color = u_color;\n\ + #ifdef USE_TEXTURED_POINTS\n\ + color *= texture2D(u_texture, gl_PointCoord.xy);\n\ + #else\n\ + #ifdef USE_TEXTURE\n\ + color *= texture2D(u_texture, v_coord);\n\ + if(color.a < 0.1)\n\ + discard;\n\ + #endif\n\ + #ifdef USE_POINTS\n\ + float dist = length( gl_PointCoord.xy - vec2(0.5) );\n\ + if( dist > 0.45 )\n\ + discard;\n\ + #endif\n\ + #endif\n\ + #ifdef USE_COLOR\n\ + color *= v_color;\n\ + #endif\n\ + gl_FragColor = color;\n\ + }\ + '; - LGraphTextureOperation.registerPreset("",""); - LGraphTextureOperation.registerPreset("bypass","color"); - LGraphTextureOperation.registerPreset("add","color + colorB * value"); - LGraphTextureOperation.registerPreset("substract","(color - colorB) * value"); - LGraphTextureOperation.registerPreset("mate","mix( color, colorB, color4B.a * value)"); - LGraphTextureOperation.registerPreset("invert","vec3(1.0) - color"); - LGraphTextureOperation.registerPreset("multiply","color * colorB * value"); - LGraphTextureOperation.registerPreset("divide","(color / colorB) / value"); - LGraphTextureOperation.registerPreset("difference","abs(color - colorB) * value"); - LGraphTextureOperation.registerPreset("max","max(color, colorB) * value"); - LGraphTextureOperation.registerPreset("min","min(color, colorB) * value"); - LGraphTextureOperation.registerPreset("displace","texture2D(u_texture, uv + (colorB.xy - vec2(0.5)) * value).xyz"); - LGraphTextureOperation.registerPreset("grayscale","vec3(color.x + color.y + color.z) * value / 3.0"); - LGraphTextureOperation.registerPreset("saturation","mix( vec3(color.x + color.y + color.z) / 3.0, color, value )"); - LGraphTextureOperation.registerPreset("normalmap","\n\ - float z0 = texture2D(u_texture, uv + vec2(-texSize.z, -texSize.w) ).x;\n\ - float z1 = texture2D(u_texture, uv + vec2(0.0, -texSize.w) ).x;\n\ - float z2 = texture2D(u_texture, uv + vec2(texSize.z, -texSize.w) ).x;\n\ - float z3 = texture2D(u_texture, uv + vec2(-texSize.z, 0.0) ).x;\n\ - float z4 = color.x;\n\ - float z5 = texture2D(u_texture, uv + vec2(texSize.z, 0.0) ).x;\n\ - float z6 = texture2D(u_texture, uv + vec2(-texSize.z, texSize.w) ).x;\n\ - float z7 = texture2D(u_texture, uv + vec2(0.0, texSize.w) ).x;\n\ - float z8 = texture2D(u_texture, uv + vec2(texSize.z, texSize.w) ).x;\n\ - vec3 normal = vec3( z2 + 2.0*z4 + z7 - z0 - 2.0*z3 - z5, z5 + 2.0*z6 + z7 -z0 - 2.0*z1 - z2, 1.0 );\n\ - normal.xy *= value;\n\ - result.xyz = normalize(normal) * 0.5 + vec3(0.5);\n\ - "); - LGraphTextureOperation.registerPreset("threshold","vec3(color.x > colorB.x * value ? 1.0 : 0.0,color.y > colorB.y * value ? 1.0 : 0.0,color.z > colorB.z * value ? 1.0 : 0.0)"); + // based on https://inconvergent.net/2019/depth-of-field/ + /* + function LGraphRenderGeometryDOF() { + this.addInput("in", "geometry"); + this.addInput("mat4", "mat4"); + this.addInput("tex", "texture"); + this.properties = { + enabled: true, + lines: true, + point_size: 0.1, + fixed_size: false, + additive: true, + color: [1,1,1], + opacity: 1 + }; - //webglstudio stuff... - LGraphTextureOperation.prototype.onInspect = function(widgets) - { - var that = this; - widgets.addCombo("Presets","",{ values: Object.keys(LGraphTextureOperation.presets), callback: function(v){ - var code = LGraphTextureOperation.presets[v]; - if(!code) - return; - that.setProperty("pixelcode",code); - that.title = v; - widgets.refresh(); - }}); + this.color = vec4.create([1,1,1,1]); + + this.uniforms = { + u_point_size: 1, + u_perspective: 1, + u_point_perspective: 1, + u_color: this.color + }; + + this.geometry_id = -1; + this.version = -1; + this.mesh = null; } - LiteGraph.registerNodeType("texture/operation", LGraphTextureOperation); + LGraphRenderGeometryDOF.widgets_info = { + color: { widget: "color" } + }; - //**************************************************** + LGraphRenderGeometryDOF.prototype.updateMesh = function(geometry) + { + var buffer = this.buffer; + if(!this.buffer || this.buffer.data.length != geometry.vertices.length) + this.buffer = new GL.Buffer( GL.ARRAY_BUFFER, geometry.vertices,3,GL.DYNAMIC_DRAW); + else + { + this.buffer.data.set( geometry.vertices ); + this.buffer.upload(GL.DYNAMIC_DRAW); + } - function LGraphTextureShader() { - this.addOutput("out", "Texture"); - this.properties = { - code: "", - u_value: 1, - u_color: [1,1,1,1], - width: 512, - height: 512, - precision: LGraphTexture.DEFAULT - }; + if(!this.mesh) + this.mesh = new GL.Mesh(); - this.properties.code = LGraphTextureShader.pixel_shader; - this._uniforms = { u_value: 1, u_color: vec4.create(), in_texture: 0, texSize: vec4.create(), time: 0 }; + this.mesh.addBuffer("vertices",this.buffer); + this.geometry_id = this.mesh.id = geometry._id; + this.version = this.mesh.version = geometry._version; } - LGraphTextureShader.title = "Shader"; - LGraphTextureShader.desc = "Texture shader"; - LGraphTextureShader.widgets_info = { - code: { type: "code", lang: "glsl" }, - precision: { widget: "combo", values: LGraphTexture.MODE_VALUES } - }; + LGraphRenderGeometryDOF.prototype.onExecute = function() { - LGraphTextureShader.prototype.onPropertyChanged = function( - name, - value - ) { - if (name != "code") { + if(!this.properties.enabled) return; - } - var shader = this.getShader(); - if (!shader) { + var geometry = this.getInputData(0); + if(!geometry) + return; + if(this.version != geometry._version || this.geometry_id != geometry._id ) + this.updateMesh( geometry ); + + if(!LiteGraph.LGraphRender.onRequestCameraMatrices) + { + console.warn("cannot render geometry, LiteGraph.onRequestCameraMatrices is null, remember to fill this with a callback(view_matrix, projection_matrix,viewprojection_matrix) to use 3D rendering from the graph"); return; } - //update connections - var uniforms = shader.uniformInfo; + LiteGraph.LGraphRender.onRequestCameraMatrices( view_matrix, projection_matrix,viewprojection_matrix ); + var shader = null; - //remove deprecated slots - if (this.inputs) { - var already = {}; - for (var i = 0; i < this.inputs.length; ++i) { - var info = this.getInputInfo(i); - if (!info) { - continue; - } + var texture = this.getInputData(2); - if (uniforms[info.name] && !already[info.name]) { - already[info.name] = true; - continue; - } - this.removeInput(i); - i--; - } + if(texture) + { + shader = gl.shaders["textured_points"]; + if(!shader) + shader = gl.shaders["textured_points"] = new GL.Shader( LGraphRenderGeometryDOF.vertex_shader_code, LGraphRenderGeometryDOF.fragment_shader_code, { USE_TEXTURED_POINTS:"" }); + } + else + { + shader = gl.shaders["points"]; + if(!shader) + shader = gl.shaders["points"] = new GL.Shader( LGraphRenderGeometryDOF.vertex_shader_code, LGraphRenderGeometryDOF.fragment_shader_code, { USE_POINTS: "" }); } - //update existing ones - for (var i in uniforms) { - var info = shader.uniformInfo[i]; - if (info.loc === null) { - continue; - } //is an attribute, not a uniform - if (i == "time") { - //default one - continue; - } + this.color.set( this.properties.color ); + this.color[3] = this.properties.opacity; - var type = "number"; - if (this._shader.samplers[i]) { - type = "texture"; - } else { - switch (info.size) { - case 1: - type = "number"; - break; - case 2: - type = "vec2"; - break; - case 3: - type = "vec3"; - break; - case 4: - type = "vec4"; - break; - case 9: - type = "mat3"; - break; - case 16: - type = "mat4"; - break; - default: - continue; - } - } + var m = this.getInputData(1); + if(m) + model_matrix.set(m); + else + mat4.identity( model_matrix ); - var slot = this.findInputSlot(i); - if (slot == -1) { - this.addInput(i, type); - continue; - } + this.uniforms.u_point_size = this.properties.point_size; + this.uniforms.u_point_perspective = this.properties.fixed_size ? 0 : 1; + this.uniforms.u_perspective = gl.viewport_data[3] * projection_matrix[5]; - var input_info = this.getInputInfo(slot); - if (!input_info) { - this.addInput(i, type); - } else { - if (input_info.type == type) { - continue; - } - this.removeInput(slot, type); - this.addInput(i, type); - } - } - }; + shader.uniforms( global_uniforms ); + shader.uniforms( this.uniforms ); - LGraphTextureShader.prototype.getShader = function() { - //replug - if (this._shader && this._shader_code == this.properties.code) { - return this._shader; - } + if(this.properties.opacity >= 1) + gl.disable( gl.BLEND ); + else + gl.enable( gl.BLEND ); - this._shader_code = this.properties.code; - this._shader = new GL.Shader( Shader.SCREEN_VERTEX_SHADER, this.properties.code ); - if (!this._shader) { - this.boxcolor = "red"; - return null; - } else { - this.boxcolor = "green"; + gl.enable( gl.DEPTH_TEST ); + if( this.properties.additive ) + { + gl.blendFunc( gl.SRC_ALPHA, gl.ONE ); + gl.depthMask( false ); } - return this._shader; - }; + else + gl.blendFunc( gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA ); - LGraphTextureShader.prototype.onExecute = function() { - if (!this.isOutputConnected(0)) { - return; - } //saves work + shader.draw( this.mesh, GL.POINTS ); - var shader = this.getShader(); - if (!shader) { - return; - } + gl.disable( gl.BLEND ); + gl.depthMask( true ); + } - var tex_slot = 0; - var in_tex = null; + LiteGraph.registerNodeType( "geometry/render_dof", LGraphRenderGeometryDOF ); + + LGraphRenderGeometryDOF.vertex_shader_code = '\ + precision mediump float;\n\ + attribute vec3 a_vertex;\n\ + varying vec3 v_vertex;\n\ + attribute vec3 a_normal;\n\ + varying vec3 v_normal;\n\ + #ifdef USE_COLOR\n\ + attribute vec4 a_color;\n\ + varying vec4 v_color;\n\ + #endif\n\ + attribute vec2 a_coord;\n\ + varying vec2 v_coord;\n\ + #ifdef USE_SIZE\n\ + attribute float a_extra;\n\ + #endif\n\ + #ifdef USE_INSTANCING\n\ + attribute mat4 u_model;\n\ + #else\n\ + uniform mat4 u_model;\n\ + #endif\n\ + uniform mat4 u_viewprojection;\n\ + uniform float u_point_size;\n\ + uniform float u_perspective;\n\ + uniform float u_point_perspective;\n\ + float computePointSize(float radius, float w)\n\ + {\n\ + if(radius < 0.0)\n\ + return -radius;\n\ + return u_perspective * radius / w;\n\ + }\n\ + void main() {\n\ + v_coord = a_coord;\n\ + #ifdef USE_COLOR\n\ + v_color = a_color;\n\ + #endif\n\ + v_vertex = ( u_model * vec4( a_vertex, 1.0 )).xyz;\n\ + v_normal = ( u_model * vec4( a_normal, 0.0 )).xyz;\n\ + gl_Position = u_viewprojection * vec4(v_vertex,1.0);\n\ + gl_PointSize = u_point_size;\n\ + #ifdef USE_SIZE\n\ + gl_PointSize = a_extra;\n\ + #endif\n\ + if(u_point_perspective != 0.0)\n\ + gl_PointSize = computePointSize( gl_PointSize, gl_Position.w );\n\ + }\ + '; - //set uniforms - if(this.inputs) - for (var i = 0; i < this.inputs.length; ++i) { - var info = this.getInputInfo(i); - var data = this.getInputData(i); - if (data == null) { - continue; - } + LGraphRenderGeometryDOF.fragment_shader_code = '\ + precision mediump float;\n\ + uniform vec4 u_color;\n\ + #ifdef USE_COLOR\n\ + varying vec4 v_color;\n\ + #endif\n\ + varying vec2 v_coord;\n\ + uniform sampler2D u_texture;\n\ + void main() {\n\ + vec4 color = u_color;\n\ + #ifdef USE_TEXTURED_POINTS\n\ + color *= texture2D(u_texture, gl_PointCoord.xy);\n\ + #else\n\ + #ifdef USE_TEXTURE\n\ + color *= texture2D(u_texture, v_coord);\n\ + if(color.a < 0.1)\n\ + discard;\n\ + #endif\n\ + #ifdef USE_POINTS\n\ + float dist = length( gl_PointCoord.xy - vec2(0.5) );\n\ + if( dist > 0.45 )\n\ + discard;\n\ + #endif\n\ + #endif\n\ + #ifdef USE_COLOR\n\ + color *= v_color;\n\ + #endif\n\ + gl_FragColor = color;\n\ + }\ + '; + */ + + + +})(this); + +(function(global) { + var LiteGraph = global.LiteGraph; + var LGraphTexture = global.LGraphTexture; + + // Works with Litegl.js to create WebGL nodes + if (typeof GL != "undefined") { + // Texture Lens ***************************************** + function LGraphFXLens() { + this.addInput("Texture", "Texture"); + this.addInput("Aberration", "number"); + this.addInput("Distortion", "number"); + this.addInput("Blur", "number"); + this.addOutput("Texture", "Texture"); + this.properties = { + aberration: 1.0, + distortion: 1.0, + blur: 1.0, + precision: LGraphTexture.DEFAULT, + }; + + if (!LGraphFXLens._shader) { + LGraphFXLens._shader = new GL.Shader( + GL.Shader.SCREEN_VERTEX_SHADER, + LGraphFXLens.pixel_shader, + ); + LGraphFXLens._texture = new GL.Texture(3, 1, { + format: gl.RGB, + wrap: gl.CLAMP_TO_EDGE, + magFilter: gl.LINEAR, + minFilter: gl.LINEAR, + pixel_data: [255, 0, 0, 0, 255, 0, 0, 0, 255], + }); + } + } + + LGraphFXLens.title = "Lens"; + LGraphFXLens.desc = "Camera Lens distortion"; + LGraphFXLens.widgets_info = {precision: { widget: "combo", values: LGraphTexture.MODE_VALUES }}; + + LGraphFXLens.prototype.onExecute = function() { + var tex = this.getInputData(0); + if (this.properties.precision === LGraphTexture.PASS_THROUGH) { + this.setOutputData(0, tex); + return; + } + + if (!tex) { + return; + } + + this._tex = LGraphTexture.getTargetTexture( + tex, + this._tex, + this.properties.precision, + ); + + var aberration = this.properties.aberration; + if (this.isInputConnected(1)) { + aberration = this.getInputData(1); + this.properties.aberration = aberration; + } - if (data.constructor === GL.Texture) { - data.bind(tex_slot); - if (!in_tex) { - in_tex = data; - } - data = tex_slot; - tex_slot++; - } - shader.setUniform(info.name, data); //data is tex_slot - } + var distortion = this.properties.distortion; + if (this.isInputConnected(2)) { + distortion = this.getInputData(2); + this.properties.distortion = distortion; + } - var uniforms = this._uniforms; - var type = LGraphTexture.getTextureType( this.properties.precision, in_tex ); + var blur = this.properties.blur; + if (this.isInputConnected(3)) { + blur = this.getInputData(3); + this.properties.blur = blur; + } - //render to texture - var w = this.properties.width | 0; - var h = this.properties.height | 0; - if (w == 0) { - w = in_tex ? in_tex.width : gl.canvas.width; - } - if (h == 0) { - h = in_tex ? in_tex.height : gl.canvas.height; - } - uniforms.texSize[0] = w; - uniforms.texSize[1] = h; - uniforms.texSize[2] = 1/w; - uniforms.texSize[3] = 1/h; - uniforms.time = this.graph.getTime(); - uniforms.u_value = this.properties.u_value; - uniforms.u_color.set( this.properties.u_color ); - - if ( !this._tex || this._tex.type != type || this._tex.width != w || this._tex.height != h ) { - this._tex = new GL.Texture(w, h, { type: type, format: gl.RGBA, filter: gl.LINEAR }); - } - var tex = this._tex; - tex.drawTo(function() { - shader.uniforms(uniforms).draw(GL.Mesh.getScreenQuad()); - }); + gl.disable(gl.BLEND); + gl.disable(gl.DEPTH_TEST); + var mesh = Mesh.getScreenQuad(); + var shader = LGraphFXLens._shader; + // var camera = LS.Renderer._current_camera; - this.setOutputData(0, this._tex); - }; + this._tex.drawTo(function() { + tex.bind(0); + shader + .uniforms({ + u_texture: 0, + u_aberration: aberration, + u_distortion: distortion, + u_blur: blur, + }) + .draw(mesh); + }); - LGraphTextureShader.pixel_shader = -"precision highp float;\n\ -\n\ -varying vec2 v_coord;\n\ -uniform float time; //time in seconds\n\ -uniform vec4 texSize; //tex resolution\n\ -uniform float u_value;\n\ -uniform vec4 u_color;\n\n\ -void main() {\n\ - vec2 uv = v_coord;\n\ - vec3 color = vec3(0.0);\n\ - //your code here\n\ - color.xy=uv;\n\n\ - gl_FragColor = vec4(color, 1.0);\n\ -}\n\ -"; + this.setOutputData(0, this._tex); + }; - LiteGraph.registerNodeType("texture/shader", LGraphTextureShader); + LGraphFXLens.pixel_shader = + "precision highp float;\n\ + precision highp float;\n\ + varying vec2 v_coord;\n\ + uniform sampler2D u_texture;\n\ + uniform vec2 u_camera_planes;\n\ + uniform float u_aberration;\n\ + uniform float u_distortion;\n\ + uniform float u_blur;\n\ + \n\ + void main() {\n\ + vec2 coord = v_coord;\n\ + float dist = distance(vec2(0.5), coord);\n\ + vec2 dist_coord = coord - vec2(0.5);\n\ + float percent = 1.0 + ((0.5 - dist) / 0.5) * u_distortion;\n\ + dist_coord *= percent;\n\ + coord = dist_coord + vec2(0.5);\n\ + vec4 color = texture2D(u_texture,coord, u_blur * dist);\n\ + color.r = texture2D(u_texture,vec2(0.5) + dist_coord * (1.0+0.01*u_aberration), u_blur * dist ).r;\n\ + color.b = texture2D(u_texture,vec2(0.5) + dist_coord * (1.0-0.01*u_aberration), u_blur * dist ).b;\n\ + gl_FragColor = color;\n\ + }\n\ + "; + /* + float normalized_tunable_sigmoid(float xs, float k)\n\ + {\n\ + xs = xs * 2.0 - 1.0;\n\ + float signx = sign(xs);\n\ + float absx = abs(xs);\n\ + return signx * ((-k - 1.0)*absx)/(2.0*(-2.0*k*absx+k-1.0)) + 0.5;\n\ + }\n\ + */ - // Texture Scale Offset + LiteGraph.registerNodeType("fx/lens", LGraphFXLens); + global.LGraphFXLens = LGraphFXLens; - function LGraphTextureScaleOffset() { - this.addInput("in", "Texture"); - this.addInput("scale", "vec2"); - this.addInput("offset", "vec2"); - this.addOutput("out", "Texture"); - this.properties = { - offset: vec2.fromValues(0, 0), - scale: vec2.fromValues(1, 1), - precision: LGraphTexture.DEFAULT - }; + /* not working yet + function LGraphDepthOfField() + { + this.addInput("Color","Texture"); + this.addInput("Linear Depth","Texture"); + this.addInput("Camera","camera"); + this.addOutput("Texture","Texture"); + this.properties = { high_precision: false }; } - LGraphTextureScaleOffset.widgets_info = { - precision: { widget: "combo", values: LGraphTexture.MODE_VALUES } - }; - - LGraphTextureScaleOffset.title = "Scale/Offset"; - LGraphTextureScaleOffset.desc = "Applies an scaling and offseting"; + LGraphDepthOfField.title = "Depth Of Field"; + LGraphDepthOfField.desc = "Applies a depth of field effect"; - LGraphTextureScaleOffset.prototype.onExecute = function() { + LGraphDepthOfField.prototype.onExecute = function() + { var tex = this.getInputData(0); + var depth = this.getInputData(1); + var camera = this.getInputData(2); - if (!this.isOutputConnected(0) || !tex) { - return; - } //saves work - - if (this.properties.precision === LGraphTexture.PASS_THROUGH) { + if(!tex || !depth || !camera) + { this.setOutputData(0, tex); return; } - var width = tex.width; - var height = tex.height; - var type = this.precision === LGraphTexture.LOW ? gl.UNSIGNED_BYTE : gl.HIGH_PRECISION_FORMAT; - if (this.precision === LGraphTexture.DEFAULT) { - type = tex.type; - } - - if ( - !this._tex || - this._tex.width != width || - this._tex.height != height || - this._tex.type != type - ) { - this._tex = new GL.Texture(width, height, { - type: type, - format: gl.RGBA, - filter: gl.LINEAR - }); - } + var precision = gl.UNSIGNED_BYTE; + if(this.properties.high_precision) + precision = gl.half_float_ext ? gl.HALF_FLOAT_OES : gl.FLOAT; + if(!this._temp_texture || this._temp_texture.type != precision || + this._temp_texture.width != tex.width || this._temp_texture.height != tex.height) + this._temp_texture = new GL.Texture( tex.width, tex.height, { type: precision, format: gl.RGBA, filter: gl.LINEAR }); - var shader = this._shader; + var shader = LGraphDepthOfField._shader = new GL.Shader( GL.Shader.SCREEN_VERTEX_SHADER, LGraphDepthOfField._pixel_shader ); - if (!shader) { - shader = new GL.Shader( - GL.Shader.SCREEN_VERTEX_SHADER, - LGraphTextureScaleOffset.pixel_shader - ); - } + var screen_mesh = Mesh.getScreenQuad(); - var scale = this.getInputData(1); - if (scale) { - this.properties.scale[0] = scale[0]; - this.properties.scale[1] = scale[1]; - } else { - scale = this.properties.scale; - } + gl.disable( gl.DEPTH_TEST ); + gl.disable( gl.BLEND ); - var offset = this.getInputData(2); - if (offset) { - this.properties.offset[0] = offset[0]; - this.properties.offset[1] = offset[1]; - } else { - offset = this.properties.offset; - } + var camera_position = camera.getEye(); + var focus_point = camera.getCenter(); + var distance = vec3.distance( camera_position, focus_point ); + var far = camera.far; + var focus_range = distance * 0.5; - this._tex.drawTo(function() { - gl.disable(gl.DEPTH_TEST); - gl.disable(gl.CULL_FACE); - gl.disable(gl.BLEND); + this._temp_texture.drawTo( function() { tex.bind(0); - var mesh = Mesh.getScreenQuad(); - shader - .uniforms({ - u_texture: 0, - u_scale: scale, - u_offset: offset - }) - .draw(mesh); + depth.bind(1); + shader.uniforms({u_texture:0, u_depth_texture:1, u_resolution: [1/tex.width, 1/tex.height], u_far: far, u_focus_point: distance, u_focus_scale: focus_range }).draw(screen_mesh); }); - this.setOutputData(0, this._tex); - }; + this.setOutputData(0, this._temp_texture); + } - LGraphTextureScaleOffset.pixel_shader = - "precision highp float;\n\ - \n\ - uniform sampler2D u_texture;\n\ - uniform sampler2D u_textureB;\n\ + //from http://tuxedolabs.blogspot.com.es/2018/05/bokeh-depth-of-field-in-single-pass.html + LGraphDepthOfField._pixel_shader = "\n\ + precision highp float;\n\ varying vec2 v_coord;\n\ - uniform vec2 u_scale;\n\ - uniform vec2 u_offset;\n\ + uniform sampler2D u_texture; //Image to be processed\n\ + uniform sampler2D u_depth_texture; //Linear depth, where 1.0 == far plane\n\ + uniform vec2 u_iresolution; //The size of a pixel: vec2(1.0/width, 1.0/height)\n\ + uniform float u_far; // Far plane\n\ + uniform float u_focus_point;\n\ + uniform float u_focus_scale;\n\ \n\ - void main() {\n\ - vec2 uv = v_coord;\n\ - uv = uv / u_scale - u_offset;\n\ - gl_FragColor = texture2D(u_texture, uv);\n\ + const float GOLDEN_ANGLE = 2.39996323;\n\ + const float MAX_BLUR_SIZE = 20.0;\n\ + const float RAD_SCALE = 0.5; // Smaller = nicer blur, larger = faster\n\ + \n\ + float getBlurSize(float depth, float focusPoint, float focusScale)\n\ + {\n\ + float coc = clamp((1.0 / focusPoint - 1.0 / depth)*focusScale, -1.0, 1.0);\n\ + return abs(coc) * MAX_BLUR_SIZE;\n\ + }\n\ + \n\ + vec3 depthOfField(vec2 texCoord, float focusPoint, float focusScale)\n\ + {\n\ + float centerDepth = texture2D(u_depth_texture, texCoord).r * u_far;\n\ + float centerSize = getBlurSize(centerDepth, focusPoint, focusScale);\n\ + vec3 color = texture2D(u_texture, v_coord).rgb;\n\ + float tot = 1.0;\n\ + \n\ + float radius = RAD_SCALE;\n\ + for (float ang = 0.0; ang < 100.0; ang += GOLDEN_ANGLE)\n\ + {\n\ + vec2 tc = texCoord + vec2(cos(ang), sin(ang)) * u_iresolution * radius;\n\ + \n\ + vec3 sampleColor = texture2D(u_texture, tc).rgb;\n\ + float sampleDepth = texture2D(u_depth_texture, tc).r * u_far;\n\ + float sampleSize = getBlurSize( sampleDepth, focusPoint, focusScale );\n\ + if (sampleDepth > centerDepth)\n\ + sampleSize = clamp(sampleSize, 0.0, centerSize*2.0);\n\ + \n\ + float m = smoothstep(radius-0.5, radius+0.5, sampleSize);\n\ + color += mix(color/tot, sampleColor, m);\n\ + tot += 1.0;\n\ + radius += RAD_SCALE/radius;\n\ + if(radius>=MAX_BLUR_SIZE)\n\ + return color / tot;\n\ + }\n\ + return color / tot;\n\ + }\n\ + void main()\n\ + {\n\ + gl_FragColor = vec4( depthOfField( v_coord, u_focus_point, u_focus_scale ), 1.0 );\n\ + //gl_FragColor = vec4( texture2D(u_depth_texture, v_coord).r );\n\ }\n\ "; - LiteGraph.registerNodeType( - "texture/scaleOffset", - LGraphTextureScaleOffset - ); - - // Warp (distort a texture) ************************* - - function LGraphTextureWarp() { - this.addInput("in", "Texture"); - this.addInput("warp", "Texture"); - this.addInput("factor", "number"); - this.addOutput("out", "Texture"); - this.properties = { - factor: 0.01, - scale: [1,1], - offset: [0,0], - precision: LGraphTexture.DEFAULT - }; + LiteGraph.registerNodeType("fx/DOF", LGraphDepthOfField ); + global.LGraphDepthOfField = LGraphDepthOfField; + */ - this._uniforms = { - u_texture: 0, - u_textureB: 1, - u_factor: 1, - u_scale: vec2.create(), - u_offset: vec2.create() - }; - } + //* ****************************************************** + + function LGraphFXBokeh() { + this.addInput("Texture", "Texture"); + this.addInput("Blurred", "Texture"); + this.addInput("Mask", "Texture"); + this.addInput("Threshold", "number"); + this.addOutput("Texture", "Texture"); + this.properties = { + shape: "", + size: 10, + alpha: 1.0, + threshold: 1.0, + high_precision: false, + }; + } - LGraphTextureWarp.widgets_info = { - precision: { widget: "combo", values: LGraphTexture.MODE_VALUES } - }; + LGraphFXBokeh.title = "Bokeh"; + LGraphFXBokeh.desc = "applies an Bokeh effect"; - LGraphTextureWarp.title = "Warp"; - LGraphTextureWarp.desc = "Texture warp operation"; + LGraphFXBokeh.widgets_info = { shape: { widget: "texture" } }; - LGraphTextureWarp.prototype.onExecute = function() { - var tex = this.getInputData(0); + LGraphFXBokeh.prototype.onExecute = function() { + var tex = this.getInputData(0); + var blurred_tex = this.getInputData(1); + var mask_tex = this.getInputData(2); + if (!tex || !mask_tex || !this.properties.shape) { + this.setOutputData(0, tex); + return; + } - if (!this.isOutputConnected(0)) { - return; - } //saves work + if (!blurred_tex) { + blurred_tex = tex; + } - if (this.properties.precision === LGraphTexture.PASS_THROUGH) { - this.setOutputData(0, tex); - return; - } + var shape_tex = LGraphTexture.getTexture(this.properties.shape); + if (!shape_tex) { + return; + } - var texB = this.getInputData(1); - - var width = 512; - var height = 512; - var type = gl.UNSIGNED_BYTE; - if (tex) { - width = tex.width; - height = tex.height; - type = tex.type; - } else if (texB) { - width = texB.width; - height = texB.height; - type = texB.type; - } + var threshold = this.properties.threshold; + if (this.isInputConnected(3)) { + threshold = this.getInputData(3); + this.properties.threshold = threshold; + } - if (!tex && !this._tex) { - this._tex = new GL.Texture(width, height, { - type: - this.precision === LGraphTexture.LOW - ? gl.UNSIGNED_BYTE - : gl.HIGH_PRECISION_FORMAT, - format: gl.RGBA, - filter: gl.LINEAR - }); - } else { - this._tex = LGraphTexture.getTargetTexture( - tex || this._tex, - this._tex, - this.properties.precision - ); - } + var precision = gl.UNSIGNED_BYTE; + if (this.properties.high_precision) { + precision = gl.half_float_ext ? gl.HALF_FLOAT_OES : gl.FLOAT; + } + if ( + !this._temp_texture || + this._temp_texture.type != precision || + this._temp_texture.width != tex.width || + this._temp_texture.height != tex.height + ) { + this._temp_texture = new GL.Texture(tex.width, tex.height, { + type: precision, + format: gl.RGBA, + filter: gl.LINEAR, + }); + } - var shader = this._shader; + // iterations + var size = this.properties.size; - if (!shader) { - shader = new GL.Shader( - GL.Shader.SCREEN_VERTEX_SHADER, - LGraphTextureWarp.pixel_shader - ); - } + var first_shader = LGraphFXBokeh._first_shader; + if (!first_shader) { + first_shader = LGraphFXBokeh._first_shader = new GL.Shader( + Shader.SCREEN_VERTEX_SHADER, + LGraphFXBokeh._first_pixel_shader, + ); + } - var factor = this.getInputData(2); - if (factor != null) { - this.properties.factor = factor; - } else { - factor = parseFloat(this.properties.factor); - } - var uniforms = this._uniforms; - uniforms.u_factor = factor; - uniforms.u_scale.set( this.properties.scale ); - uniforms.u_offset.set( this.properties.offset ); - - this._tex.drawTo(function() { - gl.disable(gl.DEPTH_TEST); - gl.disable(gl.CULL_FACE); - gl.disable(gl.BLEND); - if (tex) { - tex.bind(0); - } - if (texB) { - texB.bind(1); - } - var mesh = Mesh.getScreenQuad(); - shader - .uniforms( uniforms ) - .draw(mesh); - }); + var second_shader = LGraphFXBokeh._second_shader; + if (!second_shader) { + second_shader = LGraphFXBokeh._second_shader = new GL.Shader( + LGraphFXBokeh._second_vertex_shader, + LGraphFXBokeh._second_pixel_shader, + ); + } - this.setOutputData(0, this._tex); - }; + var points_mesh = this._points_mesh; + if ( + !points_mesh || + points_mesh._width != tex.width || + points_mesh._height != tex.height || + points_mesh._spacing != 2 + ) { + points_mesh = this.createPointsMesh(tex.width, tex.height, 2); + } + + var screen_mesh = Mesh.getScreenQuad(); + + var point_size = this.properties.size; + var min_light = this.properties.min_light; + var alpha = this.properties.alpha; + + gl.disable(gl.DEPTH_TEST); + gl.disable(gl.BLEND); + + this._temp_texture.drawTo(function() { + tex.bind(0); + blurred_tex.bind(1); + mask_tex.bind(2); + first_shader + .uniforms({ + u_texture: 0, + u_texture_blur: 1, + u_mask: 2, + u_texsize: [tex.width, tex.height], + }) + .draw(screen_mesh); + }); - LGraphTextureWarp.pixel_shader = - "precision highp float;\n\ - \n\ - uniform sampler2D u_texture;\n\ - uniform sampler2D u_textureB;\n\ - varying vec2 v_coord;\n\ - uniform float u_factor;\n\ - uniform vec2 u_scale;\n\ - uniform vec2 u_offset;\n\ - \n\ - void main() {\n\ - vec2 uv = v_coord;\n\ - uv += ( texture2D(u_textureB, uv).rg - vec2(0.5)) * u_factor * u_scale + u_offset;\n\ - gl_FragColor = texture2D(u_texture, uv);\n\ - }\n\ - "; + this._temp_texture.drawTo(function() { + // clear because we use blending + // gl.clearColor(0.0,0.0,0.0,1.0); + // gl.clear( gl.COLOR_BUFFER_BIT ); + gl.enable(gl.BLEND); + gl.blendFunc(gl.ONE, gl.ONE); + + tex.bind(0); + shape_tex.bind(3); + second_shader + .uniforms({ + u_texture: 0, + u_mask: 2, + u_shape: 3, + u_alpha: alpha, + u_threshold: threshold, + u_pointSize: point_size, + u_itexsize: [1.0 / tex.width, 1.0 / tex.height], + }) + .draw(points_mesh, gl.POINTS); + }); - LiteGraph.registerNodeType("texture/warp", LGraphTextureWarp); + this.setOutputData(0, this._temp_texture); + }; - //**************************************************** + LGraphFXBokeh.prototype.createPointsMesh = function( + width, + height, + spacing, + ) { + var nwidth = Math.round(width / spacing); + var nheight = Math.round(height / spacing); + + var vertices = new Float32Array(nwidth * nheight * 2); + + var ny = -1; + var dx = (2 / width) * spacing; + var dy = (2 / height) * spacing; + for (var y = 0; y < nheight; ++y) { + var nx = -1; + for (var x = 0; x < nwidth; ++x) { + var pos = y * nwidth * 2 + x * 2; + vertices[pos] = nx; + vertices[pos + 1] = ny; + nx += dx; + } + ny += dy; + } - // Texture to Viewport ***************************************** - function LGraphTextureToViewport() { - this.addInput("Texture", "Texture"); - this.properties = { - additive: false, - antialiasing: false, - filter: true, - disable_alpha: false, - gamma: 1.0, - viewport: [0,0,1,1] - }; - this.size[0] = 130; - } + this._points_mesh = GL.Mesh.load({ vertices2D: vertices }); + this._points_mesh._width = width; + this._points_mesh._height = height; + this._points_mesh._spacing = spacing; - LGraphTextureToViewport.title = "to Viewport"; - LGraphTextureToViewport.desc = "Texture to viewport"; + return this._points_mesh; + }; - LGraphTextureToViewport._prev_viewport = new Float32Array(4); + /* + LGraphTextureBokeh._pixel_shader = "precision highp float;\n\ + varying vec2 a_coord;\n\ + uniform sampler2D u_texture;\n\ + uniform sampler2D u_shape;\n\ + \n\ + void main() {\n\ + vec4 color = texture2D( u_texture, gl_PointCoord );\n\ + color *= v_color * u_alpha;\n\ + gl_FragColor = color;\n\ + }\n"; + */ - LGraphTextureToViewport.prototype.onDrawBackground = function( ctx ) - { - if ( this.flags.collapsed || this.size[1] <= 40 ) - return; + LGraphFXBokeh._first_pixel_shader = + "precision highp float;\n\ + precision highp float;\n\ + varying vec2 v_coord;\n\ + uniform sampler2D u_texture;\n\ + uniform sampler2D u_texture_blur;\n\ + uniform sampler2D u_mask;\n\ + \n\ + void main() {\n\ + vec4 color = texture2D(u_texture, v_coord);\n\ + vec4 blurred_color = texture2D(u_texture_blur, v_coord);\n\ + float mask = texture2D(u_mask, v_coord).x;\n\ + gl_FragColor = mix(color, blurred_color, mask);\n\ + }\n\ + "; + + LGraphFXBokeh._second_vertex_shader = + "precision highp float;\n\ + attribute vec2 a_vertex2D;\n\ + varying vec4 v_color;\n\ + uniform sampler2D u_texture;\n\ + uniform sampler2D u_mask;\n\ + uniform vec2 u_itexsize;\n\ + uniform float u_pointSize;\n\ + uniform float u_threshold;\n\ + void main() {\n\ + vec2 coord = a_vertex2D * 0.5 + 0.5;\n\ + v_color = texture2D( u_texture, coord );\n\ + v_color += texture2D( u_texture, coord + vec2(u_itexsize.x, 0.0) );\n\ + v_color += texture2D( u_texture, coord + vec2(0.0, u_itexsize.y));\n\ + v_color += texture2D( u_texture, coord + u_itexsize);\n\ + v_color *= 0.25;\n\ + float mask = texture2D(u_mask, coord).x;\n\ + float luminance = length(v_color) * mask;\n\ + /*luminance /= (u_pointSize*u_pointSize)*0.01 */;\n\ + luminance -= u_threshold;\n\ + if(luminance < 0.0)\n\ + {\n\ + gl_Position.x = -100.0;\n\ + return;\n\ + }\n\ + gl_PointSize = u_pointSize;\n\ + gl_Position = vec4(a_vertex2D,0.0,1.0);\n\ + }\n\ + "; - var tex = this.getInputData(0); - if (!tex) { - return; - } + LGraphFXBokeh._second_pixel_shader = + "precision highp float;\n\ + varying vec4 v_color;\n\ + uniform sampler2D u_shape;\n\ + uniform float u_alpha;\n\ + \n\ + void main() {\n\ + vec4 color = texture2D( u_shape, gl_PointCoord );\n\ + color *= v_color * u_alpha;\n\ + gl_FragColor = color;\n\ + }\n"; + + LiteGraph.registerNodeType("fx/bokeh", LGraphFXBokeh); + global.LGraphFXBokeh = LGraphFXBokeh; + + //* *********************************************** + + function LGraphFXGeneric() { + this.addInput("Texture", "Texture"); + this.addInput("value1", "number"); + this.addInput("value2", "number"); + this.addOutput("Texture", "Texture"); + this.properties = { + fx: "halftone", + value1: 1, + value2: 1, + precision: LGraphTexture.DEFAULT, + }; + } - ctx.drawImage( ctx == gl ? tex : gl.canvas, 10,30, this.size[0] -20, this.size[1] -40); - } + LGraphFXGeneric.title = "FX"; + LGraphFXGeneric.desc = "applies an FX from a list"; - LGraphTextureToViewport.prototype.onExecute = function() { - var tex = this.getInputData(0); - if (!tex) { - return; - } + LGraphFXGeneric.widgets_info = { + fx: { + widget: "combo", + values: ["halftone", "pixelate", "lowpalette", "noise", "gamma"], + }, + precision: { widget: "combo", values: LGraphTexture.MODE_VALUES }, + }; + LGraphFXGeneric.shaders = {}; - if (this.properties.disable_alpha) { - gl.disable(gl.BLEND); - } else { - gl.enable(gl.BLEND); - if (this.properties.additive) { - gl.blendFunc(gl.SRC_ALPHA, gl.ONE); - } else { - gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA); - } - } + LGraphFXGeneric.prototype.onExecute = function() { + if (!this.isOutputConnected(0)) { + return; + } // saves work - gl.disable(gl.DEPTH_TEST); - var gamma = this.properties.gamma || 1.0; - if (this.isInputConnected(1)) { - gamma = this.getInputData(1); - } + var tex = this.getInputData(0); + if (this.properties.precision === LGraphTexture.PASS_THROUGH) { + this.setOutputData(0, tex); + return; + } - tex.setParameter( - gl.TEXTURE_MAG_FILTER, - this.properties.filter ? gl.LINEAR : gl.NEAREST - ); - - var old_viewport = LGraphTextureToViewport._prev_viewport; - old_viewport.set( gl.viewport_data ); - var new_view = this.properties.viewport; - gl.viewport( old_viewport[0] + old_viewport[2] * new_view[0], old_viewport[1] + old_viewport[3] * new_view[1], old_viewport[2] * new_view[2], old_viewport[3] * new_view[3] ); - var viewport = gl.getViewport(); //gl.getParameter(gl.VIEWPORT); - - if (this.properties.antialiasing) { - if (!LGraphTextureToViewport._shader) { - LGraphTextureToViewport._shader = new GL.Shader( - GL.Shader.SCREEN_VERTEX_SHADER, - LGraphTextureToViewport.aa_pixel_shader - ); - } + if (!tex) { + return; + } - var mesh = Mesh.getScreenQuad(); - tex.bind(0); - LGraphTextureToViewport._shader - .uniforms({ - u_texture: 0, - uViewportSize: [tex.width, tex.height], - u_igamma: 1 / gamma, - inverseVP: [1 / tex.width, 1 / tex.height] - }) - .draw(mesh); - } else { - if (gamma != 1.0) { - if (!LGraphTextureToViewport._gamma_shader) { - LGraphTextureToViewport._gamma_shader = new GL.Shader( - Shader.SCREEN_VERTEX_SHADER, - LGraphTextureToViewport.gamma_pixel_shader - ); - } - tex.toViewport(LGraphTextureToViewport._gamma_shader, { - u_texture: 0, - u_igamma: 1 / gamma - }); - } else { - tex.toViewport(); - } - } + this._tex = LGraphTexture.getTargetTexture( + tex, + this._tex, + this.properties.precision, + ); - gl.viewport( old_viewport[0], old_viewport[1], old_viewport[2], old_viewport[3] ); - }; + // iterations + var value1 = this.properties.value1; + if (this.isInputConnected(1)) { + value1 = this.getInputData(1); + this.properties.value1 = value1; + } - LGraphTextureToViewport.prototype.onGetInputs = function() { - return [["gamma", "number"]]; - }; + var value2 = this.properties.value2; + if (this.isInputConnected(2)) { + value2 = this.getInputData(2); + this.properties.value2 = value2; + } - LGraphTextureToViewport.aa_pixel_shader = - "precision highp float;\n\ - precision highp float;\n\ - varying vec2 v_coord;\n\ - uniform sampler2D u_texture;\n\ - uniform vec2 uViewportSize;\n\ - uniform vec2 inverseVP;\n\ - uniform float u_igamma;\n\ - #define FXAA_REDUCE_MIN (1.0/ 128.0)\n\ - #define FXAA_REDUCE_MUL (1.0 / 8.0)\n\ - #define FXAA_SPAN_MAX 8.0\n\ - \n\ - /* from mitsuhiko/webgl-meincraft based on the code on geeks3d.com */\n\ - vec4 applyFXAA(sampler2D tex, vec2 fragCoord)\n\ - {\n\ - vec4 color = vec4(0.0);\n\ - /*vec2 inverseVP = vec2(1.0 / uViewportSize.x, 1.0 / uViewportSize.y);*/\n\ - vec3 rgbNW = texture2D(tex, (fragCoord + vec2(-1.0, -1.0)) * inverseVP).xyz;\n\ - vec3 rgbNE = texture2D(tex, (fragCoord + vec2(1.0, -1.0)) * inverseVP).xyz;\n\ - vec3 rgbSW = texture2D(tex, (fragCoord + vec2(-1.0, 1.0)) * inverseVP).xyz;\n\ - vec3 rgbSE = texture2D(tex, (fragCoord + vec2(1.0, 1.0)) * inverseVP).xyz;\n\ - vec3 rgbM = texture2D(tex, fragCoord * inverseVP).xyz;\n\ - vec3 luma = vec3(0.299, 0.587, 0.114);\n\ - float lumaNW = dot(rgbNW, luma);\n\ - float lumaNE = dot(rgbNE, luma);\n\ - float lumaSW = dot(rgbSW, luma);\n\ - float lumaSE = dot(rgbSE, luma);\n\ - float lumaM = dot(rgbM, luma);\n\ - float lumaMin = min(lumaM, min(min(lumaNW, lumaNE), min(lumaSW, lumaSE)));\n\ - float lumaMax = max(lumaM, max(max(lumaNW, lumaNE), max(lumaSW, lumaSE)));\n\ - \n\ - vec2 dir;\n\ - dir.x = -((lumaNW + lumaNE) - (lumaSW + lumaSE));\n\ - dir.y = ((lumaNW + lumaSW) - (lumaNE + lumaSE));\n\ - \n\ - float dirReduce = max((lumaNW + lumaNE + lumaSW + lumaSE) * (0.25 * FXAA_REDUCE_MUL), FXAA_REDUCE_MIN);\n\ - \n\ - float rcpDirMin = 1.0 / (min(abs(dir.x), abs(dir.y)) + dirReduce);\n\ - dir = min(vec2(FXAA_SPAN_MAX, FXAA_SPAN_MAX), max(vec2(-FXAA_SPAN_MAX, -FXAA_SPAN_MAX), dir * rcpDirMin)) * inverseVP;\n\ - \n\ - vec3 rgbA = 0.5 * (texture2D(tex, fragCoord * inverseVP + dir * (1.0 / 3.0 - 0.5)).xyz + \n\ - texture2D(tex, fragCoord * inverseVP + dir * (2.0 / 3.0 - 0.5)).xyz);\n\ - vec3 rgbB = rgbA * 0.5 + 0.25 * (texture2D(tex, fragCoord * inverseVP + dir * -0.5).xyz + \n\ - texture2D(tex, fragCoord * inverseVP + dir * 0.5).xyz);\n\ - \n\ - //return vec4(rgbA,1.0);\n\ - float lumaB = dot(rgbB, luma);\n\ - if ((lumaB < lumaMin) || (lumaB > lumaMax))\n\ - color = vec4(rgbA, 1.0);\n\ - else\n\ - color = vec4(rgbB, 1.0);\n\ - if(u_igamma != 1.0)\n\ - color.xyz = pow( color.xyz, vec3(u_igamma) );\n\ - return color;\n\ - }\n\ - \n\ - void main() {\n\ - gl_FragColor = applyFXAA( u_texture, v_coord * uViewportSize) ;\n\ - }\n\ - "; + var fx = this.properties.fx; + var shader = LGraphFXGeneric.shaders[fx]; + if (!shader) { + var pixel_shader_code = LGraphFXGeneric["pixel_shader_" + fx]; + if (!pixel_shader_code) { + return; + } - LGraphTextureToViewport.gamma_pixel_shader = - "precision highp float;\n\ - precision highp float;\n\ - varying vec2 v_coord;\n\ - uniform sampler2D u_texture;\n\ - uniform float u_igamma;\n\ - void main() {\n\ - vec4 color = texture2D( u_texture, v_coord);\n\ - color.xyz = pow(color.xyz, vec3(u_igamma) );\n\ - gl_FragColor = color;\n\ - }\n\ - "; + shader = LGraphFXGeneric.shaders[fx] = new GL.Shader( + Shader.SCREEN_VERTEX_SHADER, + pixel_shader_code, + ); + } - LiteGraph.registerNodeType( - "texture/toviewport", - LGraphTextureToViewport - ); + gl.disable(gl.BLEND); + gl.disable(gl.DEPTH_TEST); + var mesh = Mesh.getScreenQuad(); + var camera = global.LS ? LS.Renderer._current_camera : null; + var camera_planes; + if (camera) { + camera_planes = [ + LS.Renderer._current_camera.near, + LS.Renderer._current_camera.far, + ]; + } else { + camera_planes = [1, 100]; + } - // Texture Copy ***************************************** - function LGraphTextureCopy() { - this.addInput("Texture", "Texture"); - this.addOutput("", "Texture"); - this.properties = { - size: 0, - generate_mipmaps: false, - precision: LGraphTexture.DEFAULT - }; - } + var noise = null; + if (fx == "noise") { + noise = LGraphTexture.getNoiseTexture(); + } - LGraphTextureCopy.title = "Copy"; - LGraphTextureCopy.desc = "Copy Texture"; - LGraphTextureCopy.widgets_info = { - size: { - widget: "combo", - values: [0, 32, 64, 128, 256, 512, 1024, 2048] - }, - precision: { widget: "combo", values: LGraphTexture.MODE_VALUES } - }; + this._tex.drawTo(function() { + tex.bind(0); + if (fx == "noise") { + noise.bind(1); + } - LGraphTextureCopy.prototype.onExecute = function() { - var tex = this.getInputData(0); - if (!tex && !this._temp_texture) { - return; - } + shader + .uniforms({ + u_texture: 0, + u_noise: 1, + u_size: [tex.width, tex.height], + u_rand: [Math.random(), Math.random()], + u_value1: value1, + u_value2: value2, + u_camera_planes: camera_planes, + }) + .draw(mesh); + }); - if (!this.isOutputConnected(0)) { - return; - } //saves work + this.setOutputData(0, this._tex); + }; - //copy the texture - if (tex) { - var width = tex.width; - var height = tex.height; + LGraphFXGeneric.pixel_shader_halftone = + "precision highp float;\n\ + varying vec2 v_coord;\n\ + uniform sampler2D u_texture;\n\ + uniform vec2 u_camera_planes;\n\ + uniform vec2 u_size;\n\ + uniform float u_value1;\n\ + uniform float u_value2;\n\ + \n\ + float pattern() {\n\ + float s = sin(u_value1 * 3.1415), c = cos(u_value1 * 3.1415);\n\ + vec2 tex = v_coord * u_size.xy;\n\ + vec2 point = vec2(\n\ + c * tex.x - s * tex.y ,\n\ + s * tex.x + c * tex.y \n\ + ) * u_value2;\n\ + return (sin(point.x) * sin(point.y)) * 4.0;\n\ + }\n\ + void main() {\n\ + vec4 color = texture2D(u_texture, v_coord);\n\ + float average = (color.r + color.g + color.b) / 3.0;\n\ + gl_FragColor = vec4(vec3(average * 10.0 - 5.0 + pattern()), color.a);\n\ + }\n"; + + LGraphFXGeneric.pixel_shader_pixelate = + "precision highp float;\n\ + varying vec2 v_coord;\n\ + uniform sampler2D u_texture;\n\ + uniform vec2 u_camera_planes;\n\ + uniform vec2 u_size;\n\ + uniform float u_value1;\n\ + uniform float u_value2;\n\ + \n\ + void main() {\n\ + vec2 coord = vec2( floor(v_coord.x * u_value1) / u_value1, floor(v_coord.y * u_value2) / u_value2 );\n\ + vec4 color = texture2D(u_texture, coord);\n\ + gl_FragColor = color;\n\ + }\n"; + + LGraphFXGeneric.pixel_shader_lowpalette = + "precision highp float;\n\ + varying vec2 v_coord;\n\ + uniform sampler2D u_texture;\n\ + uniform vec2 u_camera_planes;\n\ + uniform vec2 u_size;\n\ + uniform float u_value1;\n\ + uniform float u_value2;\n\ + \n\ + void main() {\n\ + vec4 color = texture2D(u_texture, v_coord);\n\ + gl_FragColor = floor(color * u_value1) / u_value1;\n\ + }\n"; + + LGraphFXGeneric.pixel_shader_noise = + "precision highp float;\n\ + varying vec2 v_coord;\n\ + uniform sampler2D u_texture;\n\ + uniform sampler2D u_noise;\n\ + uniform vec2 u_size;\n\ + uniform float u_value1;\n\ + uniform float u_value2;\n\ + uniform vec2 u_rand;\n\ + \n\ + void main() {\n\ + vec4 color = texture2D(u_texture, v_coord);\n\ + vec3 noise = texture2D(u_noise, v_coord * vec2(u_size.x / 512.0, u_size.y / 512.0) + u_rand).xyz - vec3(0.5);\n\ + gl_FragColor = vec4( color.xyz + noise * u_value1, color.a );\n\ + }\n"; + + LGraphFXGeneric.pixel_shader_gamma = + "precision highp float;\n\ + varying vec2 v_coord;\n\ + uniform sampler2D u_texture;\n\ + uniform float u_value1;\n\ + \n\ + void main() {\n\ + vec4 color = texture2D(u_texture, v_coord);\n\ + float gamma = 1.0 / u_value1;\n\ + gl_FragColor = vec4( pow( color.xyz, vec3(gamma) ), color.a );\n\ + }\n"; + + LiteGraph.registerNodeType("fx/generic", LGraphFXGeneric); + global.LGraphFXGeneric = LGraphFXGeneric; + + // Vigneting ************************************ + + function LGraphFXVigneting() { + this.addInput("Tex.", "Texture"); + this.addInput("intensity", "number"); + + this.addOutput("Texture", "Texture"); + this.properties = { + intensity: 1, + invert: false, + precision: LGraphTexture.DEFAULT, + }; - if (this.properties.size != 0) { - width = this.properties.size; - height = this.properties.size; - } + if (!LGraphFXVigneting._shader) { + LGraphFXVigneting._shader = new GL.Shader( + Shader.SCREEN_VERTEX_SHADER, + LGraphFXVigneting.pixel_shader, + ); + } + } - var temp = this._temp_texture; + LGraphFXVigneting.title = "Vigneting"; + LGraphFXVigneting.desc = "Vigneting"; - var type = tex.type; - if (this.properties.precision === LGraphTexture.LOW) { - type = gl.UNSIGNED_BYTE; - } else if (this.properties.precision === LGraphTexture.HIGH) { - type = gl.HIGH_PRECISION_FORMAT; - } + LGraphFXVigneting.widgets_info = {precision: { widget: "combo", values: LGraphTexture.MODE_VALUES }}; - if ( - !temp || - temp.width != width || - temp.height != height || - temp.type != type - ) { - var minFilter = gl.LINEAR; - if ( - this.properties.generate_mipmaps && - isPowerOfTwo(width) && - isPowerOfTwo(height) - ) { - minFilter = gl.LINEAR_MIPMAP_LINEAR; - } - this._temp_texture = new GL.Texture(width, height, { - type: type, - format: gl.RGBA, - minFilter: minFilter, - magFilter: gl.LINEAR - }); - } - tex.copyTo(this._temp_texture); + LGraphFXVigneting.prototype.onExecute = function() { + var tex = this.getInputData(0); - if (this.properties.generate_mipmaps) { - this._temp_texture.bind(0); - gl.generateMipmap(this._temp_texture.texture_type); - this._temp_texture.unbind(0); - } - } + if (this.properties.precision === LGraphTexture.PASS_THROUGH) { + this.setOutputData(0, tex); + return; + } - this.setOutputData(0, this._temp_texture); - }; + if (!tex) { + return; + } - LiteGraph.registerNodeType("texture/copy", LGraphTextureCopy); + this._tex = LGraphTexture.getTargetTexture( + tex, + this._tex, + this.properties.precision, + ); - // Texture Downsample ***************************************** - function LGraphTextureDownsample() { - this.addInput("Texture", "Texture"); - this.addOutput("", "Texture"); - this.properties = { - iterations: 1, - generate_mipmaps: false, - precision: LGraphTexture.DEFAULT - }; - } + var intensity = this.properties.intensity; + if (this.isInputConnected(1)) { + intensity = this.getInputData(1); + this.properties.intensity = intensity; + } + + gl.disable(gl.BLEND); + gl.disable(gl.DEPTH_TEST); + + var mesh = Mesh.getScreenQuad(); + var shader = LGraphFXVigneting._shader; + var invert = this.properties.invert; + + this._tex.drawTo(function() { + tex.bind(0); + shader + .uniforms({ + u_texture: 0, + u_intensity: intensity, + u_isize: [1 / tex.width, 1 / tex.height], + u_invert: invert ? 1 : 0, + }) + .draw(mesh); + }); - LGraphTextureDownsample.title = "Downsample"; - LGraphTextureDownsample.desc = "Downsample Texture"; - LGraphTextureDownsample.widgets_info = { - iterations: { type: "number", step: 1, precision: 0, min: 0 }, - precision: { widget: "combo", values: LGraphTexture.MODE_VALUES } - }; + this.setOutputData(0, this._tex); + }; - LGraphTextureDownsample.prototype.onExecute = function() { - var tex = this.getInputData(0); - if (!tex && !this._temp_texture) { - return; - } + LGraphFXVigneting.pixel_shader = + "precision highp float;\n\ + precision highp float;\n\ + varying vec2 v_coord;\n\ + uniform sampler2D u_texture;\n\ + uniform float u_intensity;\n\ + uniform int u_invert;\n\ + \n\ + void main() {\n\ + float luminance = 1.0 - length( v_coord - vec2(0.5) ) * 1.414;\n\ + vec4 color = texture2D(u_texture, v_coord);\n\ + if(u_invert == 1)\n\ + luminance = 1.0 - luminance;\n\ + luminance = mix(1.0, luminance, u_intensity);\n\ + gl_FragColor = vec4( luminance * color.xyz, color.a);\n\ + }\n\ + "; - if (!this.isOutputConnected(0)) { - return; - } //saves work + LiteGraph.registerNodeType("fx/vigneting", LGraphFXVigneting); + global.LGraphFXVigneting = LGraphFXVigneting; + } +})(this); - //we do not allow any texture different than texture 2D - if (!tex || tex.texture_type !== GL.TEXTURE_2D) { - return; - } +(function(global) { + var LiteGraph = global.LiteGraph; + var MIDI_COLOR = "#243"; - if (this.properties.iterations < 1) { - this.setOutputData(0, tex); - return; - } + function MIDIEvent(data) { + this.channel = 0; + this.cmd = 0; + this.data = new Uint32Array(3); - var shader = LGraphTextureDownsample._shader; - if (!shader) { - LGraphTextureDownsample._shader = shader = new GL.Shader( - GL.Shader.SCREEN_VERTEX_SHADER, - LGraphTextureDownsample.pixel_shader - ); - } + if (data) { + this.setup(data); + } + } - var width = tex.width | 0; - var height = tex.height | 0; - var type = tex.type; - if (this.properties.precision === LGraphTexture.LOW) { - type = gl.UNSIGNED_BYTE; - } else if (this.properties.precision === LGraphTexture.HIGH) { - type = gl.HIGH_PRECISION_FORMAT; - } - var iterations = this.properties.iterations || 1; + LiteGraph.MIDIEvent = MIDIEvent; - var origin = tex; - var target = null; + MIDIEvent.prototype.fromJSON = function(o) { + this.setup(o.data); + }; - var temp = []; - var options = { - type: type, - format: tex.format - }; + MIDIEvent.prototype.setup = function(data) { + var raw_data = data; + if (data.constructor === Object) { + raw_data = data.data; + } - var offset = vec2.create(); - var uniforms = { - u_offset: offset - }; + this.data.set(raw_data); - if (this._texture) { - GL.Texture.releaseTemporary(this._texture); - } + var midiStatus = raw_data[0]; + this.status = midiStatus; - for (var i = 0; i < iterations; ++i) { - offset[0] = 1 / width; - offset[1] = 1 / height; - width = width >> 1 || 0; - height = height >> 1 || 0; - target = GL.Texture.getTemporary(width, height, options); - temp.push(target); - origin.setParameter(GL.TEXTURE_MAG_FILTER, GL.NEAREST); - origin.copyTo(target, shader, uniforms); - if (width == 1 && height == 1) { - break; - } //nothing else to do - origin = target; - } + var midiCommand = midiStatus & 0xf0; - //keep the last texture used - this._texture = temp.pop(); + if (midiStatus >= 0xf0) { + this.cmd = midiStatus; + } else { + this.cmd = midiCommand; + } - //free the rest - for (var i = 0; i < temp.length; ++i) { - GL.Texture.releaseTemporary(temp[i]); - } + if (this.cmd == MIDIEvent.NOTEON && this.velocity == 0) { + this.cmd = MIDIEvent.NOTEOFF; + } - if (this.properties.generate_mipmaps) { - this._texture.bind(0); - gl.generateMipmap(this._texture.texture_type); - this._texture.unbind(0); - } + this.cmd_str = MIDIEvent.commands[this.cmd] || ""; - this.setOutputData(0, this._texture); - }; + if ( + midiCommand >= MIDIEvent.NOTEON || + midiCommand <= MIDIEvent.NOTEOFF + ) { + this.channel = midiStatus & 0x0f; + } + }; - LGraphTextureDownsample.pixel_shader = - "precision highp float;\n\ - precision highp float;\n\ - uniform sampler2D u_texture;\n\ - uniform vec2 u_offset;\n\ - varying vec2 v_coord;\n\ - \n\ - void main() {\n\ - vec4 color = texture2D(u_texture, v_coord );\n\ - color += texture2D(u_texture, v_coord + vec2( u_offset.x, 0.0 ) );\n\ - color += texture2D(u_texture, v_coord + vec2( 0.0, u_offset.y ) );\n\ - color += texture2D(u_texture, v_coord + vec2( u_offset.x, u_offset.y ) );\n\ - gl_FragColor = color * 0.25;\n\ - }\n\ - "; + Object.defineProperty(MIDIEvent.prototype, "velocity", { + get: function() { + if (this.cmd == MIDIEvent.NOTEON) { + return this.data[2]; + } + return -1; + }, + set: function(v) { + this.data[2] = v; // v / 127; + }, + enumerable: true, + }); - LiteGraph.registerNodeType( - "texture/downsample", - LGraphTextureDownsample - ); + MIDIEvent.notes = [ + "A", + "A#", + "B", + "C", + "C#", + "D", + "D#", + "E", + "F", + "F#", + "G", + "G#", + ]; + MIDIEvent.note_to_index = { + A: 0, + "A#": 1, + B: 2, + C: 3, + "C#": 4, + D: 5, + "D#": 6, + E: 7, + F: 8, + "F#": 9, + G: 10, + "G#": 11, + }; + + Object.defineProperty(MIDIEvent.prototype, "note", { + get: function() { + if (this.cmd != MIDIEvent.NOTEON) { + return -1; + } + return MIDIEvent.toNoteString(this.data[1], true); + }, + set: function(v) { + throw "notes cannot be assigned this way, must modify the data[1]"; + }, + enumerable: true, + }); + Object.defineProperty(MIDIEvent.prototype, "octave", { + get: function() { + if (this.cmd != MIDIEvent.NOTEON) { + return -1; + } + var octave = this.data[1] - 24; + return Math.floor(octave / 12 + 1); + }, + set: function(v) { + throw "octave cannot be assigned this way, must modify the data[1]"; + }, + enumerable: true, + }); + // returns HZs + MIDIEvent.prototype.getPitch = function() { + return Math.pow(2, (this.data[1] - 69) / 12) * 440; + }; - function LGraphTextureResize() { - this.addInput("Texture", "Texture"); - this.addOutput("", "Texture"); - this.properties = { - size: [512,512], - generate_mipmaps: false, - precision: LGraphTexture.DEFAULT - }; - } + MIDIEvent.computePitch = function(note) { + return Math.pow(2, (note - 69) / 12) * 440; + }; - LGraphTextureResize.title = "Resize"; - LGraphTextureResize.desc = "Resize Texture"; - LGraphTextureResize.widgets_info = { - iterations: { type: "number", step: 1, precision: 0, min: 0 }, - precision: { widget: "combo", values: LGraphTexture.MODE_VALUES } - }; + MIDIEvent.prototype.getCC = function() { + return this.data[1]; + }; - LGraphTextureResize.prototype.onExecute = function() { - var tex = this.getInputData(0); - if (!tex && !this._temp_texture) { - return; - } + MIDIEvent.prototype.getCCValue = function() { + return this.data[2]; + }; - if (!this.isOutputConnected(0)) { - return; - } //saves work + // not tested, there is a formula missing here + MIDIEvent.prototype.getPitchBend = function() { + return this.data[1] + (this.data[2] << 7) - 8192; + }; - //we do not allow any texture different than texture 2D - if (!tex || tex.texture_type !== GL.TEXTURE_2D) { - return; - } + MIDIEvent.computePitchBend = function(v1, v2) { + return v1 + (v2 << 7) - 8192; + }; - var width = this.properties.size[0] | 0; - var height = this.properties.size[1] | 0; - if(width == 0) - width = tex.width; - if(height == 0) - height = tex.height; - var type = tex.type; - if (this.properties.precision === LGraphTexture.LOW) { - type = gl.UNSIGNED_BYTE; - } else if (this.properties.precision === LGraphTexture.HIGH) { - type = gl.HIGH_PRECISION_FORMAT; - } + MIDIEvent.prototype.setCommandFromString = function(str) { + this.cmd = MIDIEvent.computeCommandFromString(str); + }; - if( !this._texture || this._texture.width != width || this._texture.height != height || this._texture.type != type ) - this._texture = new GL.Texture( width, height, { type: type } ); + MIDIEvent.computeCommandFromString = function(str) { + if (!str) { + return 0; + } - tex.copyTo( this._texture ); + if (str && str.constructor === Number) { + return str; + } + + str = str.toUpperCase(); + switch (str) { + case "NOTE ON": + case "NOTEON": + return MIDIEvent.NOTEON; + case "NOTE OFF": + case "NOTEOFF": + return MIDIEvent.NOTEON; + case "KEY PRESSURE": + case "KEYPRESSURE": + return MIDIEvent.KEYPRESSURE; + case "CONTROLLER CHANGE": + case "CONTROLLERCHANGE": + case "CC": + return MIDIEvent.CONTROLLERCHANGE; + case "PROGRAM CHANGE": + case "PROGRAMCHANGE": + case "PC": + return MIDIEvent.PROGRAMCHANGE; + case "CHANNEL PRESSURE": + case "CHANNELPRESSURE": + return MIDIEvent.CHANNELPRESSURE; + case "PITCH BEND": + case "PITCHBEND": + return MIDIEvent.PITCHBEND; + case "TIME TICK": + case "TIMETICK": + return MIDIEvent.TIMETICK; + default: + return Number(str); // assume its a hex code + } + }; - if (this.properties.generate_mipmaps) { - this._texture.bind(0); - gl.generateMipmap(this._texture.texture_type); - this._texture.unbind(0); - } + // transform from a pitch number to string like "C4" + MIDIEvent.toNoteString = function(d, skip_octave) { + d = Math.round(d); // in case it has decimals + var note = d - 21; + var octave = Math.floor((d - 24) / 12 + 1); + note = note % 12; + if (note < 0) { + note = 12 + note; + } + return MIDIEvent.notes[note] + (skip_octave ? "" : octave); + }; - this.setOutputData(0, this._texture); - }; + MIDIEvent.NoteStringToPitch = function(str) { + str = str.toUpperCase(); + var note = str[0]; + var octave = 4; - LiteGraph.registerNodeType( "texture/resize", LGraphTextureResize ); + if (str[1] == "#") { + note += "#"; + if (str.length > 2) { + octave = Number(str[2]); + } + } else { + if (str.length > 1) { + octave = Number(str[1]); + } + } + var pitch = MIDIEvent.note_to_index[note]; + if (pitch == null) { + return null; + } + return (octave - 1) * 12 + pitch + 21; + }; - // Texture Average ***************************************** - function LGraphTextureAverage() { - this.addInput("Texture", "Texture"); - this.addOutput("tex", "Texture"); - this.addOutput("avg", "vec4"); - this.addOutput("lum", "number"); - this.properties = { - use_previous_frame: true, //to avoid stalls - high_quality: false //to use as much pixels as possible - }; + MIDIEvent.prototype.toString = function() { + var str = "" + this.channel + ". "; + switch (this.cmd) { + case MIDIEvent.NOTEON: + str += "NOTEON " + MIDIEvent.toNoteString(this.data[1]); + break; + case MIDIEvent.NOTEOFF: + str += "NOTEOFF " + MIDIEvent.toNoteString(this.data[1]); + break; + case MIDIEvent.CONTROLLERCHANGE: + str += "CC " + this.data[1] + " " + this.data[2]; + break; + case MIDIEvent.PROGRAMCHANGE: + str += "PC " + this.data[1]; + break; + case MIDIEvent.PITCHBEND: + str += "PITCHBEND " + this.getPitchBend(); + break; + case MIDIEvent.KEYPRESSURE: + str += "KEYPRESS " + this.data[1]; + break; + } - this._uniforms = { - u_texture: 0, - u_mipmap_offset: 0 - }; - this._luminance = new Float32Array(4); - } + return str; + }; - LGraphTextureAverage.title = "Average"; - LGraphTextureAverage.desc = - "Compute a partial average (32 random samples) of a texture and stores it as a 1x1 pixel texture.\n If high_quality is true, then it generates the mipmaps first and reads from the lower one."; + MIDIEvent.prototype.toHexString = function() { + var str = ""; + for (var i = 0; i < this.data.length; i++) { + str += this.data[i].toString(16) + " "; + } + }; - LGraphTextureAverage.prototype.onExecute = function() { - if (!this.properties.use_previous_frame) { - this.updateAverage(); - } + MIDIEvent.prototype.toJSON = function() { + return { + data: [this.data[0], this.data[1], this.data[2]], + object_class: "MIDIEvent", + }; + }; - var v = this._luminance; - this.setOutputData(0, this._temp_texture); - this.setOutputData(1, v); - this.setOutputData(2, (v[0] + v[1] + v[2]) / 3); - }; + MIDIEvent.NOTEOFF = 0x80; + MIDIEvent.NOTEON = 0x90; + MIDIEvent.KEYPRESSURE = 0xa0; + MIDIEvent.CONTROLLERCHANGE = 0xb0; + MIDIEvent.PROGRAMCHANGE = 0xc0; + MIDIEvent.CHANNELPRESSURE = 0xd0; + MIDIEvent.PITCHBEND = 0xe0; + MIDIEvent.TIMETICK = 0xf8; + + MIDIEvent.commands = { + 0x80: "note off", + 0x90: "note on", + 0xa0: "key pressure", + 0xb0: "controller change", + 0xc0: "program change", + 0xd0: "channel pressure", + 0xe0: "pitch bend", + 0xf0: "system", + 0xf2: "Song pos", + 0xf3: "Song select", + 0xf6: "Tune request", + 0xf8: "time tick", + 0xfa: "Start Song", + 0xfb: "Continue Song", + 0xfc: "Stop Song", + 0xfe: "Sensing", + 0xff: "Reset", + }; + + MIDIEvent.commands_short = { + 0x80: "NOTEOFF", + 0x90: "NOTEOFF", + 0xa0: "KEYP", + 0xb0: "CC", + 0xc0: "PC", + 0xd0: "CP", + 0xe0: "PB", + 0xf0: "SYS", + 0xf2: "POS", + 0xf3: "SELECT", + 0xf6: "TUNEREQ", + 0xf8: "TT", + 0xfa: "START", + 0xfb: "CONTINUE", + 0xfc: "STOP", + 0xfe: "SENS", + 0xff: "RESET", + }; + + MIDIEvent.commands_reversed = {}; + for (var i in MIDIEvent.commands) { + MIDIEvent.commands_reversed[MIDIEvent.commands[i]] = i; + } - //executed before rendering the frame - LGraphTextureAverage.prototype.onPreRenderExecute = function() { - this.updateAverage(); - }; + // MIDI wrapper, instantiate by MIDIIn and MIDIOut + function MIDIInterface(on_ready, on_error) { + if (!navigator.requestMIDIAccess) { + this.error = "not suppoorted"; + if (on_error) { + on_error("Not supported"); + } else { + console.error("MIDI NOT SUPPORTED, enable by chrome://flags"); + } + return; + } - LGraphTextureAverage.prototype.updateAverage = function() { - var tex = this.getInputData(0); - if (!tex) { - return; - } + this.on_ready = on_ready; - if ( - !this.isOutputConnected(0) && - !this.isOutputConnected(1) && - !this.isOutputConnected(2) - ) { - return; - } //saves work - - if (!LGraphTextureAverage._shader) { - LGraphTextureAverage._shader = new GL.Shader( - GL.Shader.SCREEN_VERTEX_SHADER, - LGraphTextureAverage.pixel_shader - ); - //creates 256 random numbers and stores them in two mat4 - var samples = new Float32Array(16); - for (var i = 0; i < samples.length; ++i) { - samples[i] = Math.random(); //poorly distributed samples - } - //upload only once - LGraphTextureAverage._shader.uniforms({ - u_samples_a: samples.subarray(0, 16), - u_samples_b: samples.subarray(16, 32) - }); - } + this.state = { + note: [], + cc: [], + }; - var temp = this._temp_texture; - var type = gl.UNSIGNED_BYTE; - if (tex.type != type) { - //force floats, half floats cannot be read with gl.readPixels - type = gl.FLOAT; - } + this.input_ports = null; + this.input_ports_info = []; + this.output_ports = null; + this.output_ports_info = []; - if (!temp || temp.type != type) { - this._temp_texture = new GL.Texture(1, 1, { - type: type, - format: gl.RGBA, - filter: gl.NEAREST - }); - } + navigator.requestMIDIAccess().then(this.onMIDISuccess.bind(this), this.onMIDIFailure.bind(this)); + } - this._uniforms.u_mipmap_offset = 0; + MIDIInterface.input = null; - if(this.properties.high_quality) - { - if( !this._temp_pot2_texture || this._temp_pot2_texture.type != type ) - this._temp_pot2_texture = new GL.Texture(512, 512, { - type: type, - format: gl.RGBA, - minFilter: gl.LINEAR_MIPMAP_LINEAR, - magFilter: gl.LINEAR - }); + MIDIInterface.MIDIEvent = MIDIEvent; - tex.copyTo( this._temp_pot2_texture ); - tex = this._temp_pot2_texture; - tex.bind(0); - gl.generateMipmap(GL.TEXTURE_2D); - this._uniforms.u_mipmap_offset = 9; - } + MIDIInterface.prototype.onMIDISuccess = function(midiAccess) { + console.log("MIDI ready!"); + console.log(midiAccess); + this.midi = midiAccess; // store in the global (in real usage, would probably keep in an object instance) + this.updatePorts(); - var shader = LGraphTextureAverage._shader; - var uniforms = this._uniforms; - uniforms.u_mipmap_offset = this.properties.mipmap_offset; - gl.disable(gl.DEPTH_TEST); - gl.disable(gl.BLEND); - this._temp_texture.drawTo(function() { - tex.toViewport(shader, uniforms); - }); + if (this.on_ready) { + this.on_ready(this); + } + }; - if (this.isOutputConnected(1) || this.isOutputConnected(2)) { - var pixel = this._temp_texture.getPixels(); - if (pixel) { - var v = this._luminance; - var type = this._temp_texture.type; - v.set(pixel); - if (type == gl.UNSIGNED_BYTE) { - vec4.scale(v, v, 1 / 255); - } else if ( - type == GL.HALF_FLOAT || - type == GL.HALF_FLOAT_OES - ) { - //no half floats possible, hard to read back unless copyed to a FLOAT texture, so temp_texture is always forced to FLOAT - } - } - } - }; + MIDIInterface.prototype.updatePorts = function() { + var midi = this.midi; + this.input_ports = midi.inputs; + this.input_ports_info = []; + this.output_ports = midi.outputs; + this.output_ports_info = []; - LGraphTextureAverage.pixel_shader = - "precision highp float;\n\ - precision highp float;\n\ - uniform mat4 u_samples_a;\n\ - uniform mat4 u_samples_b;\n\ - uniform sampler2D u_texture;\n\ - uniform float u_mipmap_offset;\n\ - varying vec2 v_coord;\n\ - \n\ - void main() {\n\ - vec4 color = vec4(0.0);\n\ - //random average\n\ - for(int i = 0; i < 4; ++i)\n\ - for(int j = 0; j < 4; ++j)\n\ - {\n\ - color += texture2D(u_texture, vec2( u_samples_a[i][j], u_samples_b[i][j] ), u_mipmap_offset );\n\ - color += texture2D(u_texture, vec2( 1.0 - u_samples_a[i][j], 1.0 - u_samples_b[i][j] ), u_mipmap_offset );\n\ - }\n\ - gl_FragColor = color * 0.03125;\n\ - }\n\ - "; + var num = 0; - LiteGraph.registerNodeType("texture/average", LGraphTextureAverage); + var it = this.input_ports.values(); + var it_value = it.next(); + while (it_value && it_value.done === false) { + var port_info = it_value.value; + this.input_ports_info.push(port_info); + console.log( "Input port [type:'" + port_info.type + "'] id:'" + port_info.id + "' manufacturer:'" + port_info.manufacturer + "' name:'" + port_info.name + "' version:'" + port_info.version + "'" ); + num++; + it_value = it.next(); + } + this.num_input_ports = num; + + num = 0; + var it = this.output_ports.values(); + var it_value = it.next(); + while (it_value && it_value.done === false) { + var port_info = it_value.value; + this.output_ports_info.push(port_info); + console.log( "Output port [type:'" + port_info.type + "'] id:'" + port_info.id + "' manufacturer:'" + port_info.manufacturer + "' name:'" + port_info.name + "' version:'" + port_info.version + "'" ); + num++; + it_value = it.next(); + } + this.num_output_ports = num; + }; + MIDIInterface.prototype.onMIDIFailure = function(msg) { + console.error("Failed to get MIDI access - " + msg); + }; + MIDIInterface.prototype.openInputPort = function(port, callback) { + var input_port = this.input_ports.get("input-" + port); + if (!input_port) { + return false; + } + MIDIInterface.input = this; + var that = this; - // Computes operation between pixels (max, min) ***************************************** - function LGraphTextureMinMax() { - this.addInput("Texture", "Texture"); - this.addOutput("min_t", "Texture"); - this.addOutput("max_t", "Texture"); - this.addOutput("min", "vec4"); - this.addOutput("max", "vec4"); - this.properties = { - mode: "max", - use_previous_frame: true //to avoid stalls - }; + input_port.onmidimessage = function(a) { + var midi_event = new MIDIEvent(a.data); + that.updateState(midi_event); + if (callback) { + callback(a.data, midi_event); + } + if (MIDIInterface.on_message) { + MIDIInterface.on_message(a.data, midi_event); + } + }; + console.log("port open: ", input_port); + return true; + }; - this._uniforms = { - u_texture: 0 - }; + MIDIInterface.parseMsg = function(data) {}; - this._max = new Float32Array(4); - this._min = new Float32Array(4); + MIDIInterface.prototype.updateState = function(midi_event) { + switch (midi_event.cmd) { + case MIDIEvent.NOTEON: + this.state.note[midi_event.value1 | 0] = midi_event.value2; + break; + case MIDIEvent.NOTEOFF: + this.state.note[midi_event.value1 | 0] = 0; + break; + case MIDIEvent.CONTROLLERCHANGE: + this.state.cc[midi_event.getCC()] = midi_event.getCCValue(); + break; + } + }; - this._textures_chain = []; - } + MIDIInterface.prototype.sendMIDI = function(port, midi_data) { + if (!midi_data) { + return; + } - LGraphTextureMinMax.widgets_info = { - mode: { widget: "combo", values: ["min","max","avg"] } - }; + var output_port = this.output_ports_info[port];// this.output_ports.get("output-" + port); + if (!output_port) { + return; + } - LGraphTextureMinMax.title = "MinMax"; - LGraphTextureMinMax.desc = "Compute the scene min max"; + MIDIInterface.output = this; - LGraphTextureMinMax.prototype.onExecute = function() { - if (!this.properties.use_previous_frame) { - this.update(); - } + if (midi_data.constructor === MIDIEvent) { + output_port.send(midi_data.data); + } else { + output_port.send(midi_data); + } + }; - this.setOutputData(0, this._temp_texture); - this.setOutputData(1, this._luminance); - }; + function LGMIDIIn() { + this.addOutput("on_midi", LiteGraph.EVENT); + this.addOutput("out", "midi"); + this.properties = { port: 0 }; + this._last_midi_event = null; + this._current_midi_event = null; + this.boxcolor = "#AAA"; + this._last_time = 0; - //executed before rendering the frame - LGraphTextureMinMax.prototype.onPreRenderExecute = function() { - this.update(); - }; + var that = this; + new MIDIInterface(function(midi) { + // open + that._midi = midi; + if (that._waiting) { + that.onStart(); + } + that._waiting = false; + }); + } - LGraphTextureMinMax.prototype.update = function() { - var tex = this.getInputData(0); - if (!tex) { - return; - } + LGMIDIIn.MIDIInterface = MIDIInterface; - if ( !this.isOutputConnected(0) && !this.isOutputConnected(1) ) { - return; - } //saves work + LGMIDIIn.title = "MIDI Input"; + LGMIDIIn.desc = "Reads MIDI from a input port"; + LGMIDIIn.color = MIDI_COLOR; - if (!LGraphTextureMinMax._shader) { - LGraphTextureMinMax._shader = new GL.Shader( GL.Shader.SCREEN_VERTEX_SHADER, LGraphTextureMinMax.pixel_shader ); - } + LGMIDIIn.prototype.getPropertyInfo = function(name) { + if (!this._midi) { + return; + } - var temp = this._temp_texture; - var type = gl.UNSIGNED_BYTE; - if (tex.type != type) { - //force floats, half floats cannot be read with gl.readPixels - type = gl.FLOAT; - } + if (name == "port") { + var values = {}; + for (var i = 0; i < this._midi.input_ports_info.length; ++i) { + var input = this._midi.input_ports_info[i]; + values[i] = i + ".- " + input.name + " version:" + input.version; + } + return { type: "enum", values: values }; + } + }; - var size = 512; + LGMIDIIn.prototype.onStart = function() { + if (this._midi) { + this._midi.openInputPort( + this.properties.port, + this.onMIDIEvent.bind(this), + ); + } else { + this._waiting = true; + } + }; + + LGMIDIIn.prototype.onMIDIEvent = function(data, midi_event) { + this._last_midi_event = midi_event; + this.boxcolor = "#AFA"; + this._last_time = LiteGraph.getTime(); + this.trigger("on_midi", midi_event); + if (midi_event.cmd == MIDIEvent.NOTEON) { + this.trigger("on_noteon", midi_event); + } else if (midi_event.cmd == MIDIEvent.NOTEOFF) { + this.trigger("on_noteoff", midi_event); + } else if (midi_event.cmd == MIDIEvent.CONTROLLERCHANGE) { + this.trigger("on_cc", midi_event); + } else if (midi_event.cmd == MIDIEvent.PROGRAMCHANGE) { + this.trigger("on_pc", midi_event); + } else if (midi_event.cmd == MIDIEvent.PITCHBEND) { + this.trigger("on_pitchbend", midi_event); + } + }; + + LGMIDIIn.prototype.onDrawBackground = function(ctx) { + this.boxcolor = "#AAA"; + if (!this.flags.collapsed && this._last_midi_event) { + ctx.fillStyle = "white"; + var now = LiteGraph.getTime(); + var f = 1.0 - Math.max(0, (now - this._last_time) * 0.001); + if (f > 0) { + var t = ctx.globalAlpha; + ctx.globalAlpha *= f; + ctx.font = "12px Tahoma"; + ctx.fillText( + this._last_midi_event.toString(), + 2, + this.size[1] * 0.5 + 3, + ); + // ctx.fillRect(0,0,this.size[0],this.size[1]); + ctx.globalAlpha = t; + } + } + }; - if( !this._textures_chain.length || this._textures_chain[0].type != type ) - { - var index = 0; - while(i) - { - this._textures_chain[i] = new GL.Texture( size, size, { - type: type, - format: gl.RGBA, - filter: gl.NEAREST - }); - size = size >> 2; - i++; - if(size == 1) - break; - } - } + LGMIDIIn.prototype.onExecute = function() { + if (this.outputs) { + var last = this._last_midi_event; + for (var i = 0; i < this.outputs.length; ++i) { + var output = this.outputs[i]; + var v = null; + switch (output.name) { + case "midi": + v = this._midi; + break; + case "last_midi": + v = last; + break; + default: + continue; + } + this.setOutputData(i, v); + } + } + }; - tex.copyTo( this._textures_chain[0] ); - var prev = this._textures_chain[0]; - for(var i = 1; i <= this._textures_chain.length; ++i) - { - var tex = this._textures_chain[i]; + LGMIDIIn.prototype.onGetOutputs = function() { + return [ + ["last_midi", "midi"], + ["on_midi", LiteGraph.EVENT], + ["on_noteon", LiteGraph.EVENT], + ["on_noteoff", LiteGraph.EVENT], + ["on_cc", LiteGraph.EVENT], + ["on_pc", LiteGraph.EVENT], + ["on_pitchbend", LiteGraph.EVENT], + ]; + }; - prev = tex; - } + LiteGraph.registerNodeType("midi/input", LGMIDIIn); - var shader = LGraphTextureMinMax._shader; - var uniforms = this._uniforms; - uniforms.u_mipmap_offset = this.properties.mipmap_offset; - gl.disable(gl.DEPTH_TEST); - gl.disable(gl.BLEND); - this._temp_texture.drawTo(function() { - tex.toViewport(shader, uniforms); - }); - }; + function LGMIDIOut() { + this.addInput("send", LiteGraph.EVENT); + this.properties = { port: 0 }; - LGraphTextureMinMax.pixel_shader = - "precision highp float;\n\ - precision highp float;\n\ - uniform mat4 u_samples_a;\n\ - uniform mat4 u_samples_b;\n\ - uniform sampler2D u_texture;\n\ - uniform float u_mipmap_offset;\n\ - varying vec2 v_coord;\n\ - \n\ - void main() {\n\ - vec4 color = vec4(0.0);\n\ - //random average\n\ - for(int i = 0; i < 4; ++i)\n\ - for(int j = 0; j < 4; ++j)\n\ - {\n\ - color += texture2D(u_texture, vec2( u_samples_a[i][j], u_samples_b[i][j] ), u_mipmap_offset );\n\ - color += texture2D(u_texture, vec2( 1.0 - u_samples_a[i][j], 1.0 - u_samples_b[i][j] ), u_mipmap_offset );\n\ - }\n\ - gl_FragColor = color * 0.03125;\n\ - }\n\ - "; + var that = this; + new MIDIInterface(function(midi) { + that._midi = midi; + that.widget.options.values = that.getMIDIOutputs(); + }); + this.widget = this.addWidget("combo","Device",this.properties.port,{ property: "port", values: this.getMIDIOutputs.bind(this) }); + this.size = [340,60]; + } - //LiteGraph.registerNodeType("texture/clustered_operation", LGraphTextureClusteredOperation); + LGMIDIOut.MIDIInterface = MIDIInterface; + LGMIDIOut.title = "MIDI Output"; + LGMIDIOut.desc = "Sends MIDI to output channel"; + LGMIDIOut.color = MIDI_COLOR; - function LGraphTextureTemporalSmooth() { - this.addInput("in", "Texture"); - this.addInput("factor", "Number"); - this.addOutput("out", "Texture"); - this.properties = { factor: 0.5 }; - this._uniforms = { - u_texture: 0, - u_textureB: 1, - u_factor: this.properties.factor - }; - } + LGMIDIOut.prototype.onGetPropertyInfo = function(name) { + if (!this._midi) { + return; + } - LGraphTextureTemporalSmooth.title = "Smooth"; - LGraphTextureTemporalSmooth.desc = "Smooth texture over time"; + if (name == "port") { + var values = this.getMIDIOutputs(); + return { type: "enum", values: values }; + } + }; + LGMIDIOut.default_ports = {0: "unknown"}; - LGraphTextureTemporalSmooth.prototype.onExecute = function() { - var tex = this.getInputData(0); - if (!tex || !this.isOutputConnected(0)) { - return; - } + LGMIDIOut.prototype.getMIDIOutputs = function() { + var values = {}; + if(!this._midi) + return LGMIDIOut.default_ports; + if(this._midi.output_ports_info) + for (var i = 0; i < this._midi.output_ports_info.length; ++i) { + var output = this._midi.output_ports_info[i]; + if(!output) + continue; + var name = i + ".- " + output.name + " version:" + output.version; + values[i] = name; + } + return values; + } - if (!LGraphTextureTemporalSmooth._shader) { - LGraphTextureTemporalSmooth._shader = new GL.Shader( - GL.Shader.SCREEN_VERTEX_SHADER, - LGraphTextureTemporalSmooth.pixel_shader - ); - } + LGMIDIOut.prototype.onAction = function(event, midi_event) { + // console.log(midi_event); + if (!this._midi) { + return; + } + if (event == "send") { + this._midi.sendMIDI(this.properties.port, midi_event); + } + this.trigger("midi", midi_event); + }; - var temp = this._temp_texture; - if ( - !temp || - temp.type != tex.type || - temp.width != tex.width || - temp.height != tex.height - ) { - var options = { - type: tex.type, - format: gl.RGBA, - filter: gl.NEAREST - }; - this._temp_texture = new GL.Texture(tex.width, tex.height, options ); - this._temp_texture2 = new GL.Texture(tex.width, tex.height, options ); - tex.copyTo(this._temp_texture2); - } + LGMIDIOut.prototype.onGetInputs = function() { + return [["send", LiteGraph.ACTION]]; + }; - var tempA = this._temp_texture; - var tempB = this._temp_texture2; + LGMIDIOut.prototype.onGetOutputs = function() { + return [["on_midi", LiteGraph.EVENT]]; + }; - var shader = LGraphTextureTemporalSmooth._shader; - var uniforms = this._uniforms; - uniforms.u_factor = 1.0 - this.getInputOrProperty("factor"); + LiteGraph.registerNodeType("midi/output", LGMIDIOut); - gl.disable(gl.BLEND); - gl.disable(gl.DEPTH_TEST); - tempA.drawTo(function() { - tempB.bind(1); - tex.toViewport(shader, uniforms); - }); - this.setOutputData(0, tempA); + function LGMIDIShow() { + this.addInput("on_midi", LiteGraph.EVENT); + this._str = ""; + this.size = [200, 40]; + } - //swap - this._temp_texture = tempB; - this._temp_texture2 = tempA; - }; + LGMIDIShow.title = "MIDI Show"; + LGMIDIShow.desc = "Shows MIDI in the graph"; + LGMIDIShow.color = MIDI_COLOR; - LGraphTextureTemporalSmooth.pixel_shader = - "precision highp float;\n\ - precision highp float;\n\ - uniform sampler2D u_texture;\n\ - uniform sampler2D u_textureB;\n\ - uniform float u_factor;\n\ - varying vec2 v_coord;\n\ - \n\ - void main() {\n\ - gl_FragColor = mix( texture2D( u_texture, v_coord ), texture2D( u_textureB, v_coord ), u_factor );\n\ - }\n\ - "; + LGMIDIShow.prototype.getTitle = function() { + if (this.flags.collapsed) { + return this._str; + } + return this.title; + }; - LiteGraph.registerNodeType( "texture/temporal_smooth", LGraphTextureTemporalSmooth ); + LGMIDIShow.prototype.onAction = function(event, midi_event) { + if (!midi_event) { + return; + } + if (midi_event.constructor === MIDIEvent) { + this._str = midi_event.toString(); + } else { + this._str = "???"; + } + }; + LGMIDIShow.prototype.onDrawForeground = function(ctx) { + if (!this._str || this.flags.collapsed) { + return; + } - function LGraphTextureLinearAvgSmooth() { - this.addInput("in", "Texture"); - this.addOutput("avg", "Texture"); - this.addOutput("array", "Texture"); - this.properties = { samples: 64, frames_interval: 1 }; - this._uniforms = { - u_texture: 0, - u_textureB: 1, - u_samples: this.properties.samples, - u_isamples: 1/this.properties.samples - }; - this.frame = 0; - } + ctx.font = "30px Arial"; + ctx.fillText(this._str, 10, this.size[1] * 0.8); + }; - LGraphTextureLinearAvgSmooth.title = "Lineal Avg Smooth"; - LGraphTextureLinearAvgSmooth.desc = "Smooth texture linearly over time"; + LGMIDIShow.prototype.onGetInputs = function() { + return [["in", LiteGraph.ACTION]]; + }; - LGraphTextureLinearAvgSmooth["@samples"] = { type: "number", min: 1, max: 64, step: 1, precision: 1 }; + LGMIDIShow.prototype.onGetOutputs = function() { + return [["on_midi", LiteGraph.EVENT]]; + }; - LGraphTextureLinearAvgSmooth.prototype.getPreviewTexture = function() - { - return this._temp_texture2; - } + LiteGraph.registerNodeType("midi/show", LGMIDIShow); - LGraphTextureLinearAvgSmooth.prototype.onExecute = function() { + function LGMIDIFilter() { + this.properties = { + channel: -1, + cmd: -1, + min_value: -1, + max_value: -1, + }; - var tex = this.getInputData(0); - if (!tex || !this.isOutputConnected(0)) { - return; - } + var that = this; + this._learning = false; + this.addWidget("button", "Learn", "", function() { + that._learning = true; + that.boxcolor = "#FA3"; + }); - if (!LGraphTextureLinearAvgSmooth._shader) { - LGraphTextureLinearAvgSmooth._shader_copy = new GL.Shader( GL.Shader.SCREEN_VERTEX_SHADER, LGraphTextureLinearAvgSmooth.pixel_shader_copy ); - LGraphTextureLinearAvgSmooth._shader_avg = new GL.Shader( GL.Shader.SCREEN_VERTEX_SHADER, LGraphTextureLinearAvgSmooth.pixel_shader_avg ); - } + this.addInput("in", LiteGraph.EVENT); + this.addOutput("on_midi", LiteGraph.EVENT); + this.boxcolor = "#AAA"; + } - var samples = clamp(this.properties.samples,0,64); - var frame = this.frame; - var interval = this.properties.frames_interval; + LGMIDIFilter.title = "MIDI Filter"; + LGMIDIFilter.desc = "Filters MIDI messages"; + LGMIDIFilter.color = MIDI_COLOR; - if( interval == 0 || frame % interval == 0 ) - { - var temp = this._temp_texture; - if ( !temp || temp.type != tex.type || temp.width != samples ) { - var options = { - type: tex.type, - format: gl.RGBA, - filter: gl.NEAREST - }; - this._temp_texture = new GL.Texture( samples, 1, options ); - this._temp_texture2 = new GL.Texture( samples, 1, options ); - this._temp_texture_out = new GL.Texture( 1, 1, options ); - } + LGMIDIFilter["@cmd"] = { + type: "enum", + title: "Command", + values: MIDIEvent.commands_reversed, + }; - var tempA = this._temp_texture; - var tempB = this._temp_texture2; + LGMIDIFilter.prototype.getTitle = function() { + var str = null; + if (this.properties.cmd == -1) { + str = "Nothing"; + } else { + str = MIDIEvent.commands_short[this.properties.cmd] || "Unknown"; + } - var shader_copy = LGraphTextureLinearAvgSmooth._shader_copy; - var shader_avg = LGraphTextureLinearAvgSmooth._shader_avg; - var uniforms = this._uniforms; - uniforms.u_samples = samples; - uniforms.u_isamples = 1.0 / samples; + if ( + this.properties.min_value != -1 && + this.properties.max_value != -1 + ) { + str += + " " + + (this.properties.min_value == this.properties.max_value + ? this.properties.max_value + : this.properties.min_value + + ".." + + this.properties.max_value); + } - gl.disable(gl.BLEND); - gl.disable(gl.DEPTH_TEST); - tempA.drawTo(function() { - tempB.bind(1); - tex.toViewport( shader_copy, uniforms ); - }); + return "Filter: " + str; + }; - this._temp_texture_out.drawTo(function() { - tempA.toViewport( shader_avg, uniforms ); - }); + LGMIDIFilter.prototype.onPropertyChanged = function(name, value) { + if (name == "cmd") { + var num = Number(value); + if (isNaN(num)) { + num = MIDIEvent.commands[value] || 0; + } + this.properties.cmd = num; + } + }; - this.setOutputData( 0, this._temp_texture_out ); + LGMIDIFilter.prototype.onAction = function(event, midi_event) { + if (!midi_event || midi_event.constructor !== MIDIEvent) { + return; + } - //swap - this._temp_texture = tempB; - this._temp_texture2 = tempA; - } - else - this.setOutputData(0, this._temp_texture_out); - this.setOutputData(1, this._temp_texture2); - this.frame++; - }; + if (this._learning) { + this._learning = false; + this.boxcolor = "#AAA"; + this.properties.channel = midi_event.channel; + this.properties.cmd = midi_event.cmd; + this.properties.min_value = this.properties.max_value = + midi_event.data[1]; + } else { + if ( + this.properties.channel != -1 && + midi_event.channel != this.properties.channel + ) { + return; + } + if ( + this.properties.cmd != -1 && + midi_event.cmd != this.properties.cmd + ) { + return; + } + if ( + this.properties.min_value != -1 && + midi_event.data[1] < this.properties.min_value + ) { + return; + } + if ( + this.properties.max_value != -1 && + midi_event.data[1] > this.properties.max_value + ) { + return; + } + } - LGraphTextureLinearAvgSmooth.pixel_shader_copy = - "precision highp float;\n\ - precision highp float;\n\ - uniform sampler2D u_texture;\n\ - uniform sampler2D u_textureB;\n\ - uniform float u_isamples;\n\ - varying vec2 v_coord;\n\ - \n\ - void main() {\n\ - if( v_coord.x <= u_isamples )\n\ - gl_FragColor = texture2D( u_texture, vec2(0.5) );\n\ - else\n\ - gl_FragColor = texture2D( u_textureB, v_coord - vec2(u_isamples,0.0) );\n\ - }\n\ - "; + this.trigger("on_midi", midi_event); + }; - LGraphTextureLinearAvgSmooth.pixel_shader_avg = - "precision highp float;\n\ - precision highp float;\n\ - uniform sampler2D u_texture;\n\ - uniform int u_samples;\n\ - uniform float u_isamples;\n\ - varying vec2 v_coord;\n\ - \n\ - void main() {\n\ - vec4 color = vec4(0.0);\n\ - for(int i = 0; i < 64; ++i)\n\ - {\n\ - color += texture2D( u_texture, vec2( float(i)*u_isamples,0.0) );\n\ - if(i == (u_samples - 1))\n\ - break;\n\ - }\n\ - gl_FragColor = color * u_isamples;\n\ - }\n\ - "; + LiteGraph.registerNodeType("midi/filter", LGMIDIFilter); + function LGMIDIEvent() { + this.properties = { + channel: 0, + cmd: 144, // 0x90 + value1: 1, + value2: 1, + }; - LiteGraph.registerNodeType( "texture/linear_avg_smooth", LGraphTextureLinearAvgSmooth ); + this.addInput("send", LiteGraph.EVENT); + this.addInput("assign", LiteGraph.EVENT); + this.addOutput("on_midi", LiteGraph.EVENT); - // Image To Texture ***************************************** - function LGraphImageToTexture() { - this.addInput("Image", "image"); - this.addOutput("", "Texture"); - this.properties = {}; - } + this.midi_event = new MIDIEvent(); + this.gate = false; + } - LGraphImageToTexture.title = "Image to Texture"; - LGraphImageToTexture.desc = "Uploads an image to the GPU"; - //LGraphImageToTexture.widgets_info = { size: { widget:"combo", values:[0,32,64,128,256,512,1024,2048]} }; + LGMIDIEvent.title = "MIDIEvent"; + LGMIDIEvent.desc = "Create a MIDI Event"; + LGMIDIEvent.color = MIDI_COLOR; + + LGMIDIEvent.prototype.onAction = function(event, midi_event) { + if (event == "assign") { + this.properties.channel = midi_event.channel; + this.properties.cmd = midi_event.cmd; + this.properties.value1 = midi_event.data[1]; + this.properties.value2 = midi_event.data[2]; + if (midi_event.cmd == MIDIEvent.NOTEON) { + this.gate = true; + } else if (midi_event.cmd == MIDIEvent.NOTEOFF) { + this.gate = false; + } + return; + } - LGraphImageToTexture.prototype.onExecute = function() { - var img = this.getInputData(0); - if (!img) { - return; - } + // send + var midi_event = this.midi_event; + midi_event.channel = this.properties.channel; + if (this.properties.cmd && this.properties.cmd.constructor === String) { + midi_event.setCommandFromString(this.properties.cmd); + } else { + midi_event.cmd = this.properties.cmd; + } + midi_event.data[0] = midi_event.cmd | midi_event.channel; + midi_event.data[1] = Number(this.properties.value1); + midi_event.data[2] = Number(this.properties.value2); - var width = img.videoWidth || img.width; - var height = img.videoHeight || img.height; + this.trigger("on_midi", midi_event); + }; - //this is in case we are using a webgl canvas already, no need to reupload it - if (img.gltexture) { - this.setOutputData(0, img.gltexture); - return; - } + LGMIDIEvent.prototype.onExecute = function() { + var props = this.properties; - var temp = this._temp_texture; - if (!temp || temp.width != width || temp.height != height) { - this._temp_texture = new GL.Texture(width, height, { - format: gl.RGBA, - filter: gl.LINEAR - }); - } + if (this.inputs) { + for (var i = 0; i < this.inputs.length; ++i) { + var input = this.inputs[i]; + if (input.link == -1) { + continue; + } + switch (input.name) { + case "note": + var v = this.getInputData(i); + if (v != null) { + if (v.constructor === String) { + v = MIDIEvent.NoteStringToPitch(v); + } + this.properties.value1 = (v | 0) % 255; + } + break; + case "cmd": + var v = this.getInputData(i); + if (v != null) { + this.properties.cmd = v; + } + break; + case "value1": + var v = this.getInputData(i); + if (v != null) { + this.properties.value1 = clamp(v|0,0,127); + } + break; + case "value2": + var v = this.getInputData(i); + if (v != null) { + this.properties.value2 = clamp(v|0,0,127); + } + break; + } + } + } - try { - this._temp_texture.uploadImage(img); - } catch (err) { - console.error( - "image comes from an unsafe location, cannot be uploaded to webgl: " + - err - ); - return; - } + if (this.outputs) { + for (var i = 0; i < this.outputs.length; ++i) { + var output = this.outputs[i]; + var v = null; + switch (output.name) { + case "midi": + v = new MIDIEvent(); + v.setup([props.cmd, props.value1, props.value2]); + v.channel = props.channel; + break; + case "command": + v = props.cmd; + break; + case "cc": + v = props.value1; + break; + case "cc_value": + v = props.value2; + break; + case "note": + v = + props.cmd == MIDIEvent.NOTEON || + props.cmd == MIDIEvent.NOTEOFF + ? props.value1 + : null; + break; + case "velocity": + v = props.cmd == MIDIEvent.NOTEON ? props.value2 : null; + break; + case "pitch": + v = + props.cmd == MIDIEvent.NOTEON + ? MIDIEvent.computePitch(props.value1) + : null; + break; + case "pitchbend": + v = + props.cmd == MIDIEvent.PITCHBEND + ? MIDIEvent.computePitchBend( + props.value1, + props.value2, + ) + : null; + break; + case "gate": + v = this.gate; + break; + default: + continue; + } + if (v !== null) { + this.setOutputData(i, v); + } + } + } + }; + + LGMIDIEvent.prototype.onPropertyChanged = function(name, value) { + if (name == "cmd") { + this.properties.cmd = MIDIEvent.computeCommandFromString(value); + } + }; - this.setOutputData(0, this._temp_texture); - }; + LGMIDIEvent.prototype.onGetInputs = function() { + return [["cmd", "number"],["note", "number"],["value1", "number"],["value2", "number"]]; + }; - LiteGraph.registerNodeType( - "texture/imageToTexture", - LGraphImageToTexture - ); - - // Texture LUT ***************************************** - function LGraphTextureLUT() { - this.addInput("Texture", "Texture"); - this.addInput("LUT", "Texture"); - this.addInput("Intensity", "number"); - this.addOutput("", "Texture"); - this.properties = { enabled: true, intensity: 1, precision: LGraphTexture.DEFAULT, texture: null }; - - if (!LGraphTextureLUT._shader) { - LGraphTextureLUT._shader = new GL.Shader( Shader.SCREEN_VERTEX_SHADER, LGraphTextureLUT.pixel_shader ); - } - } + LGMIDIEvent.prototype.onGetOutputs = function() { + return [ + ["midi", "midi"], + ["on_midi", LiteGraph.EVENT], + ["command", "number"], + ["note", "number"], + ["velocity", "number"], + ["cc", "number"], + ["cc_value", "number"], + ["pitch", "number"], + ["gate", "bool"], + ["pitchbend", "number"], + ]; + }; - LGraphTextureLUT.widgets_info = { - texture: { widget: "texture" }, - precision: { widget: "combo", values: LGraphTexture.MODE_VALUES } - }; + LiteGraph.registerNodeType("midi/event", LGMIDIEvent); - LGraphTextureLUT.title = "LUT"; - LGraphTextureLUT.desc = "Apply LUT to Texture"; + function LGMIDICC() { + this.properties = { + // channel: 0, + cc: 1, + value: 0, + }; - LGraphTextureLUT.prototype.onExecute = function() { - if (!this.isOutputConnected(0)) { - return; - } //saves work + this.addOutput("value", "number"); + } - var tex = this.getInputData(0); + LGMIDICC.title = "MIDICC"; + LGMIDICC.desc = "gets a Controller Change"; + LGMIDICC.color = MIDI_COLOR; - if (this.properties.precision === LGraphTexture.PASS_THROUGH || this.properties.enabled === false) { - this.setOutputData(0, tex); - return; - } + LGMIDICC.prototype.onExecute = function() { + var props = this.properties; + if (MIDIInterface.input) { + this.properties.value = + MIDIInterface.input.state.cc[this.properties.cc]; + } + this.setOutputData(0, this.properties.value); + }; - if (!tex) { - return; - } + LiteGraph.registerNodeType("midi/cc", LGMIDICC); - var lut_tex = this.getInputData(1); + function LGMIDIGenerator() { + this.addInput("generate", LiteGraph.ACTION); + this.addInput("scale", "string"); + this.addInput("octave", "number"); + this.addOutput("note", LiteGraph.EVENT); + this.properties = { + notes: "A,A#,B,C,C#,D,D#,E,F,F#,G,G#", + octave: 2, + duration: 0.5, + mode: "sequence", + }; - if (!lut_tex) { - lut_tex = LGraphTexture.getTexture(this.properties.texture); - } + this.notes_pitches = LGMIDIGenerator.processScale(this.properties.notes); + this.sequence_index = 0; + } - if (!lut_tex) { - this.setOutputData(0, tex); - return; - } + LGMIDIGenerator.title = "MIDI Generator"; + LGMIDIGenerator.desc = "Generates a random MIDI note"; + LGMIDIGenerator.color = MIDI_COLOR; - lut_tex.bind(0); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); - gl.texParameteri( - gl.TEXTURE_2D, - gl.TEXTURE_WRAP_S, - gl.CLAMP_TO_EDGE - ); - gl.texParameteri( - gl.TEXTURE_2D, - gl.TEXTURE_WRAP_T, - gl.CLAMP_TO_EDGE - ); - gl.bindTexture(gl.TEXTURE_2D, null); - - var intensity = this.properties.intensity; - if (this.isInputConnected(2)) { - this.properties.intensity = intensity = this.getInputData(2); - } + LGMIDIGenerator.processScale = function(scale) { + var notes = scale.split(","); + for (var i = 0; i < notes.length; ++i) { + var n = notes[i]; + if ((n.length == 2 && n[1] != "#") || n.length > 2) { + notes[i] = -LiteGraph.MIDIEvent.NoteStringToPitch(n); + } else { + notes[i] = MIDIEvent.note_to_index[n] || 0; + } + } + return notes; + }; - this._tex = LGraphTexture.getTargetTexture( - tex, - this._tex, - this.properties.precision - ); - - //var mesh = Mesh.getScreenQuad(); - - this._tex.drawTo(function() { - lut_tex.bind(1); - tex.toViewport(LGraphTextureLUT._shader, { - u_texture: 0, - u_textureB: 1, - u_amount: intensity - }); - }); + LGMIDIGenerator.prototype.onPropertyChanged = function(name, value) { + if (name == "notes") { + this.notes_pitches = LGMIDIGenerator.processScale(value); + } + }; - this.setOutputData(0, this._tex); - }; + LGMIDIGenerator.prototype.onExecute = function() { + var octave = this.getInputData(2); + if (octave != null) { + this.properties.octave = octave; + } - LGraphTextureLUT.pixel_shader = - "precision highp float;\n\ - precision highp float;\n\ - varying vec2 v_coord;\n\ - uniform sampler2D u_texture;\n\ - uniform sampler2D u_textureB;\n\ - uniform float u_amount;\n\ - \n\ - void main() {\n\ - lowp vec4 textureColor = clamp( texture2D(u_texture, v_coord), vec4(0.0), vec4(1.0) );\n\ - mediump float blueColor = textureColor.b * 63.0;\n\ - mediump vec2 quad1;\n\ - quad1.y = floor(floor(blueColor) / 8.0);\n\ - quad1.x = floor(blueColor) - (quad1.y * 8.0);\n\ - mediump vec2 quad2;\n\ - quad2.y = floor(ceil(blueColor) / 8.0);\n\ - quad2.x = ceil(blueColor) - (quad2.y * 8.0);\n\ - highp vec2 texPos1;\n\ - texPos1.x = (quad1.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.r);\n\ - texPos1.y = 1.0 - ((quad1.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.g));\n\ - highp vec2 texPos2;\n\ - texPos2.x = (quad2.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.r);\n\ - texPos2.y = 1.0 - ((quad2.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.g));\n\ - lowp vec4 newColor1 = texture2D(u_textureB, texPos1);\n\ - lowp vec4 newColor2 = texture2D(u_textureB, texPos2);\n\ - lowp vec4 newColor = mix(newColor1, newColor2, fract(blueColor));\n\ - gl_FragColor = vec4( mix( textureColor.rgb, newColor.rgb, u_amount), textureColor.w);\n\ - }\n\ - "; + var scale = this.getInputData(1); + if (scale) { + this.notes_pitches = LGMIDIGenerator.processScale(scale); + } + }; - LiteGraph.registerNodeType("texture/LUT", LGraphTextureLUT); + LGMIDIGenerator.prototype.onAction = function(event, midi_event) { + // var range = this.properties.max - this.properties.min; + // var pitch = this.properties.min + ((Math.random() * range)|0); + var pitch = 0; + var range = this.notes_pitches.length; + var index = 0; + if (this.properties.mode == "sequence") { + index = this.sequence_index = (this.sequence_index + 1) % range; + } else if (this.properties.mode == "random") { + index = Math.floor(Math.random() * range); + } - // Texture LUT ***************************************** - function LGraphTextureEncode() { - this.addInput("Texture", "Texture"); - this.addInput("Atlas", "Texture"); - this.addOutput("", "Texture"); - this.properties = { enabled: true, num_row_symbols: 4, symbol_size: 16, brightness: 1, colorize: false, filter: false, invert: false, precision: LGraphTexture.DEFAULT, generate_mipmaps: false, texture: null }; + var note = this.notes_pitches[index]; + if (note >= 0) { + pitch = note + (this.properties.octave - 1) * 12 + 33; + } else { + pitch = -note; + } + + var midi_event = new MIDIEvent(); + midi_event.setup([MIDIEvent.NOTEON, pitch, 10]); + var duration = this.properties.duration || 1; + this.trigger("note", midi_event); + + // noteoff + setTimeout( + function() { + var midi_event = new MIDIEvent(); + midi_event.setup([MIDIEvent.NOTEOFF, pitch, 0]); + this.trigger("note", midi_event); + }.bind(this), + duration * 1000, + ); + }; - if (!LGraphTextureEncode._shader) { - LGraphTextureEncode._shader = new GL.Shader( Shader.SCREEN_VERTEX_SHADER, LGraphTextureEncode.pixel_shader ); - } + LiteGraph.registerNodeType("midi/generator", LGMIDIGenerator); - this._uniforms = { - u_texture: 0, - u_textureB: 1, - u_row_simbols: 4, - u_simbol_size: 16, - u_res: vec2.create() - }; - } + function LGMIDITranspose() { + this.properties = {amount: 0}; + this.addInput("in", LiteGraph.ACTION); + this.addInput("amount", "number"); + this.addOutput("out", LiteGraph.EVENT); - LGraphTextureEncode.widgets_info = { - texture: { widget: "texture" }, - precision: { widget: "combo", values: LGraphTexture.MODE_VALUES } - }; + this.midi_event = new MIDIEvent(); + } - LGraphTextureEncode.title = "Encode"; - LGraphTextureEncode.desc = "Apply a texture atlas to encode a texture"; + LGMIDITranspose.title = "MIDI Transpose"; + LGMIDITranspose.desc = "Transpose a MIDI note"; + LGMIDITranspose.color = MIDI_COLOR; - LGraphTextureEncode.prototype.onExecute = function() { - if (!this.isOutputConnected(0)) { - return; - } //saves work + LGMIDITranspose.prototype.onAction = function(event, midi_event) { + if (!midi_event || midi_event.constructor !== MIDIEvent) { + return; + } - var tex = this.getInputData(0); + if ( + midi_event.data[0] == MIDIEvent.NOTEON || + midi_event.data[0] == MIDIEvent.NOTEOFF + ) { + this.midi_event = new MIDIEvent(); + this.midi_event.setup(midi_event.data); + this.midi_event.data[1] = Math.round(this.midi_event.data[1] + this.properties.amount); + this.trigger("out", this.midi_event); + } else { + this.trigger("out", midi_event); + } + }; - if (this.properties.precision === LGraphTexture.PASS_THROUGH || this.properties.enabled === false) { - this.setOutputData(0, tex); - return; - } + LGMIDITranspose.prototype.onExecute = function() { + var amount = this.getInputData(1); + if (amount != null) { + this.properties.amount = amount; + } + }; - if (!tex) { - return; - } + LiteGraph.registerNodeType("midi/transpose", LGMIDITranspose); - var symbols_tex = this.getInputData(1); + function LGMIDIQuantize() { + this.properties = {scale: "A,A#,B,C,C#,D,D#,E,F,F#,G,G#"}; + this.addInput("note", LiteGraph.ACTION); + this.addInput("scale", "string"); + this.addOutput("out", LiteGraph.EVENT); - if (!symbols_tex) { - symbols_tex = LGraphTexture.getTexture(this.properties.texture); - } + this.valid_notes = new Array(12); + this.offset_notes = new Array(12); + this.processScale(this.properties.scale); + } - if (!symbols_tex) { - this.setOutputData(0, tex); - return; - } + LGMIDIQuantize.title = "MIDI Quantize Pitch"; + LGMIDIQuantize.desc = "Transpose a MIDI note tp fit an scale"; + LGMIDIQuantize.color = MIDI_COLOR; - symbols_tex.bind(0); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, this.properties.filter ? gl.LINEAR : gl.NEAREST ); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, this.properties.filter ? gl.LINEAR : gl.NEAREST ); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE ); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE ); - gl.bindTexture(gl.TEXTURE_2D, null); - - var uniforms = this._uniforms; - uniforms.u_row_simbols = Math.floor(this.properties.num_row_symbols); - uniforms.u_symbol_size = this.properties.symbol_size; - uniforms.u_brightness = this.properties.brightness; - uniforms.u_invert = this.properties.invert ? 1 : 0; - uniforms.u_colorize = this.properties.colorize ? 1 : 0; - - this._tex = LGraphTexture.getTargetTexture( tex, this._tex, this.properties.precision ); - uniforms.u_res[0] = this._tex.width; - uniforms.u_res[1] = this._tex.height; - this._tex.bind(0); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST ); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST ); - - this._tex.drawTo(function() { - symbols_tex.bind(1); - tex.toViewport(LGraphTextureEncode._shader, uniforms); - }); + LGMIDIQuantize.prototype.onPropertyChanged = function(name, value) { + if (name == "scale") { + this.processScale(value); + } + }; - if (this.properties.generate_mipmaps) { - this._tex.bind(0); - gl.generateMipmap(this._tex.texture_type); - this._tex.unbind(0); - } + LGMIDIQuantize.prototype.processScale = function(scale) { + this._current_scale = scale; + this.notes_pitches = LGMIDIGenerator.processScale(scale); + for (var i = 0; i < 12; ++i) { + this.valid_notes[i] = this.notes_pitches.indexOf(i) != -1; + } + for (var i = 0; i < 12; ++i) { + if (this.valid_notes[i]) { + this.offset_notes[i] = 0; + continue; + } + for (var j = 1; j < 12; ++j) { + if (this.valid_notes[(i - j) % 12]) { + this.offset_notes[i] = -j; + break; + } + if (this.valid_notes[(i + j) % 12]) { + this.offset_notes[i] = j; + break; + } + } + } + }; - this.setOutputData(0, this._tex); - }; + LGMIDIQuantize.prototype.onAction = function(event, midi_event) { + if (!midi_event || midi_event.constructor !== MIDIEvent) { + return; + } - LGraphTextureEncode.pixel_shader = - "precision highp float;\n\ - precision highp float;\n\ - varying vec2 v_coord;\n\ - uniform sampler2D u_texture;\n\ - uniform sampler2D u_textureB;\n\ - uniform float u_row_simbols;\n\ - uniform float u_symbol_size;\n\ - uniform float u_brightness;\n\ - uniform float u_invert;\n\ - uniform float u_colorize;\n\ - uniform vec2 u_res;\n\ - \n\ - void main() {\n\ - vec2 total_symbols = u_res / u_symbol_size;\n\ - vec2 uv = floor(v_coord * total_symbols) / total_symbols; //pixelate \n\ - vec2 local_uv = mod(v_coord * u_res, u_symbol_size) / u_symbol_size;\n\ - lowp vec4 textureColor = texture2D(u_texture, uv );\n\ - float lum = clamp(u_brightness * (textureColor.x + textureColor.y + textureColor.z)/3.0,0.0,1.0);\n\ - if( u_invert == 1.0 ) lum = 1.0 - lum;\n\ - float index = floor( lum * (u_row_simbols * u_row_simbols - 1.0));\n\ - float col = mod( index, u_row_simbols );\n\ - float row = u_row_simbols - floor( index / u_row_simbols ) - 1.0;\n\ - vec2 simbol_uv = ( vec2( col, row ) + local_uv ) / u_row_simbols;\n\ - vec4 color = texture2D( u_textureB, simbol_uv );\n\ - if(u_colorize == 1.0)\n\ - color *= textureColor;\n\ - gl_FragColor = color;\n\ - }\n\ - "; + if ( + midi_event.data[0] == MIDIEvent.NOTEON || + midi_event.data[0] == MIDIEvent.NOTEOFF + ) { + this.midi_event = new MIDIEvent(); + this.midi_event.setup(midi_event.data); + var note = midi_event.note; + var index = MIDIEvent.note_to_index[note]; + var offset = this.offset_notes[index]; + this.midi_event.data[1] += offset; + this.trigger("out", this.midi_event); + } else { + this.trigger("out", midi_event); + } + }; - LiteGraph.registerNodeType("texture/encode", LGraphTextureEncode); + LGMIDIQuantize.prototype.onExecute = function() { + var scale = this.getInputData(1); + if (scale != null && scale != this._current_scale) { + this.processScale(scale); + } + }; - // Texture Channels ***************************************** - function LGraphTextureChannels() { - this.addInput("Texture", "Texture"); + LiteGraph.registerNodeType("midi/quantize", LGMIDIQuantize); - this.addOutput("R", "Texture"); - this.addOutput("G", "Texture"); - this.addOutput("B", "Texture"); - this.addOutput("A", "Texture"); + function LGMIDIFromFile() { + this.properties = { + url: "", + autoplay: true, + }; - //this.properties = { use_single_channel: true }; - if (!LGraphTextureChannels._shader) { - LGraphTextureChannels._shader = new GL.Shader( - Shader.SCREEN_VERTEX_SHADER, - LGraphTextureChannels.pixel_shader - ); - } - } + this.addInput("play", LiteGraph.ACTION); + this.addInput("pause", LiteGraph.ACTION); + this.addOutput("note", LiteGraph.EVENT); + this._midi = null; + this._current_time = 0; + this._playing = false; - LGraphTextureChannels.title = "Texture to Channels"; - LGraphTextureChannels.desc = "Split texture channels"; + if (typeof MidiParser == "undefined") { + console.error("midi-parser.js not included, LGMidiPlay requires that library: https://raw.githubusercontent.com/colxi/midi-parser-js/master/src/main.js"); + this.boxcolor = "red"; + } - LGraphTextureChannels.prototype.onExecute = function() { - var texA = this.getInputData(0); - if (!texA) { - return; - } + } - if (!this._channels) { - this._channels = Array(4); - } + LGMIDIFromFile.title = "MIDI fromFile"; + LGMIDIFromFile.desc = "Plays a MIDI file"; + LGMIDIFromFile.color = MIDI_COLOR; - //var format = this.properties.use_single_channel ? gl.LUMINANCE : gl.RGBA; //not supported by WebGL1 - var format = gl.RGB; - var connections = 0; - for (var i = 0; i < 4; i++) { - if (this.isOutputConnected(i)) { - if ( - !this._channels[i] || - this._channels[i].width != texA.width || - this._channels[i].height != texA.height || - this._channels[i].type != texA.type || - this._channels[i].format != format - ) { - this._channels[i] = new GL.Texture( - texA.width, - texA.height, - { - type: texA.type, - format: format, - filter: gl.LINEAR - } - ); - } - connections++; - } else { - this._channels[i] = null; - } - } + LGMIDIFromFile.prototype.onAction = function( name ) { + if(name == "play") + this.play(); + else if(name == "pause") + this._playing = !this._playing; + } - if (!connections) { - return; - } + LGMIDIFromFile.prototype.onPropertyChanged = function(name,value) { + if(name == "url") + this.loadMIDIFile(value); + } - gl.disable(gl.BLEND); - gl.disable(gl.DEPTH_TEST); - - var mesh = Mesh.getScreenQuad(); - var shader = LGraphTextureChannels._shader; - var masks = [ - [1, 0, 0, 0], - [0, 1, 0, 0], - [0, 0, 1, 0], - [0, 0, 0, 1] - ]; - - for (var i = 0; i < 4; i++) { - if (!this._channels[i]) { - continue; - } + LGMIDIFromFile.prototype.onExecute = function() { + if(!this._midi) + return; - this._channels[i].drawTo(function() { - texA.bind(0); - shader - .uniforms({ u_texture: 0, u_mask: masks[i] }) - .draw(mesh); - }); - this.setOutputData(i, this._channels[i]); - } - }; + if(!this._playing) + return; - LGraphTextureChannels.pixel_shader = - "precision highp float;\n\ - precision highp float;\n\ - varying vec2 v_coord;\n\ - uniform sampler2D u_texture;\n\ - uniform vec4 u_mask;\n\ - \n\ - void main() {\n\ - gl_FragColor = vec4( vec3( length( texture2D(u_texture, v_coord) * u_mask )), 1.0 );\n\ - }\n\ - "; + this._current_time += this.graph.elapsed_time; + var current_time = this._current_time * 100; - LiteGraph.registerNodeType( - "texture/textureChannels", - LGraphTextureChannels - ); + for(var i = 0; i < this._midi.tracks; ++i) { + var track = this._midi.track[i]; + if(!track._last_pos) { + track._last_pos = 0; + track._time = 0; + } - // Texture Channels to Texture ***************************************** - function LGraphChannelsTexture() { - this.addInput("R", "Texture"); - this.addInput("G", "Texture"); - this.addInput("B", "Texture"); - this.addInput("A", "Texture"); + var elem = track.event[track._last_pos]; + if(elem && (track._time + elem.deltaTime) <= current_time ) { + track._last_pos++; + track._time += elem.deltaTime; - this.addOutput("Texture", "Texture"); + if(elem.data) { + var midi_cmd = elem.type << 4 + elem.channel; + var midi_event = new MIDIEvent(); + midi_event.setup([midi_cmd, elem.data[0], elem.data[1]]); + this.trigger("note", midi_event); + } + } - this.properties = { - precision: LGraphTexture.DEFAULT, - R: 1, - G: 1, - B: 1, - A: 1 - }; - this._color = vec4.create(); - this._uniforms = { - u_textureR: 0, - u_textureG: 1, - u_textureB: 2, - u_textureA: 3, - u_color: this._color - }; - } + } + }; - LGraphChannelsTexture.title = "Channels to Texture"; - LGraphChannelsTexture.desc = "Split texture channels"; - LGraphChannelsTexture.widgets_info = { - precision: { widget: "combo", values: LGraphTexture.MODE_VALUES } - }; + LGMIDIFromFile.prototype.play = function() { + this._playing = true; + this._current_time = 0; + if(!this._midi) + return; - LGraphChannelsTexture.prototype.onExecute = function() { - var white = LGraphTexture.getWhiteTexture(); - var texR = this.getInputData(0) || white; - var texG = this.getInputData(1) || white; - var texB = this.getInputData(2) || white; - var texA = this.getInputData(3) || white; - - gl.disable(gl.BLEND); - gl.disable(gl.DEPTH_TEST); - - var mesh = Mesh.getScreenQuad(); - if (!LGraphChannelsTexture._shader) { - LGraphChannelsTexture._shader = new GL.Shader( - Shader.SCREEN_VERTEX_SHADER, - LGraphChannelsTexture.pixel_shader - ); - } - var shader = LGraphChannelsTexture._shader; - - var w = Math.max(texR.width, texG.width, texB.width, texA.width); - var h = Math.max( - texR.height, - texG.height, - texB.height, - texA.height - ); - var type = - this.properties.precision == LGraphTexture.HIGH - ? LGraphTexture.HIGH_PRECISION_FORMAT - : gl.UNSIGNED_BYTE; - - if ( - !this._texture || - this._texture.width != w || - this._texture.height != h || - this._texture.type != type - ) { - this._texture = new GL.Texture(w, h, { - type: type, - format: gl.RGBA, - filter: gl.LINEAR - }); - } + for(var i = 0; i < this._midi.tracks; ++i) { + var track = this._midi.track[i]; + track._last_pos = 0; + track._time = 0; + } + } - var color = this._color; - color[0] = this.properties.R; - color[1] = this.properties.G; - color[2] = this.properties.B; - color[3] = this.properties.A; - var uniforms = this._uniforms; - - this._texture.drawTo(function() { - texR.bind(0); - texG.bind(1); - texB.bind(2); - texA.bind(3); - shader.uniforms(uniforms).draw(mesh); - }); - this.setOutputData(0, this._texture); - }; + LGMIDIFromFile.prototype.loadMIDIFile = function(url) { + var that = this; + LiteGraph.fetchFile( url, "arraybuffer", function(data) { + that.boxcolor = "#AFA"; + that._midi = MidiParser.parse( new Uint8Array(data) ); + if(that.properties.autoplay) + that.play(); + }, function(err) { + that.boxcolor = "#FAA"; + that._midi = null; + }); + } - LGraphChannelsTexture.pixel_shader = - "precision highp float;\n\ - precision highp float;\n\ - varying vec2 v_coord;\n\ - uniform sampler2D u_textureR;\n\ - uniform sampler2D u_textureG;\n\ - uniform sampler2D u_textureB;\n\ - uniform sampler2D u_textureA;\n\ - uniform vec4 u_color;\n\ - \n\ - void main() {\n\ - gl_FragColor = u_color * vec4( \ - texture2D(u_textureR, v_coord).r,\ - texture2D(u_textureG, v_coord).r,\ - texture2D(u_textureB, v_coord).r,\ - texture2D(u_textureA, v_coord).r);\n\ - }\n\ - "; + LGMIDIFromFile.prototype.onDropFile = function(file) { + this.properties.url = ""; + this.loadMIDIFile( file ); + } - LiteGraph.registerNodeType( - "texture/channelsTexture", - LGraphChannelsTexture - ); + LiteGraph.registerNodeType("midi/fromFile", LGMIDIFromFile); - // Texture Color ***************************************** - function LGraphTextureColor() { - this.addOutput("Texture", "Texture"); - this._tex_color = vec4.create(); - this.properties = { - color: vec4.create(), - precision: LGraphTexture.DEFAULT - }; - } + function LGMIDIPlay() { + this.properties = { + volume: 0.5, + duration: 1, + }; + this.addInput("note", LiteGraph.ACTION); + this.addInput("volume", "number"); + this.addInput("duration", "number"); + this.addOutput("note", LiteGraph.EVENT); - LGraphTextureColor.title = "Color"; - LGraphTextureColor.desc = - "Generates a 1x1 texture with a constant color"; + if (typeof AudioSynth == "undefined") { + console.error("Audiosynth.js not included, LGMidiPlay requires that library"); + this.boxcolor = "red"; + } else { + var Synth = (this.synth = new AudioSynth()); + this.instrument = Synth.createInstrument("piano"); + } + } - LGraphTextureColor.widgets_info = { - precision: { widget: "combo", values: LGraphTexture.MODE_VALUES } - }; + LGMIDIPlay.title = "MIDI Play"; + LGMIDIPlay.desc = "Plays a MIDI note"; + LGMIDIPlay.color = MIDI_COLOR; - LGraphTextureColor.prototype.onDrawBackground = function(ctx) { - var c = this.properties.color; - ctx.fillStyle = - "rgb(" + - Math.floor(clamp(c[0], 0, 1) * 255) + - "," + - Math.floor(clamp(c[1], 0, 1) * 255) + - "," + - Math.floor(clamp(c[2], 0, 1) * 255) + - ")"; - if (this.flags.collapsed) { - this.boxcolor = ctx.fillStyle; - } else { - ctx.fillRect(0, 0, this.size[0], this.size[1]); - } - }; + LGMIDIPlay.prototype.onAction = function(event, midi_event) { + if (!midi_event || midi_event.constructor !== MIDIEvent) { + return; + } - LGraphTextureColor.prototype.onExecute = function() { - var type = - this.properties.precision == LGraphTexture.HIGH - ? LGraphTexture.HIGH_PRECISION_FORMAT - : gl.UNSIGNED_BYTE; - - if (!this._tex || this._tex.type != type) { - this._tex = new GL.Texture(1, 1, { - format: gl.RGBA, - type: type, - minFilter: gl.NEAREST - }); - } - var color = this.properties.color; - - if (this.inputs) { - for (var i = 0; i < this.inputs.length; i++) { - var input = this.inputs[i]; - var v = this.getInputData(i); - if (v === undefined) { - continue; - } - switch (input.name) { - case "RGB": - case "RGBA": - color.set(v); - break; - case "R": - color[0] = v; - break; - case "G": - color[1] = v; - break; - case "B": - color[2] = v; - break; - case "A": - color[3] = v; - break; - } - } - } + if (this.instrument && midi_event.data[0] == MIDIEvent.NOTEON) { + var note = midi_event.note; // C# + if (!note || note == "undefined" || note.constructor !== String) { + return; + } + this.instrument.play( + note, + midi_event.octave, + this.properties.duration, + this.properties.volume, + ); + } + this.trigger("note", midi_event); + }; - if (vec4.sqrDist(this._tex_color, color) > 0.001) { - this._tex_color.set(color); - this._tex.fill(color); - } - this.setOutputData(0, this._tex); - }; + LGMIDIPlay.prototype.onExecute = function() { + var volume = this.getInputData(1); + if (volume != null) { + this.properties.volume = volume; + } - LGraphTextureColor.prototype.onGetInputs = function() { - return [ - ["RGB", "vec3"], - ["RGBA", "vec4"], - ["R", "number"], - ["G", "number"], - ["B", "number"], - ["A", "number"] - ]; - }; + var duration = this.getInputData(2); + if (duration != null) { + this.properties.duration = duration; + } + }; - LiteGraph.registerNodeType("texture/color", LGraphTextureColor); + LiteGraph.registerNodeType("midi/play", LGMIDIPlay); - // Texture Channels to Texture ***************************************** - function LGraphTextureGradient() { - this.addInput("A", "color"); - this.addInput("B", "color"); - this.addOutput("Texture", "Texture"); + function LGMIDIKeys() { + this.properties = { + num_octaves: 2, + start_octave: 2, + }; + this.addInput("note", LiteGraph.ACTION); + this.addInput("reset", LiteGraph.ACTION); + this.addOutput("note", LiteGraph.EVENT); + this.size = [400, 100]; + this.keys = []; + this._last_key = -1; + } - this.properties = { - angle: 0, - scale: 1, - A: [0, 0, 0], - B: [1, 1, 1], - texture_size: 32 - }; - if (!LGraphTextureGradient._shader) { - LGraphTextureGradient._shader = new GL.Shader( - Shader.SCREEN_VERTEX_SHADER, - LGraphTextureGradient.pixel_shader - ); - } + LGMIDIKeys.title = "MIDI Keys"; + LGMIDIKeys.desc = "Keyboard to play notes"; + LGMIDIKeys.color = MIDI_COLOR; + + LGMIDIKeys.keys = [ + { x: 0, w: 1, h: 1, t: 0 }, + { x: 0.75, w: 0.5, h: 0.6, t: 1 }, + { x: 1, w: 1, h: 1, t: 0 }, + { x: 1.75, w: 0.5, h: 0.6, t: 1 }, + { x: 2, w: 1, h: 1, t: 0 }, + { x: 2.75, w: 0.5, h: 0.6, t: 1 }, + { x: 3, w: 1, h: 1, t: 0 }, + { x: 4, w: 1, h: 1, t: 0 }, + { x: 4.75, w: 0.5, h: 0.6, t: 1 }, + { x: 5, w: 1, h: 1, t: 0 }, + { x: 5.75, w: 0.5, h: 0.6, t: 1 }, + { x: 6, w: 1, h: 1, t: 0 }, + ]; - this._uniforms = { - u_angle: 0, - u_colorA: vec3.create(), - u_colorB: vec3.create() - }; - } + LGMIDIKeys.prototype.onDrawForeground = function(ctx) { + if (this.flags.collapsed) { + return; + } - LGraphTextureGradient.title = "Gradient"; - LGraphTextureGradient.desc = "Generates a gradient"; - LGraphTextureGradient["@A"] = { type: "color" }; - LGraphTextureGradient["@B"] = { type: "color" }; - LGraphTextureGradient["@texture_size"] = { - type: "enum", - values: [32, 64, 128, 256, 512] - }; + var num_keys = this.properties.num_octaves * 12; + this.keys.length = num_keys; + var key_width = this.size[0] / (this.properties.num_octaves * 7); + var key_height = this.size[1]; - LGraphTextureGradient.prototype.onExecute = function() { - gl.disable(gl.BLEND); - gl.disable(gl.DEPTH_TEST); + ctx.globalAlpha = 1; - var mesh = GL.Mesh.getScreenQuad(); - var shader = LGraphTextureGradient._shader; + for ( + var k = 0; + k < 2; + k++ // draw first whites (0) then blacks (1) + ) { + for (var i = 0; i < num_keys; ++i) { + var key_info = LGMIDIKeys.keys[i % 12]; + if (key_info.t != k) { + continue; + } + var octave = Math.floor(i / 12); + var x = octave * 7 * key_width + key_info.x * key_width; + if (k == 0) { + ctx.fillStyle = this.keys[i] ? "#CCC" : "white"; + } else { + ctx.fillStyle = this.keys[i] ? "#333" : "black"; + } + ctx.fillRect( + x + 1, + 0, + key_width * key_info.w - 2, + key_height * key_info.h, + ); + } + } + }; - var A = this.getInputData(0); - if (!A) { - A = this.properties.A; - } - var B = this.getInputData(1); - if (!B) { - B = this.properties.B; - } + LGMIDIKeys.prototype.getKeyIndex = function(pos) { + var num_keys = this.properties.num_octaves * 12; + var key_width = this.size[0] / (this.properties.num_octaves * 7); + var key_height = this.size[1]; - //angle and scale - for (var i = 2; i < this.inputs.length; i++) { - var input = this.inputs[i]; - var v = this.getInputData(i); - if (v === undefined) { - continue; - } - this.properties[input.name] = v; - } + for ( + var k = 1; + k >= 0; + k-- // test blacks first (1) then whites (0) + ) { + for (var i = 0; i < this.keys.length; ++i) { + var key_info = LGMIDIKeys.keys[i % 12]; + if (key_info.t != k) { + continue; + } + var octave = Math.floor(i / 12); + var x = octave * 7 * key_width + key_info.x * key_width; + var w = key_width * key_info.w; + var h = key_height * key_info.h; + if (pos[0] < x || pos[0] > x + w || pos[1] > h) { + continue; + } + return i; + } + } + return -1; + }; - var uniforms = this._uniforms; - this._uniforms.u_angle = this.properties.angle * DEG2RAD; - this._uniforms.u_scale = this.properties.scale; - vec3.copy(uniforms.u_colorA, A); - vec3.copy(uniforms.u_colorB, B); - - var size = parseInt(this.properties.texture_size); - if (!this._tex || this._tex.width != size) { - this._tex = new GL.Texture(size, size, { - format: gl.RGB, - filter: gl.LINEAR - }); - } + LGMIDIKeys.prototype.onAction = function(event, params) { + if (event == "reset") { + for (var i = 0; i < this.keys.length; ++i) { + this.keys[i] = false; + } + return; + } - this._tex.drawTo(function() { - shader.uniforms(uniforms).draw(mesh); - }); - this.setOutputData(0, this._tex); - }; + if (!params || params.constructor !== MIDIEvent) { + return; + } + var midi_event = params; + var start_note = (this.properties.start_octave - 1) * 12 + 29; + var index = midi_event.data[1] - start_note; + if (index >= 0 && index < this.keys.length) { + if (midi_event.data[0] == MIDIEvent.NOTEON) { + this.keys[index] = true; + } else if (midi_event.data[0] == MIDIEvent.NOTEOFF) { + this.keys[index] = false; + } + } - LGraphTextureGradient.prototype.onGetInputs = function() { - return [["angle", "number"], ["scale", "number"]]; - }; + this.trigger("note", midi_event); + }; - LGraphTextureGradient.pixel_shader = - "precision highp float;\n\ - precision highp float;\n\ - varying vec2 v_coord;\n\ - uniform float u_angle;\n\ - uniform float u_scale;\n\ - uniform vec3 u_colorA;\n\ - uniform vec3 u_colorB;\n\ - \n\ - vec2 rotate(vec2 v, float angle)\n\ - {\n\ - vec2 result;\n\ - float _cos = cos(angle);\n\ - float _sin = sin(angle);\n\ - result.x = v.x * _cos - v.y * _sin;\n\ - result.y = v.x * _sin + v.y * _cos;\n\ - return result;\n\ - }\n\ - void main() {\n\ - float f = (rotate(u_scale * (v_coord - vec2(0.5)), u_angle) + vec2(0.5)).x;\n\ - vec3 color = mix(u_colorA,u_colorB,clamp(f,0.0,1.0));\n\ - gl_FragColor = vec4(color,1.0);\n\ - }\n\ - "; + LGMIDIKeys.prototype.onMouseDown = function(e, pos) { + if (pos[1] < 0) { + return; + } + var index = this.getKeyIndex(pos); + this.keys[index] = true; + this._last_key = index; + var pitch = (this.properties.start_octave - 1) * 12 + 29 + index; + var midi_event = new MIDIEvent(); + midi_event.setup([MIDIEvent.NOTEON, pitch, 100]); + this.trigger("note", midi_event); + return true; + }; - LiteGraph.registerNodeType("texture/gradient", LGraphTextureGradient); - - // Texture Mix ***************************************** - function LGraphTextureMix() { - this.addInput("A", "Texture"); - this.addInput("B", "Texture"); - this.addInput("Mixer", "Texture"); - - this.addOutput("Texture", "Texture"); - this.properties = { factor: 0.5, size_from_biggest: true, invert: false, precision: LGraphTexture.DEFAULT }; - this._uniforms = { - u_textureA: 0, - u_textureB: 1, - u_textureMix: 2, - u_mix: vec4.create() - }; - } + LGMIDIKeys.prototype.onMouseMove = function(e, pos) { + if (pos[1] < 0 || this._last_key == -1) { + return; + } + this.setDirtyCanvas(true); + var index = this.getKeyIndex(pos); + if (this._last_key == index) { + return true; + } + this.keys[this._last_key] = false; + var pitch = + (this.properties.start_octave - 1) * 12 + 29 + this._last_key; + var midi_event = new MIDIEvent(); + midi_event.setup([MIDIEvent.NOTEOFF, pitch, 100]); + this.trigger("note", midi_event); - LGraphTextureMix.title = "Mix"; - LGraphTextureMix.desc = "Generates a texture mixing two textures"; + this.keys[index] = true; + var pitch = (this.properties.start_octave - 1) * 12 + 29 + index; + var midi_event = new MIDIEvent(); + midi_event.setup([MIDIEvent.NOTEON, pitch, 100]); + this.trigger("note", midi_event); - LGraphTextureMix.widgets_info = { - precision: { widget: "combo", values: LGraphTexture.MODE_VALUES } - }; + this._last_key = index; + return true; + }; - LGraphTextureMix.prototype.onExecute = function() { - var texA = this.getInputData(0); + LGMIDIKeys.prototype.onMouseUp = function(e, pos) { + if (pos[1] < 0) { + return; + } + var index = this.getKeyIndex(pos); + this.keys[index] = false; + this._last_key = -1; + var pitch = (this.properties.start_octave - 1) * 12 + 29 + index; + var midi_event = new MIDIEvent(); + midi_event.setup([MIDIEvent.NOTEOFF, pitch, 100]); + this.trigger("note", midi_event); + return true; + }; - if (!this.isOutputConnected(0)) { - return; - } //saves work + LiteGraph.registerNodeType("midi/keys", LGMIDIKeys); - if (this.properties.precision === LGraphTexture.PASS_THROUGH) { - this.setOutputData(0, texA); - return; - } + function now() { + return window.performance.now(); + } +})(this); - var texB = this.getInputData(1); - if (!texA || !texB) { - return; - } +(function(global) { + var LiteGraph = global.LiteGraph; - var texMix = this.getInputData(2); + var LGAudio = {}; + global.LGAudio = LGAudio; - var factor = this.getInputData(3); + LGAudio.getAudioContext = function() { + if (!this._audio_context) { + window.AudioContext = + window.AudioContext || window.webkitAudioContext; + if (!window.AudioContext) { + console.error("AudioContext not supported by browser"); + return null; + } + this._audio_context = new AudioContext(); + this._audio_context.onmessage = function(msg) { + console.log("msg", msg); + }; + this._audio_context.onended = function(msg) { + console.log("ended", msg); + }; + this._audio_context.oncomplete = function(msg) { + console.log("complete", msg); + }; + } - this._tex = LGraphTexture.getTargetTexture( - this.properties.size_from_biggest && texB.width > texA.width ? texB : texA, - this._tex, - this.properties.precision - ); + // in case it crashes + // if(this._audio_context.state == "suspended") + // this._audio_context.resume(); + return this._audio_context; + }; - gl.disable(gl.BLEND); - gl.disable(gl.DEPTH_TEST); + LGAudio.connect = function(audionodeA, audionodeB) { + try { + audionodeA.connect(audionodeB); + } catch (err) { + console.warn("LGraphAudio:", err); + } + }; - var mesh = Mesh.getScreenQuad(); - var shader = null; - var uniforms = this._uniforms; - if (texMix) { - shader = LGraphTextureMix._shader_tex; - if (!shader) { - shader = LGraphTextureMix._shader_tex = new GL.Shader( - Shader.SCREEN_VERTEX_SHADER, - LGraphTextureMix.pixel_shader, - { MIX_TEX: "" } - ); - } - } else { - shader = LGraphTextureMix._shader_factor; - if (!shader) { - shader = LGraphTextureMix._shader_factor = new GL.Shader( - Shader.SCREEN_VERTEX_SHADER, - LGraphTextureMix.pixel_shader - ); - } - var f = factor == null ? this.properties.factor : factor; - uniforms.u_mix.set([f, f, f, f]); - } + LGAudio.disconnect = function(audionodeA, audionodeB) { + try { + audionodeA.disconnect(audionodeB); + } catch (err) { + console.warn("LGraphAudio:", err); + } + }; - var invert = this.properties.invert; + LGAudio.changeAllAudiosConnections = function(node, connect) { + if (node.inputs) { + for (var i = 0; i < node.inputs.length; ++i) { + var input = node.inputs[i]; + var link_info = node.graph.links[input.link]; + if (!link_info) { + continue; + } - this._tex.drawTo(function() { - texA.bind( invert ? 1 : 0 ); - texB.bind( invert ? 0 : 1 ); - if (texMix) { - texMix.bind(2); - } - shader.uniforms(uniforms).draw(mesh); - }); + var origin_node = node.graph.getNodeById(link_info.origin_id); + var origin_audionode = null; + if (origin_node.getAudioNodeInOutputSlot) { + origin_audionode = origin_node.getAudioNodeInOutputSlot(link_info.origin_slot); + } else { + origin_audionode = origin_node.audionode; + } - this.setOutputData(0, this._tex); - }; + var target_audionode = null; + if (node.getAudioNodeInInputSlot) { + target_audionode = node.getAudioNodeInInputSlot(i); + } else { + target_audionode = node.audionode; + } - LGraphTextureMix.prototype.onGetInputs = function() { - return [["factor", "number"]]; - }; + if (connect) { + LGAudio.connect(origin_audionode, target_audionode); + } else { + LGAudio.disconnect(origin_audionode, target_audionode); + } + } + } - LGraphTextureMix.pixel_shader = - "precision highp float;\n\ - precision highp float;\n\ - varying vec2 v_coord;\n\ - uniform sampler2D u_textureA;\n\ - uniform sampler2D u_textureB;\n\ - #ifdef MIX_TEX\n\ - uniform sampler2D u_textureMix;\n\ - #else\n\ - uniform vec4 u_mix;\n\ - #endif\n\ - \n\ - void main() {\n\ - #ifdef MIX_TEX\n\ - vec4 f = texture2D(u_textureMix, v_coord);\n\ - #else\n\ - vec4 f = u_mix;\n\ - #endif\n\ - gl_FragColor = mix( texture2D(u_textureA, v_coord), texture2D(u_textureB, v_coord), f );\n\ - }\n\ - "; + if (node.outputs) { + for (var i = 0; i < node.outputs.length; ++i) { + var output = node.outputs[i]; + for (var j = 0; j < output.links.length; ++j) { + var link_info = node.graph.links[output.links[j]]; + if (!link_info) { + continue; + } - LiteGraph.registerNodeType("texture/mix", LGraphTextureMix); + var origin_audionode = null; + if (node.getAudioNodeInOutputSlot) { + origin_audionode = node.getAudioNodeInOutputSlot(i); + } else { + origin_audionode = node.audionode; + } - // Texture Edges detection ***************************************** - function LGraphTextureEdges() { - this.addInput("Tex.", "Texture"); + var target_node = node.graph.getNodeById(link_info.target_id); + var target_audionode = null; + if (target_node.getAudioNodeInInputSlot) { + target_audionode = target_node.getAudioNodeInInputSlot(link_info.target_slot); + } else { + target_audionode = target_node.audionode; + } - this.addOutput("Edges", "Texture"); - this.properties = { - invert: true, - threshold: false, - factor: 1, - precision: LGraphTexture.DEFAULT - }; + if (connect) { + LGAudio.connect(origin_audionode, target_audionode); + } else { + LGAudio.disconnect(origin_audionode, target_audionode); + } + } + } + } + }; - if (!LGraphTextureEdges._shader) { - LGraphTextureEdges._shader = new GL.Shader( - Shader.SCREEN_VERTEX_SHADER, - LGraphTextureEdges.pixel_shader - ); - } - } + // used by many nodes + LGAudio.onConnectionsChange = function( + connection, + slot, + connected, + link_info, + ) { + // only process the outputs events + if (connection != LiteGraph.OUTPUT) { + return; + } - LGraphTextureEdges.title = "Edges"; - LGraphTextureEdges.desc = "Detects edges"; + var target_node = null; + if (link_info) { + target_node = this.graph.getNodeById(link_info.target_id); + } - LGraphTextureEdges.widgets_info = { - precision: { widget: "combo", values: LGraphTexture.MODE_VALUES } - }; + if (!target_node) { + return; + } - LGraphTextureEdges.prototype.onExecute = function() { - if (!this.isOutputConnected(0)) { - return; - } //saves work + // get origin audionode + var local_audionode = null; + if (this.getAudioNodeInOutputSlot) { + local_audionode = this.getAudioNodeInOutputSlot(slot); + } else { + local_audionode = this.audionode; + } - var tex = this.getInputData(0); + // get target audionode + var target_audionode = null; + if (target_node.getAudioNodeInInputSlot) { + target_audionode = target_node.getAudioNodeInInputSlot(link_info.target_slot); + } else { + target_audionode = target_node.audionode; + } - if (this.properties.precision === LGraphTexture.PASS_THROUGH) { - this.setOutputData(0, tex); - return; - } + // do the connection/disconnection + if (connected) { + LGAudio.connect(local_audionode, target_audionode); + } else { + LGAudio.disconnect(local_audionode, target_audionode); + } + }; - if (!tex) { - return; - } + // this function helps creating wrappers to existing classes + LGAudio.createAudioNodeWrapper = function(class_object) { + var old_func = class_object.prototype.onPropertyChanged; - this._tex = LGraphTexture.getTargetTexture( - tex, - this._tex, - this.properties.precision - ); + class_object.prototype.onPropertyChanged = function(name, value) { + if (old_func) { + old_func.call(this, name, value); + } - gl.disable(gl.BLEND); - gl.disable(gl.DEPTH_TEST); + if (!this.audionode) { + return; + } - var mesh = Mesh.getScreenQuad(); - var shader = LGraphTextureEdges._shader; - var invert = this.properties.invert; - var factor = this.properties.factor; - var threshold = this.properties.threshold ? 1 : 0; + if (this.audionode[name] === undefined) { + return; + } - this._tex.drawTo(function() { - tex.bind(0); - shader - .uniforms({ - u_texture: 0, - u_isize: [1 / tex.width, 1 / tex.height], - u_factor: factor, - u_threshold: threshold, - u_invert: invert ? 1 : 0 - }) - .draw(mesh); - }); + if (this.audionode[name].value !== undefined) { + this.audionode[name].value = value; + } else { + this.audionode[name] = value; + } + }; - this.setOutputData(0, this._tex); - }; + class_object.prototype.onConnectionsChange = + LGAudio.onConnectionsChange; + }; - LGraphTextureEdges.pixel_shader = - "precision highp float;\n\ - precision highp float;\n\ - varying vec2 v_coord;\n\ - uniform sampler2D u_texture;\n\ - uniform vec2 u_isize;\n\ - uniform int u_invert;\n\ - uniform float u_factor;\n\ - uniform float u_threshold;\n\ - \n\ - void main() {\n\ - vec4 center = texture2D(u_texture, v_coord);\n\ - vec4 up = texture2D(u_texture, v_coord + u_isize * vec2(0.0,1.0) );\n\ - vec4 down = texture2D(u_texture, v_coord + u_isize * vec2(0.0,-1.0) );\n\ - vec4 left = texture2D(u_texture, v_coord + u_isize * vec2(1.0,0.0) );\n\ - vec4 right = texture2D(u_texture, v_coord + u_isize * vec2(-1.0,0.0) );\n\ - vec4 diff = abs(center - up) + abs(center - down) + abs(center - left) + abs(center - right);\n\ - diff *= u_factor;\n\ - if(u_invert == 1)\n\ - diff.xyz = vec3(1.0) - diff.xyz;\n\ - if( u_threshold == 0.0 )\n\ - gl_FragColor = vec4( diff.xyz, center.a );\n\ - else\n\ - gl_FragColor = vec4( diff.x > 0.5 ? 1.0 : 0.0, diff.y > 0.5 ? 1.0 : 0.0, diff.z > 0.5 ? 1.0 : 0.0, center.a );\n\ - }\n\ - "; + // contains the samples decoded of the loaded audios in AudioBuffer format + LGAudio.cached_audios = {}; - LiteGraph.registerNodeType("texture/edges", LGraphTextureEdges); + LGAudio.loadSound = function(url, on_complete, on_error) { + if (LGAudio.cached_audios[url] && url.indexOf("blob:") == -1) { + if (on_complete) { + on_complete(LGAudio.cached_audios[url]); + } + return; + } - // Texture Depth ***************************************** - function LGraphTextureDepthRange() { - this.addInput("Texture", "Texture"); - this.addInput("Distance", "number"); - this.addInput("Range", "number"); - this.addOutput("Texture", "Texture"); - this.properties = { - distance: 100, - range: 50, - only_depth: false, - high_precision: false - }; - this._uniforms = { - u_texture: 0, - u_distance: 100, - u_range: 50, - u_camera_planes: null - }; - } + if (LGAudio.onProcessAudioURL) { + url = LGAudio.onProcessAudioURL(url); + } - LGraphTextureDepthRange.title = "Depth Range"; - LGraphTextureDepthRange.desc = "Generates a texture with a depth range"; + // load new sample + var request = new XMLHttpRequest(); + request.open("GET", url, true); + request.responseType = "arraybuffer"; - LGraphTextureDepthRange.prototype.onExecute = function() { - if (!this.isOutputConnected(0)) { - return; - } //saves work + var context = LGAudio.getAudioContext(); - var tex = this.getInputData(0); - if (!tex) { - return; - } + // Decode asynchronously + request.onload = function() { + console.log("AudioSource loaded"); + context.decodeAudioData( + request.response, + function(buffer) { + console.log("AudioSource decoded"); + LGAudio.cached_audios[url] = buffer; + if (on_complete) { + on_complete(buffer); + } + }, + onError, + ); + }; + request.send(); - var precision = gl.UNSIGNED_BYTE; - if (this.properties.high_precision) { - precision = gl.half_float_ext ? gl.HALF_FLOAT_OES : gl.FLOAT; - } + function onError(err) { + console.log("Audio loading sample error:", err); + if (on_error) { + on_error(err); + } + } - if ( - !this._temp_texture || - this._temp_texture.type != precision || - this._temp_texture.width != tex.width || - this._temp_texture.height != tex.height - ) { - this._temp_texture = new GL.Texture(tex.width, tex.height, { - type: precision, - format: gl.RGBA, - filter: gl.LINEAR - }); - } + return request; + }; - var uniforms = this._uniforms; + //* *************************************************** - //iterations - var distance = this.properties.distance; - if (this.isInputConnected(1)) { - distance = this.getInputData(1); - this.properties.distance = distance; - } + function LGAudioSource() { + this.properties = { + src: "", + gain: 0.5, + loop: true, + autoplay: true, + playbackRate: 1, + }; - var range = this.properties.range; - if (this.isInputConnected(2)) { - range = this.getInputData(2); - this.properties.range = range; - } + this._loading_audio = false; + this._audiobuffer = null; // points to AudioBuffer with the audio samples decoded + this._audionodes = []; + this._last_sourcenode = null; // the last AudioBufferSourceNode (there could be more if there are several sounds playing) - uniforms.u_distance = distance; - uniforms.u_range = range; - - gl.disable(gl.BLEND); - gl.disable(gl.DEPTH_TEST); - var mesh = Mesh.getScreenQuad(); - if (!LGraphTextureDepthRange._shader) { - LGraphTextureDepthRange._shader = new GL.Shader( - Shader.SCREEN_VERTEX_SHADER, - LGraphTextureDepthRange.pixel_shader - ); - LGraphTextureDepthRange._shader_onlydepth = new GL.Shader( - Shader.SCREEN_VERTEX_SHADER, - LGraphTextureDepthRange.pixel_shader, - { ONLY_DEPTH: "" } - ); - } - var shader = this.properties.only_depth - ? LGraphTextureDepthRange._shader_onlydepth - : LGraphTextureDepthRange._shader; - - //NEAR AND FAR PLANES - var planes = null; - if (tex.near_far_planes) { - planes = tex.near_far_planes; - } else if (window.LS && LS.Renderer._main_camera) { - planes = LS.Renderer._main_camera._uniforms.u_camera_planes; - } else { - planes = [0.1, 1000]; - } //hardcoded - uniforms.u_camera_planes = planes; - - this._temp_texture.drawTo(function() { - tex.bind(0); - shader.uniforms(uniforms).draw(mesh); - }); + this.addOutput("out", "audio"); + this.addInput("gain", "number"); - this._temp_texture.near_far_planes = planes; - this.setOutputData(0, this._temp_texture); - }; + // init context + var context = LGAudio.getAudioContext(); - LGraphTextureDepthRange.pixel_shader = - "precision highp float;\n\ - precision highp float;\n\ - varying vec2 v_coord;\n\ - uniform sampler2D u_texture;\n\ - uniform vec2 u_camera_planes;\n\ - uniform float u_distance;\n\ - uniform float u_range;\n\ - \n\ - float LinearDepth()\n\ - {\n\ - float zNear = u_camera_planes.x;\n\ - float zFar = u_camera_planes.y;\n\ - float depth = texture2D(u_texture, v_coord).x;\n\ - depth = depth * 2.0 - 1.0;\n\ - return zNear * (depth + 1.0) / (zFar + zNear - depth * (zFar - zNear));\n\ - }\n\ - \n\ - void main() {\n\ - float depth = LinearDepth();\n\ - #ifdef ONLY_DEPTH\n\ - gl_FragColor = vec4(depth);\n\ - #else\n\ - float diff = abs(depth * u_camera_planes.y - u_distance);\n\ - float dof = 1.0;\n\ - if(diff <= u_range)\n\ - dof = diff / u_range;\n\ - gl_FragColor = vec4(dof);\n\ - #endif\n\ - }\n\ - "; + // create gain node to control volume + this.audionode = context.createGain(); + this.audionode.graphnode = this; + this.audionode.gain.value = this.properties.gain; - LiteGraph.registerNodeType( "texture/depth_range", LGraphTextureDepthRange ); + // debug + if (this.properties.src) { + this.loadSound(this.properties.src); + } + } + LGAudioSource.desc = "Plays an audio file"; + LGAudioSource["@src"] = { widget: "resource" }; + LGAudioSource.supported_extensions = ["wav", "ogg", "mp3"]; - // Texture Depth ***************************************** - function LGraphTextureLinearDepth() { - this.addInput("Texture", "Texture"); - this.addOutput("Texture", "Texture"); - this.properties = { - precision: LGraphTexture.DEFAULT, - invert: false - }; - this._uniforms = { - u_texture: 0, - u_camera_planes: null, //filled later - u_ires: vec2.create() - }; - } + LGAudioSource.prototype.onAdded = function(graph) { + if (graph.status === LGraph.STATUS_RUNNING) { + this.onStart(); + } + }; - LGraphTextureLinearDepth.widgets_info = { - precision: { widget: "combo", values: LGraphTexture.MODE_VALUES } - }; + LGAudioSource.prototype.onStart = function() { + if (!this._audiobuffer) { + return; + } - LGraphTextureLinearDepth.title = "Linear Depth"; - LGraphTextureLinearDepth.desc = "Creates a color texture with linear depth"; + if (this.properties.autoplay) { + this.playBuffer(this._audiobuffer); + } + }; - LGraphTextureLinearDepth.prototype.onExecute = function() { - if (!this.isOutputConnected(0)) { - return; - } //saves work + LGAudioSource.prototype.onStop = function() { + this.stopAllSounds(); + }; - var tex = this.getInputData(0); - if (!tex || (tex.format != gl.DEPTH_COMPONENT && tex.format != gl.DEPTH_STENCIL) ) { - return; - } + LGAudioSource.prototype.onPause = function() { + this.pauseAllSounds(); + }; - var precision = this.properties.precision == LGraphTexture.HIGH ? gl.HIGH_PRECISION_FORMAT : gl.UNSIGNED_BYTE; + LGAudioSource.prototype.onUnpause = function() { + this.unpauseAllSounds(); + // this.onStart(); + }; - if ( !this._temp_texture || this._temp_texture.type != precision || this._temp_texture.width != tex.width || this._temp_texture.height != tex.height ) { - this._temp_texture = new GL.Texture(tex.width, tex.height, { - type: precision, - format: gl.RGB, - filter: gl.LINEAR - }); - } + LGAudioSource.prototype.onRemoved = function() { + this.stopAllSounds(); + if (this._dropped_url) { + URL.revokeObjectURL(this._url); + } + }; - var uniforms = this._uniforms; - uniforms.u_invert = this.properties.invert ? 1 : 0; - - gl.disable(gl.BLEND); - gl.disable(gl.DEPTH_TEST); - var mesh = Mesh.getScreenQuad(); - if(!LGraphTextureLinearDepth._shader) - LGraphTextureLinearDepth._shader = new GL.Shader( GL.Shader.SCREEN_VERTEX_SHADER, LGraphTextureLinearDepth.pixel_shader); - var shader = LGraphTextureLinearDepth._shader; - - //NEAR AND FAR PLANES - var planes = null; - if (tex.near_far_planes) { - planes = tex.near_far_planes; - } else if (window.LS && LS.Renderer._main_camera) { - planes = LS.Renderer._main_camera._uniforms.u_camera_planes; - } else { - planes = [0.1, 1000]; - } //hardcoded - uniforms.u_camera_planes = planes; - //uniforms.u_ires.set([1/tex.width, 1/tex.height]); - uniforms.u_ires.set([0,0]); - - this._temp_texture.drawTo(function() { - tex.bind(0); - shader.uniforms(uniforms).draw(mesh); - }); + LGAudioSource.prototype.stopAllSounds = function() { + // iterate and stop + for (var i = 0; i < this._audionodes.length; ++i) { + if (this._audionodes[i].started) { + this._audionodes[i].started = false; + this._audionodes[i].stop(); + } + // this._audionodes[i].disconnect( this.audionode ); + } + this._audionodes.length = 0; + }; - this._temp_texture.near_far_planes = planes; - this.setOutputData(0, this._temp_texture); - }; + LGAudioSource.prototype.pauseAllSounds = function() { + LGAudio.getAudioContext().suspend(); + }; - LGraphTextureLinearDepth.pixel_shader = - "precision highp float;\n\ - precision highp float;\n\ - varying vec2 v_coord;\n\ - uniform sampler2D u_texture;\n\ - uniform vec2 u_camera_planes;\n\ - uniform int u_invert;\n\ - uniform vec2 u_ires;\n\ - \n\ - void main() {\n\ - float zNear = u_camera_planes.x;\n\ - float zFar = u_camera_planes.y;\n\ - float depth = texture2D(u_texture, v_coord + u_ires*0.5).x * 2.0 - 1.0;\n\ - float f = zNear * (depth + 1.0) / (zFar + zNear - depth * (zFar - zNear));\n\ - if( u_invert == 1 )\n\ - f = 1.0 - f;\n\ - gl_FragColor = vec4(vec3(f),1.0);\n\ - }\n\ - "; + LGAudioSource.prototype.unpauseAllSounds = function() { + LGAudio.getAudioContext().resume(); + }; - LiteGraph.registerNodeType( "texture/linear_depth", LGraphTextureLinearDepth ); + LGAudioSource.prototype.onExecute = function() { + if (this.inputs) { + for (var i = 0; i < this.inputs.length; ++i) { + var input = this.inputs[i]; + if (input.link == null) { + continue; + } + var v = this.getInputData(i); + if (v === undefined) { + continue; + } + if (input.name == "gain") + this.audionode.gain.value = v; + else if (input.name == "src") { + this.setProperty("src",v); + } else if (input.name == "playbackRate") { + this.properties.playbackRate = v; + for (var j = 0; j < this._audionodes.length; ++j) { + this._audionodes[j].playbackRate.value = v; + } + } + } + } - // Texture Blur ***************************************** - function LGraphTextureBlur() { - this.addInput("Texture", "Texture"); - this.addInput("Iterations", "number"); - this.addInput("Intensity", "number"); - this.addOutput("Blurred", "Texture"); - this.properties = { - intensity: 1, - iterations: 1, - preserve_aspect: false, - scale: [1, 1], - precision: LGraphTexture.DEFAULT - }; - } + if (this.outputs) { + for (var i = 0; i < this.outputs.length; ++i) { + var output = this.outputs[i]; + if (output.name == "buffer" && this._audiobuffer) { + this.setOutputData(i, this._audiobuffer); + } + } + } + }; - LGraphTextureBlur.title = "Blur"; - LGraphTextureBlur.desc = "Blur a texture"; + LGAudioSource.prototype.onAction = function(event) { + if (this._audiobuffer) { + if (event == "Play") { + this.playBuffer(this._audiobuffer); + } else if (event == "Stop") { + this.stopAllSounds(); + } + } + }; - LGraphTextureBlur.widgets_info = { - precision: { widget: "combo", values: LGraphTexture.MODE_VALUES } - }; + LGAudioSource.prototype.onPropertyChanged = function(name, value) { + if (name == "src") { + this.loadSound(value); + } else if (name == "gain") { + this.audionode.gain.value = value; + } else if (name == "playbackRate") { + for (var j = 0; j < this._audionodes.length; ++j) { + this._audionodes[j].playbackRate.value = value; + } + } + }; - LGraphTextureBlur.max_iterations = 20; + LGAudioSource.prototype.playBuffer = function(buffer) { + var that = this; + var context = LGAudio.getAudioContext(); + + // create a new audionode (this is mandatory, AudioAPI doesnt like to reuse old ones) + var audionode = context.createBufferSource(); // create a AudioBufferSourceNode + this._last_sourcenode = audionode; + audionode.graphnode = this; + audionode.buffer = buffer; + audionode.loop = this.properties.loop; + audionode.playbackRate.value = this.properties.playbackRate; + this._audionodes.push(audionode); + audionode.connect(this.audionode); // connect to gain + + this._audionodes.push(audionode); + + this.trigger("start"); + + audionode.onended = function() { + // console.log("ended!"); + that.trigger("ended"); + // remove + var index = that._audionodes.indexOf(audionode); + if (index != -1) { + that._audionodes.splice(index, 1); + } + }; - LGraphTextureBlur.prototype.onExecute = function() { - var tex = this.getInputData(0); - if (!tex) { - return; - } + if (!audionode.started) { + audionode.started = true; + audionode.start(); + } + return audionode; + }; - if (!this.isOutputConnected(0)) { - return; - } //saves work + LGAudioSource.prototype.loadSound = function(url) { + var that = this; - var temp = this._final_texture; + // kill previous load + if (this._request) { + this._request.abort(); + this._request = null; + } - if ( - !temp || - temp.width != tex.width || - temp.height != tex.height || - temp.type != tex.type - ) { - //we need two textures to do the blurring - //this._temp_texture = new GL.Texture( tex.width, tex.height, { type: tex.type, format: gl.RGBA, filter: gl.LINEAR }); - temp = this._final_texture = new GL.Texture( - tex.width, - tex.height, - { type: tex.type, format: gl.RGBA, filter: gl.LINEAR } - ); - } + this._audiobuffer = null; // points to the audiobuffer once the audio is loaded + this._loading_audio = false; - //iterations - var iterations = this.properties.iterations; - if (this.isInputConnected(1)) { - iterations = this.getInputData(1); - this.properties.iterations = iterations; - } - iterations = Math.min( - Math.floor(iterations), - LGraphTextureBlur.max_iterations - ); - if (iterations == 0) { - //skip blurring - this.setOutputData(0, tex); - return; - } + if (!url) { + return; + } - var intensity = this.properties.intensity; - if (this.isInputConnected(2)) { - intensity = this.getInputData(2); - this.properties.intensity = intensity; - } + this._request = LGAudio.loadSound(url, inner); - //blur sometimes needs an aspect correction - var aspect = LiteGraph.camera_aspect; - if (!aspect && window.gl !== undefined) { - aspect = gl.canvas.height / gl.canvas.width; - } - if (!aspect) { - aspect = 1; - } - aspect = this.properties.preserve_aspect ? aspect : 1; - - var scale = this.properties.scale || [1, 1]; - tex.applyBlur(aspect * scale[0], scale[1], intensity, temp); - for (var i = 1; i < iterations; ++i) { - temp.applyBlur( - aspect * scale[0] * (i + 1), - scale[1] * (i + 1), - intensity - ); - } + this._loading_audio = true; + this.boxcolor = "#AA4"; - this.setOutputData(0, temp); - }; + function inner(buffer) { + this.boxcolor = LiteGraph.NODE_DEFAULT_BOXCOLOR; + that._audiobuffer = buffer; + that._loading_audio = false; + // if is playing, then play it + if (that.graph && that.graph.status === LGraph.STATUS_RUNNING) { + that.onStart(); + } // this controls the autoplay already + } + }; - /* -LGraphTextureBlur.pixel_shader = "precision highp float;\n\ - precision highp float;\n\ - varying vec2 v_coord;\n\ - uniform sampler2D u_texture;\n\ - uniform vec2 u_offset;\n\ - uniform float u_intensity;\n\ - void main() {\n\ - vec4 sum = vec4(0.0);\n\ - vec4 center = texture2D(u_texture, v_coord);\n\ - sum += texture2D(u_texture, v_coord + u_offset * -4.0) * 0.05/0.98;\n\ - sum += texture2D(u_texture, v_coord + u_offset * -3.0) * 0.09/0.98;\n\ - sum += texture2D(u_texture, v_coord + u_offset * -2.0) * 0.12/0.98;\n\ - sum += texture2D(u_texture, v_coord + u_offset * -1.0) * 0.15/0.98;\n\ - sum += center * 0.16/0.98;\n\ - sum += texture2D(u_texture, v_coord + u_offset * 4.0) * 0.05/0.98;\n\ - sum += texture2D(u_texture, v_coord + u_offset * 3.0) * 0.09/0.98;\n\ - sum += texture2D(u_texture, v_coord + u_offset * 2.0) * 0.12/0.98;\n\ - sum += texture2D(u_texture, v_coord + u_offset * 1.0) * 0.15/0.98;\n\ - gl_FragColor = u_intensity * sum;\n\ - }\n\ - "; -*/ + // Helps connect/disconnect AudioNodes when new connections are made in the node + LGAudioSource.prototype.onConnectionsChange = LGAudio.onConnectionsChange; - LiteGraph.registerNodeType("texture/blur", LGraphTextureBlur); + LGAudioSource.prototype.onGetInputs = function() { + return [ + ["playbackRate", "number"], + ["src","string"], + ["Play", LiteGraph.ACTION], + ["Stop", LiteGraph.ACTION], + ]; + }; - //Independent glow FX - //based on https://catlikecoding.com/unity/tutorials/advanced-rendering/bloom/ - function FXGlow() - { - this.intensity = 0.5; - this.persistence = 0.6; - this.iterations = 8; - this.threshold = 0.8; - this.scale = 1; - - this.dirt_texture = null; - this.dirt_factor = 0.5; - - this._textures = []; - this._uniforms = { - u_intensity: 1, - u_texture: 0, - u_glow_texture: 1, - u_threshold: 0, - u_texel_size: vec2.create() - }; - } + LGAudioSource.prototype.onGetOutputs = function() { + return [["buffer", "audiobuffer"], ["start", LiteGraph.EVENT], ["ended", LiteGraph.EVENT]]; + }; - FXGlow.prototype.applyFX = function( tex, output_texture, glow_texture, average_texture ) { + LGAudioSource.prototype.onDropFile = function(file) { + if (this._dropped_url) { + URL.revokeObjectURL(this._dropped_url); + } + var url = URL.createObjectURL(file); + this.properties.src = url; + this.loadSound(url); + this._dropped_url = url; + }; - var width = tex.width; - var height = tex.height; + LGAudioSource.title = "Source"; + LGAudioSource.desc = "Plays audio"; + LiteGraph.registerNodeType("audio/source", LGAudioSource); - var texture_info = { - format: tex.format, - type: tex.type, - minFilter: GL.LINEAR, - magFilter: GL.LINEAR, - wrap: gl.CLAMP_TO_EDGE - }; + //* *************************************************** - var uniforms = this._uniforms; - var textures = this._textures; + function LGAudioMediaSource() { + this.properties = {gain: 0.5}; - //cut - var shader = FXGlow._cut_shader; - if (!shader) { - shader = FXGlow._cut_shader = new GL.Shader( - GL.Shader.SCREEN_VERTEX_SHADER, - FXGlow.cut_pixel_shader - ); - } + this._audionodes = []; + this._media_stream = null; - gl.disable(gl.DEPTH_TEST); - gl.disable(gl.BLEND); - - uniforms.u_threshold = this.threshold; - var currentDestination = (textures[0] = GL.Texture.getTemporary( - width, - height, - texture_info - )); - tex.blit( currentDestination, shader.uniforms(uniforms) ); - var currentSource = currentDestination; - - var iterations = this.iterations; - iterations = clamp(iterations, 1, 16) | 0; - var texel_size = uniforms.u_texel_size; - var intensity = this.intensity; - - uniforms.u_intensity = 1; - uniforms.u_delta = this.scale; //1 - - //downscale/upscale shader - var shader = FXGlow._shader; - if (!shader) { - shader = FXGlow._shader = new GL.Shader( - GL.Shader.SCREEN_VERTEX_SHADER, - FXGlow.scale_pixel_shader - ); - } + this.addOutput("out", "audio"); + this.addInput("gain", "number"); - var i = 1; - //downscale - for (; i < iterations; i++) { - width = width >> 1; - if ((height | 0) > 1) { - height = height >> 1; - } - if (width < 2) { - break; - } - currentDestination = textures[i] = GL.Texture.getTemporary( - width, - height, - texture_info - ); - texel_size[0] = 1 / currentSource.width; - texel_size[1] = 1 / currentSource.height; - currentSource.blit( - currentDestination, - shader.uniforms(uniforms) - ); - currentSource = currentDestination; - } + // create gain node to control volume + var context = LGAudio.getAudioContext(); + this.audionode = context.createGain(); + this.audionode.graphnode = this; + this.audionode.gain.value = this.properties.gain; + } - //average - if (average_texture) { - texel_size[0] = 1 / currentSource.width; - texel_size[1] = 1 / currentSource.height; - uniforms.u_intensity = intensity; - uniforms.u_delta = 1; - currentSource.blit(average_texture, shader.uniforms(uniforms)); - } + LGAudioMediaSource.prototype.onAdded = function(graph) { + if (graph.status === LGraph.STATUS_RUNNING) { + this.onStart(); + } + }; - //upscale and blend - gl.enable(gl.BLEND); - gl.blendFunc(gl.ONE, gl.ONE); - uniforms.u_intensity = this.persistence; - uniforms.u_delta = 0.5; + LGAudioMediaSource.prototype.onStart = function() { + if (this._media_stream == null && !this._waiting_confirmation) { + this.openStream(); + } + }; - // i-=2 => -1 to point to last element in array, -1 to go to texture above - for ( i -= 2; i >= 0; i-- ) - { - currentDestination = textures[i]; - textures[i] = null; - texel_size[0] = 1 / currentSource.width; - texel_size[1] = 1 / currentSource.height; - currentSource.blit( - currentDestination, - shader.uniforms(uniforms) - ); - GL.Texture.releaseTemporary(currentSource); - currentSource = currentDestination; - } - gl.disable(gl.BLEND); + LGAudioMediaSource.prototype.onStop = function() { + this.audionode.gain.value = 0; + }; - //glow - if (glow_texture) { - currentSource.blit(glow_texture); - } + LGAudioMediaSource.prototype.onPause = function() { + this.audionode.gain.value = 0; + }; - //final composition - if ( output_texture ) { - var final_texture = output_texture; - var dirt_texture = this.dirt_texture; - var dirt_factor = this.dirt_factor; - uniforms.u_intensity = intensity; - - shader = dirt_texture - ? FXGlow._dirt_final_shader - : FXGlow._final_shader; - if (!shader) { - if (dirt_texture) { - shader = FXGlow._dirt_final_shader = new GL.Shader( - GL.Shader.SCREEN_VERTEX_SHADER, - FXGlow.final_pixel_shader, - { USE_DIRT: "" } - ); - } else { - shader = FXGlow._final_shader = new GL.Shader( - GL.Shader.SCREEN_VERTEX_SHADER, - FXGlow.final_pixel_shader - ); - } - } + LGAudioMediaSource.prototype.onUnpause = function() { + this.audionode.gain.value = this.properties.gain; + }; - final_texture.drawTo(function() { - tex.bind(0); - currentSource.bind(1); - if (dirt_texture) { - shader.setUniform("u_dirt_factor", dirt_factor); - shader.setUniform( - "u_dirt_texture", - dirt_texture.bind(2) - ); - } - shader.toViewport(uniforms); - }); - } + LGAudioMediaSource.prototype.onRemoved = function() { + this.audionode.gain.value = 0; + if (this.audiosource_node) { + this.audiosource_node.disconnect(this.audionode); + this.audiosource_node = null; + } + if (this._media_stream) { + var tracks = this._media_stream.getTracks(); + if (tracks.length) { + tracks[0].stop(); + } + } + }; - GL.Texture.releaseTemporary(currentSource); - }; + LGAudioMediaSource.prototype.openStream = function() { + if (!navigator.mediaDevices) { + console.log("getUserMedia() is not supported in your browser, use chrome and enable WebRTC from about://flags"); + return; + } - FXGlow.cut_pixel_shader = - "precision highp float;\n\ - varying vec2 v_coord;\n\ - uniform sampler2D u_texture;\n\ - uniform float u_threshold;\n\ - void main() {\n\ - gl_FragColor = max( texture2D( u_texture, v_coord ) - vec4( u_threshold ), vec4(0.0) );\n\ - }"; + this._waiting_confirmation = true; - FXGlow.scale_pixel_shader = - "precision highp float;\n\ - varying vec2 v_coord;\n\ - uniform sampler2D u_texture;\n\ - uniform vec2 u_texel_size;\n\ - uniform float u_delta;\n\ - uniform float u_intensity;\n\ - \n\ - vec4 sampleBox(vec2 uv) {\n\ - vec4 o = u_texel_size.xyxy * vec2(-u_delta, u_delta).xxyy;\n\ - vec4 s = texture2D( u_texture, uv + o.xy ) + texture2D( u_texture, uv + o.zy) + texture2D( u_texture, uv + o.xw) + texture2D( u_texture, uv + o.zw);\n\ - return s * 0.25;\n\ - }\n\ - void main() {\n\ - gl_FragColor = u_intensity * sampleBox( v_coord );\n\ - }"; + // Not showing vendor prefixes. + navigator.mediaDevices + .getUserMedia({ audio: true, video: false }) + .then(this.streamReady.bind(this)) + .catch(onFailSoHard); - FXGlow.final_pixel_shader = - "precision highp float;\n\ - varying vec2 v_coord;\n\ - uniform sampler2D u_texture;\n\ - uniform sampler2D u_glow_texture;\n\ - #ifdef USE_DIRT\n\ - uniform sampler2D u_dirt_texture;\n\ - #endif\n\ - uniform vec2 u_texel_size;\n\ - uniform float u_delta;\n\ - uniform float u_intensity;\n\ - uniform float u_dirt_factor;\n\ - \n\ - vec4 sampleBox(vec2 uv) {\n\ - vec4 o = u_texel_size.xyxy * vec2(-u_delta, u_delta).xxyy;\n\ - vec4 s = texture2D( u_glow_texture, uv + o.xy ) + texture2D( u_glow_texture, uv + o.zy) + texture2D( u_glow_texture, uv + o.xw) + texture2D( u_glow_texture, uv + o.zw);\n\ - return s * 0.25;\n\ - }\n\ - void main() {\n\ - vec4 glow = sampleBox( v_coord );\n\ - #ifdef USE_DIRT\n\ - glow = mix( glow, glow * texture2D( u_dirt_texture, v_coord ), u_dirt_factor );\n\ - #endif\n\ - gl_FragColor = texture2D( u_texture, v_coord ) + u_intensity * glow;\n\ - }"; + var that = this; + function onFailSoHard(err) { + console.log("Media rejected", err); + that._media_stream = false; + that.boxcolor = "red"; + } + }; + LGAudioMediaSource.prototype.streamReady = function(localMediaStream) { + this._media_stream = localMediaStream; + // this._waiting_confirmation = false; - // Texture Glow ***************************************** - function LGraphTextureGlow() { - this.addInput("in", "Texture"); - this.addInput("dirt", "Texture"); - this.addOutput("out", "Texture"); - this.addOutput("glow", "Texture"); - this.properties = { - enabled: true, - intensity: 1, - persistence: 0.99, - iterations: 16, - threshold: 0, - scale: 1, - dirt_factor: 0.5, - precision: LGraphTexture.DEFAULT - }; + // init context + if (this.audiosource_node) { + this.audiosource_node.disconnect(this.audionode); + } + var context = LGAudio.getAudioContext(); + this.audiosource_node = context.createMediaStreamSource(localMediaStream); + this.audiosource_node.graphnode = this; + this.audiosource_node.connect(this.audionode); + this.boxcolor = "white"; + }; - this.fx = new FXGlow(); - } + LGAudioMediaSource.prototype.onExecute = function() { + if (this._media_stream == null && !this._waiting_confirmation) { + this.openStream(); + } - LGraphTextureGlow.title = "Glow"; - LGraphTextureGlow.desc = "Filters a texture giving it a glow effect"; - - LGraphTextureGlow.widgets_info = { - iterations: { - type: "number", - min: 0, - max: 16, - step: 1, - precision: 0 - }, - threshold: { - type: "number", - min: 0, - max: 10, - step: 0.01, - precision: 2 - }, - precision: { widget: "combo", values: LGraphTexture.MODE_VALUES } - }; + if (this.inputs) { + for (var i = 0; i < this.inputs.length; ++i) { + var input = this.inputs[i]; + if (input.link == null) { + continue; + } + var v = this.getInputData(i); + if (v === undefined) { + continue; + } + if (input.name == "gain") { + this.audionode.gain.value = this.properties.gain = v; + } + } + } + }; - LGraphTextureGlow.prototype.onGetInputs = function() { - return [ - ["enabled", "boolean"], - ["threshold", "number"], - ["intensity", "number"], - ["persistence", "number"], - ["iterations", "number"], - ["dirt_factor", "number"] - ]; - }; + LGAudioMediaSource.prototype.onAction = function(event) { + if (event == "Play") { + this.audionode.gain.value = this.properties.gain; + } else if (event == "Stop") { + this.audionode.gain.value = 0; + } + }; - LGraphTextureGlow.prototype.onGetOutputs = function() { - return [["average", "Texture"]]; - }; + LGAudioMediaSource.prototype.onPropertyChanged = function(name, value) { + if (name == "gain") { + this.audionode.gain.value = value; + } + }; - LGraphTextureGlow.prototype.onExecute = function() { - var tex = this.getInputData(0); - if (!tex) { - return; - } + // Helps connect/disconnect AudioNodes when new connections are made in the node + LGAudioMediaSource.prototype.onConnectionsChange = + LGAudio.onConnectionsChange; - if (!this.isAnyOutputConnected()) { - return; - } //saves work + LGAudioMediaSource.prototype.onGetInputs = function() { + return [ + ["playbackRate", "number"], + ["Play", LiteGraph.ACTION], + ["Stop", LiteGraph.ACTION], + ]; + }; - if ( - this.properties.precision === LGraphTexture.PASS_THROUGH || - this.getInputOrProperty("enabled") === false - ) { - this.setOutputData(0, tex); - return; - } + LGAudioMediaSource.title = "MediaSource"; + LGAudioMediaSource.desc = "Plays microphone"; + LiteGraph.registerNodeType("audio/media_source", LGAudioMediaSource); - var width = tex.width; - var height = tex.height; - - var fx = this.fx; - fx.threshold = this.getInputOrProperty("threshold"); - fx.iterations = this.getInputOrProperty("iterations"); - fx.intensity = this.getInputOrProperty("intensity"); - fx.persistence = this.getInputOrProperty("persistence"); - fx.dirt_texture = this.getInputData(1); - fx.dirt_factor = this.getInputOrProperty("dirt_factor"); - fx.scale = this.properties.scale; - - var type = LGraphTexture.getTextureType( this.properties.precision, tex ); - - var average_texture = null; - if (this.isOutputConnected(2)) { - average_texture = this._average_texture; - if ( - !average_texture || - average_texture.type != tex.type || - average_texture.format != tex.format - ) { - average_texture = this._average_texture = new GL.Texture( - 1, - 1, - { - type: tex.type, - format: tex.format, - filter: gl.LINEAR - } - ); - } - } + //* **************************************************** - var glow_texture = null; - if (this.isOutputConnected(1)) { - glow_texture = this._glow_texture; - if ( - !glow_texture || - glow_texture.width != tex.width || - glow_texture.height != tex.height || - glow_texture.type != type || - glow_texture.format != tex.format - ) { - glow_texture = this._glow_texture = new GL.Texture( - tex.width, - tex.height, - { type: type, format: tex.format, filter: gl.LINEAR } - ); - } - } + function LGAudioAnalyser() { + this.properties = { + fftSize: 2048, + minDecibels: -100, + maxDecibels: -10, + smoothingTimeConstant: 0.5, + }; - var final_texture = null; - if (this.isOutputConnected(0)) { - final_texture = this._final_texture; - if ( - !final_texture || - final_texture.width != tex.width || - final_texture.height != tex.height || - final_texture.type != type || - final_texture.format != tex.format - ) { - final_texture = this._final_texture = new GL.Texture( - tex.width, - tex.height, - { type: type, format: tex.format, filter: gl.LINEAR } - ); - } + var context = LGAudio.getAudioContext(); - } + this.audionode = context.createAnalyser(); + this.audionode.graphnode = this; + this.audionode.fftSize = this.properties.fftSize; + this.audionode.minDecibels = this.properties.minDecibels; + this.audionode.maxDecibels = this.properties.maxDecibels; + this.audionode.smoothingTimeConstant = this.properties.smoothingTimeConstant; - //apply FX - fx.applyFX(tex, final_texture, glow_texture, average_texture ); + this.addInput("in", "audio"); + this.addOutput("freqs", "array"); + this.addOutput("samples", "array"); - if (this.isOutputConnected(0)) - this.setOutputData(0, final_texture); + this._freq_bin = null; + this._time_bin = null; + } - if (this.isOutputConnected(1)) - this.setOutputData(1, average_texture); + LGAudioAnalyser.prototype.onPropertyChanged = function(name, value) { + this.audionode[name] = value; + }; - if (this.isOutputConnected(2)) - this.setOutputData(2, glow_texture); - }; + LGAudioAnalyser.prototype.onExecute = function() { + if (this.isOutputConnected(0)) { + // send FFT + var bufferLength = this.audionode.frequencyBinCount; + if (!this._freq_bin || this._freq_bin.length != bufferLength) { + this._freq_bin = new Uint8Array(bufferLength); + } + this.audionode.getByteFrequencyData(this._freq_bin); + this.setOutputData(0, this._freq_bin); + } - LiteGraph.registerNodeType("texture/glow", LGraphTextureGlow); + // send analyzer + if (this.isOutputConnected(1)) { + // send Samples + var bufferLength = this.audionode.frequencyBinCount; + if (!this._time_bin || this._time_bin.length != bufferLength) { + this._time_bin = new Uint8Array(bufferLength); + } + this.audionode.getByteTimeDomainData(this._time_bin); + this.setOutputData(1, this._time_bin); + } - // Texture Filter ***************************************** - function LGraphTextureKuwaharaFilter() { - this.addInput("Texture", "Texture"); - this.addOutput("Filtered", "Texture"); - this.properties = { intensity: 1, radius: 5 }; - } + // properties + for (var i = 1; i < this.inputs.length; ++i) { + var input = this.inputs[i]; + if (input.link == null) { + continue; + } + var v = this.getInputData(i); + if (v !== undefined) { + this.audionode[input.name].value = v; + } + } - LGraphTextureKuwaharaFilter.title = "Kuwahara Filter"; - LGraphTextureKuwaharaFilter.desc = - "Filters a texture giving an artistic oil canvas painting"; + // time domain + // this.audionode.getFloatTimeDomainData( dataArray ); + }; - LGraphTextureKuwaharaFilter.max_radius = 10; - LGraphTextureKuwaharaFilter._shaders = []; + LGAudioAnalyser.prototype.onGetInputs = function() { + return [ + ["minDecibels", "number"], + ["maxDecibels", "number"], + ["smoothingTimeConstant", "number"], + ]; + }; - LGraphTextureKuwaharaFilter.prototype.onExecute = function() { - var tex = this.getInputData(0); - if (!tex) { - return; - } + LGAudioAnalyser.prototype.onGetOutputs = function() { + return [["freqs", "array"], ["samples", "array"]]; + }; - if (!this.isOutputConnected(0)) { - return; - } //saves work + LGAudioAnalyser.title = "Analyser"; + LGAudioAnalyser.desc = "Audio Analyser"; + LiteGraph.registerNodeType("audio/analyser", LGAudioAnalyser); - var temp = this._temp_texture; + //* **************************************************** - if ( - !temp || - temp.width != tex.width || - temp.height != tex.height || - temp.type != tex.type - ) { - this._temp_texture = new GL.Texture(tex.width, tex.height, { - type: tex.type, - format: gl.RGBA, - filter: gl.LINEAR - }); - } + function LGAudioGain() { + // default + this.properties = {gain: 1}; - //iterations - var radius = this.properties.radius; - radius = Math.min( - Math.floor(radius), - LGraphTextureKuwaharaFilter.max_radius - ); - if (radius == 0) { - //skip blurring - this.setOutputData(0, tex); - return; - } + this.audionode = LGAudio.getAudioContext().createGain(); + this.addInput("in", "audio"); + this.addInput("gain", "number"); + this.addOutput("out", "audio"); + } - var intensity = this.properties.intensity; + LGAudioGain.prototype.onExecute = function() { + if (!this.inputs || !this.inputs.length) { + return; + } - //blur sometimes needs an aspect correction - var aspect = LiteGraph.camera_aspect; - if (!aspect && window.gl !== undefined) { - aspect = gl.canvas.height / gl.canvas.width; - } - if (!aspect) { - aspect = 1; - } - aspect = this.properties.preserve_aspect ? aspect : 1; - - if (!LGraphTextureKuwaharaFilter._shaders[radius]) { - LGraphTextureKuwaharaFilter._shaders[radius] = new GL.Shader( - Shader.SCREEN_VERTEX_SHADER, - LGraphTextureKuwaharaFilter.pixel_shader, - { RADIUS: radius.toFixed(0) } - ); - } + for (var i = 1; i < this.inputs.length; ++i) { + var input = this.inputs[i]; + var v = this.getInputData(i); + if (v !== undefined) { + this.audionode[input.name].value = v; + } + } + }; - var shader = LGraphTextureKuwaharaFilter._shaders[radius]; - var mesh = GL.Mesh.getScreenQuad(); - tex.bind(0); - - this._temp_texture.drawTo(function() { - shader - .uniforms({ - u_texture: 0, - u_intensity: intensity, - u_resolution: [tex.width, tex.height], - u_iResolution: [1 / tex.width, 1 / tex.height] - }) - .draw(mesh); - }); + LGAudio.createAudioNodeWrapper(LGAudioGain); - this.setOutputData(0, this._temp_texture); - }; + LGAudioGain.title = "Gain"; + LGAudioGain.desc = "Audio gain"; + LiteGraph.registerNodeType("audio/gain", LGAudioGain); - //from https://www.shadertoy.com/view/MsXSz4 - LGraphTextureKuwaharaFilter.pixel_shader = - "\n\ -precision highp float;\n\ -varying vec2 v_coord;\n\ -uniform sampler2D u_texture;\n\ -uniform float u_intensity;\n\ -uniform vec2 u_resolution;\n\ -uniform vec2 u_iResolution;\n\ -#ifndef RADIUS\n\ - #define RADIUS 7\n\ -#endif\n\ -void main() {\n\ -\n\ - const int radius = RADIUS;\n\ - vec2 fragCoord = v_coord;\n\ - vec2 src_size = u_iResolution;\n\ - vec2 uv = v_coord;\n\ - float n = float((radius + 1) * (radius + 1));\n\ - int i;\n\ - int j;\n\ - vec3 m0 = vec3(0.0); vec3 m1 = vec3(0.0); vec3 m2 = vec3(0.0); vec3 m3 = vec3(0.0);\n\ - vec3 s0 = vec3(0.0); vec3 s1 = vec3(0.0); vec3 s2 = vec3(0.0); vec3 s3 = vec3(0.0);\n\ - vec3 c;\n\ - \n\ - for (int j = -radius; j <= 0; ++j) {\n\ - for (int i = -radius; i <= 0; ++i) {\n\ - c = texture2D(u_texture, uv + vec2(i,j) * src_size).rgb;\n\ - m0 += c;\n\ - s0 += c * c;\n\ - }\n\ - }\n\ - \n\ - for (int j = -radius; j <= 0; ++j) {\n\ - for (int i = 0; i <= radius; ++i) {\n\ - c = texture2D(u_texture, uv + vec2(i,j) * src_size).rgb;\n\ - m1 += c;\n\ - s1 += c * c;\n\ - }\n\ - }\n\ - \n\ - for (int j = 0; j <= radius; ++j) {\n\ - for (int i = 0; i <= radius; ++i) {\n\ - c = texture2D(u_texture, uv + vec2(i,j) * src_size).rgb;\n\ - m2 += c;\n\ - s2 += c * c;\n\ - }\n\ - }\n\ - \n\ - for (int j = 0; j <= radius; ++j) {\n\ - for (int i = -radius; i <= 0; ++i) {\n\ - c = texture2D(u_texture, uv + vec2(i,j) * src_size).rgb;\n\ - m3 += c;\n\ - s3 += c * c;\n\ - }\n\ - }\n\ - \n\ - float min_sigma2 = 1e+2;\n\ - m0 /= n;\n\ - s0 = abs(s0 / n - m0 * m0);\n\ - \n\ - float sigma2 = s0.r + s0.g + s0.b;\n\ - if (sigma2 < min_sigma2) {\n\ - min_sigma2 = sigma2;\n\ - gl_FragColor = vec4(m0, 1.0);\n\ - }\n\ - \n\ - m1 /= n;\n\ - s1 = abs(s1 / n - m1 * m1);\n\ - \n\ - sigma2 = s1.r + s1.g + s1.b;\n\ - if (sigma2 < min_sigma2) {\n\ - min_sigma2 = sigma2;\n\ - gl_FragColor = vec4(m1, 1.0);\n\ - }\n\ - \n\ - m2 /= n;\n\ - s2 = abs(s2 / n - m2 * m2);\n\ - \n\ - sigma2 = s2.r + s2.g + s2.b;\n\ - if (sigma2 < min_sigma2) {\n\ - min_sigma2 = sigma2;\n\ - gl_FragColor = vec4(m2, 1.0);\n\ - }\n\ - \n\ - m3 /= n;\n\ - s3 = abs(s3 / n - m3 * m3);\n\ - \n\ - sigma2 = s3.r + s3.g + s3.b;\n\ - if (sigma2 < min_sigma2) {\n\ - min_sigma2 = sigma2;\n\ - gl_FragColor = vec4(m3, 1.0);\n\ - }\n\ -}\n\ -"; + function LGAudioConvolver() { + // default + this.properties = { + impulse_src: "", + normalize: true, + }; - LiteGraph.registerNodeType( - "texture/kuwahara", - LGraphTextureKuwaharaFilter - ); + this.audionode = LGAudio.getAudioContext().createConvolver(); + this.addInput("in", "audio"); + this.addOutput("out", "audio"); + } - // Texture ***************************************** - function LGraphTextureXDoGFilter() { - this.addInput("Texture", "Texture"); - this.addOutput("Filtered", "Texture"); - this.properties = { - sigma: 1.4, - k: 1.6, - p: 21.7, - epsilon: 79, - phi: 0.017 - }; - } + LGAudio.createAudioNodeWrapper(LGAudioConvolver); - LGraphTextureXDoGFilter.title = "XDoG Filter"; - LGraphTextureXDoGFilter.desc = - "Filters a texture giving an artistic ink style"; + LGAudioConvolver.prototype.onRemove = function() { + if (this._dropped_url) { + URL.revokeObjectURL(this._dropped_url); + } + }; - LGraphTextureXDoGFilter.max_radius = 10; - LGraphTextureXDoGFilter._shaders = []; + LGAudioConvolver.prototype.onPropertyChanged = function(name, value) { + if (name == "impulse_src") { + this.loadImpulse(value); + } else if (name == "normalize") { + this.audionode.normalize = value; + } + }; - LGraphTextureXDoGFilter.prototype.onExecute = function() { - var tex = this.getInputData(0); - if (!tex) { - return; - } + LGAudioConvolver.prototype.onDropFile = function(file) { + if (this._dropped_url) { + URL.revokeObjectURL(this._dropped_url); + } + this._dropped_url = URL.createObjectURL(file); + this.properties.impulse_src = this._dropped_url; + this.loadImpulse(this._dropped_url); + }; - if (!this.isOutputConnected(0)) { - return; - } //saves work + LGAudioConvolver.prototype.loadImpulse = function(url) { + var that = this; - var temp = this._temp_texture; - if ( - !temp || - temp.width != tex.width || - temp.height != tex.height || - temp.type != tex.type - ) { - this._temp_texture = new GL.Texture(tex.width, tex.height, { - type: tex.type, - format: gl.RGBA, - filter: gl.LINEAR - }); - } + // kill previous load + if (this._request) { + this._request.abort(); + this._request = null; + } + + this._impulse_buffer = null; + this._loading_impulse = false; + + if (!url) { + return; + } + + // load new sample + this._request = LGAudio.loadSound(url, inner); + this._loading_impulse = true; + + // Decode asynchronously + function inner(buffer) { + that._impulse_buffer = buffer; + that.audionode.buffer = buffer; + console.log("Impulse signal set"); + that._loading_impulse = false; + } + }; - if (!LGraphTextureXDoGFilter._xdog_shader) { - LGraphTextureXDoGFilter._xdog_shader = new GL.Shader( - Shader.SCREEN_VERTEX_SHADER, - LGraphTextureXDoGFilter.xdog_pixel_shader - ); - } - var shader = LGraphTextureXDoGFilter._xdog_shader; - var mesh = GL.Mesh.getScreenQuad(); - - var sigma = this.properties.sigma; - var k = this.properties.k; - var p = this.properties.p; - var epsilon = this.properties.epsilon; - var phi = this.properties.phi; - tex.bind(0); - this._temp_texture.drawTo(function() { - shader - .uniforms({ - src: 0, - sigma: sigma, - k: k, - p: p, - epsilon: epsilon, - phi: phi, - cvsWidth: tex.width, - cvsHeight: tex.height - }) - .draw(mesh); - }); + LGAudioConvolver.title = "Convolver"; + LGAudioConvolver.desc = "Convolves the signal (used for reverb)"; + LiteGraph.registerNodeType("audio/convolver", LGAudioConvolver); - this.setOutputData(0, this._temp_texture); - }; + function LGAudioDynamicsCompressor() { + // default + this.properties = { + threshold: -50, + knee: 40, + ratio: 12, + reduction: -20, + attack: 0, + release: 0.25, + }; - //from https://github.com/RaymondMcGuire/GPU-Based-Image-Processing-Tools/blob/master/lib_webgl/scripts/main.js - LGraphTextureXDoGFilter.xdog_pixel_shader = - "\n\ -precision highp float;\n\ -uniform sampler2D src;\n\n\ -uniform float cvsHeight;\n\ -uniform float cvsWidth;\n\n\ -uniform float sigma;\n\ -uniform float k;\n\ -uniform float p;\n\ -uniform float epsilon;\n\ -uniform float phi;\n\ -varying vec2 v_coord;\n\n\ -float cosh(float val)\n\ -{\n\ - float tmp = exp(val);\n\ - float cosH = (tmp + 1.0 / tmp) / 2.0;\n\ - return cosH;\n\ -}\n\n\ -float tanh(float val)\n\ -{\n\ - float tmp = exp(val);\n\ - float tanH = (tmp - 1.0 / tmp) / (tmp + 1.0 / tmp);\n\ - return tanH;\n\ -}\n\n\ -float sinh(float val)\n\ -{\n\ - float tmp = exp(val);\n\ - float sinH = (tmp - 1.0 / tmp) / 2.0;\n\ - return sinH;\n\ -}\n\n\ -void main(void){\n\ - vec3 destColor = vec3(0.0);\n\ - float tFrag = 1.0 / cvsHeight;\n\ - float sFrag = 1.0 / cvsWidth;\n\ - vec2 Frag = vec2(sFrag,tFrag);\n\ - vec2 uv = gl_FragCoord.st;\n\ - float twoSigmaESquared = 2.0 * sigma * sigma;\n\ - float twoSigmaRSquared = twoSigmaESquared * k * k;\n\ - int halfWidth = int(ceil( 1.0 * sigma * k ));\n\n\ - const int MAX_NUM_ITERATION = 99999;\n\ - vec2 sum = vec2(0.0);\n\ - vec2 norm = vec2(0.0);\n\n\ - for(int cnt=0;cnt (2*halfWidth+1)*(2*halfWidth+1)){break;}\n\ - int i = int(cnt / (2*halfWidth+1)) - halfWidth;\n\ - int j = cnt - halfWidth - int(cnt / (2*halfWidth+1)) * (2*halfWidth+1);\n\n\ - float d = length(vec2(i,j));\n\ - vec2 kernel = vec2( exp( -d * d / twoSigmaESquared ), \n\ - exp( -d * d / twoSigmaRSquared ));\n\n\ - vec2 L = texture2D(src, (uv + vec2(i,j)) * Frag).xx;\n\n\ - norm += kernel;\n\ - sum += kernel * L;\n\ - }\n\n\ - sum /= norm;\n\n\ - float H = 100.0 * ((1.0 + p) * sum.x - p * sum.y);\n\ - float edge = ( H > epsilon )? 1.0 : 1.0 + tanh( phi * (H - epsilon));\n\ - destColor = vec3(edge);\n\ - gl_FragColor = vec4(destColor, 1.0);\n\ -}"; + this.audionode = LGAudio.getAudioContext().createDynamicsCompressor(); + this.addInput("in", "audio"); + this.addOutput("out", "audio"); + } - LiteGraph.registerNodeType("texture/xDoG", LGraphTextureXDoGFilter); + LGAudio.createAudioNodeWrapper(LGAudioDynamicsCompressor); - // Texture Webcam ***************************************** - function LGraphTextureWebcam() { - this.addOutput("Webcam", "Texture"); - this.properties = { texture_name: "", facingMode: "user" }; - this.boxcolor = "black"; - this.version = 0; - } + LGAudioDynamicsCompressor.prototype.onExecute = function() { + if (!this.inputs || !this.inputs.length) { + return; + } + for (var i = 1; i < this.inputs.length; ++i) { + var input = this.inputs[i]; + if (input.link == null) { + continue; + } + var v = this.getInputData(i); + if (v !== undefined) { + this.audionode[input.name].value = v; + } + } + }; - LGraphTextureWebcam.title = "Webcam"; - LGraphTextureWebcam.desc = "Webcam texture"; + LGAudioDynamicsCompressor.prototype.onGetInputs = function() { + return [ + ["threshold", "number"], + ["knee", "number"], + ["ratio", "number"], + ["reduction", "number"], + ["attack", "number"], + ["release", "number"], + ]; + }; - LGraphTextureWebcam.is_webcam_open = false; + LGAudioDynamicsCompressor.title = "DynamicsCompressor"; + LGAudioDynamicsCompressor.desc = "Dynamics Compressor"; + LiteGraph.registerNodeType( + "audio/dynamicsCompressor", + LGAudioDynamicsCompressor, + ); - LGraphTextureWebcam.prototype.openStream = function() { - if (!navigator.getUserMedia) { - //console.log('getUserMedia() is not supported in your browser, use chrome and enable WebRTC from about://flags'); - return; - } + function LGAudioWaveShaper() { + // default + this.properties = {}; - this._waiting_confirmation = true; + this.audionode = LGAudio.getAudioContext().createWaveShaper(); + this.addInput("in", "audio"); + this.addInput("shape", "waveshape"); + this.addOutput("out", "audio"); + } - // Not showing vendor prefixes. - var constraints = { - audio: false, - video: { facingMode: this.properties.facingMode } - }; - navigator.mediaDevices - .getUserMedia(constraints) - .then(this.streamReady.bind(this)) - .catch(onFailSoHard); - - var that = this; - function onFailSoHard(e) { - LGraphTextureWebcam.is_webcam_open = false; - console.log("Webcam rejected", e); - that._webcam_stream = false; - that.boxcolor = "red"; - that.trigger("stream_error"); - } - }; + LGAudioWaveShaper.prototype.onExecute = function() { + if (!this.inputs || !this.inputs.length) { + return; + } + var v = this.getInputData(1); + if (v === undefined) { + return; + } + this.audionode.curve = v; + }; - LGraphTextureWebcam.prototype.closeStream = function() { - if (this._webcam_stream) { - var tracks = this._webcam_stream.getTracks(); - if (tracks.length) { - for (var i = 0; i < tracks.length; ++i) { - tracks[i].stop(); - } - } - LGraphTextureWebcam.is_webcam_open = false; - this._webcam_stream = null; - this._video = null; - this.boxcolor = "black"; - this.trigger("stream_closed"); - } - }; + LGAudioWaveShaper.prototype.setWaveShape = function(shape) { + this.audionode.curve = shape; + }; - LGraphTextureWebcam.prototype.streamReady = function(localMediaStream) { - this._webcam_stream = localMediaStream; - //this._waiting_confirmation = false; - this.boxcolor = "green"; - var video = this._video; - if (!video) { - video = document.createElement("video"); - video.autoplay = true; - video.srcObject = 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. - LGraphTextureWebcam.is_webcam_open = true; - console.log(e); - }; - } - this.trigger("stream_ready", video); - }; + LGAudio.createAudioNodeWrapper(LGAudioWaveShaper); - LGraphTextureWebcam.prototype.onPropertyChanged = function( - name, - value - ) { - if (name == "facingMode") { - this.properties.facingMode = value; - this.closeStream(); - this.openStream(); - } - }; + /* disabled till I dont find a way to do a wave shape +LGAudioWaveShaper.title = "WaveShaper"; +LGAudioWaveShaper.desc = "Distortion using wave shape"; +LiteGraph.registerNodeType("audio/waveShaper", LGAudioWaveShaper); +*/ - LGraphTextureWebcam.prototype.onRemoved = function() { - if (!this._webcam_stream) { - return; - } + function LGAudioMixer() { + // default + this.properties = { + gain1: 0.5, + gain2: 0.5, + }; - var tracks = this._webcam_stream.getTracks(); - if (tracks.length) { - for (var i = 0; i < tracks.length; ++i) { - tracks[i].stop(); - } - } + this.audionode = LGAudio.getAudioContext().createGain(); - this._webcam_stream = null; - this._video = null; - }; + this.audionode1 = LGAudio.getAudioContext().createGain(); + this.audionode1.gain.value = this.properties.gain1; + this.audionode2 = LGAudio.getAudioContext().createGain(); + this.audionode2.gain.value = this.properties.gain2; - LGraphTextureWebcam.prototype.onDrawBackground = function(ctx) { - if (this.flags.collapsed || this.size[1] <= 20) { - return; - } + this.audionode1.connect(this.audionode); + this.audionode2.connect(this.audionode); - if (!this._video) { - return; - } + this.addInput("in1", "audio"); + this.addInput("in1 gain", "number"); + this.addInput("in2", "audio"); + this.addInput("in2 gain", "number"); - //render to graph canvas - ctx.save(); - if (!ctx.webgl) { - //reverse image - ctx.drawImage(this._video, 0, 0, this.size[0], this.size[1]); - } else { - if (this._video_texture) { - ctx.drawImage( - this._video_texture, - 0, - 0, - this.size[0], - this.size[1] - ); - } - } - ctx.restore(); - }; + this.addOutput("out", "audio"); + } - LGraphTextureWebcam.prototype.onExecute = function() { - if (this._webcam_stream == null && !this._waiting_confirmation) { - this.openStream(); - } + LGAudioMixer.prototype.getAudioNodeInInputSlot = function(slot) { + if (slot == 0) { + return this.audionode1; + } else if (slot == 2) { + return this.audionode2; + } + }; - if (!this._video || !this._video.videoWidth) { - return; - } + LGAudioMixer.prototype.onPropertyChanged = function(name, value) { + if (name == "gain1") { + this.audionode1.gain.value = value; + } else if (name == "gain2") { + this.audionode2.gain.value = value; + } + }; + + LGAudioMixer.prototype.onExecute = function() { + if (!this.inputs || !this.inputs.length) { + return; + } - var width = this._video.videoWidth; - var height = this._video.videoHeight; + for (var i = 1; i < this.inputs.length; ++i) { + var input = this.inputs[i]; - var temp = this._video_texture; - if (!temp || temp.width != width || temp.height != height) { - this._video_texture = new GL.Texture(width, height, { - format: gl.RGB, - filter: gl.LINEAR - }); - } + if (input.link == null || input.type == "audio") { + continue; + } - this._video_texture.uploadImage(this._video); - this._video_texture.version = ++this.version; + var v = this.getInputData(i); + if (v === undefined) { + continue; + } - if (this.properties.texture_name) { - var container = LGraphTexture.getTexturesContainer(); - container[this.properties.texture_name] = this._video_texture; - } + if (i == 1) { + this.audionode1.gain.value = v; + } else if (i == 3) { + this.audionode2.gain.value = v; + } + } + }; - this.setOutputData(0, this._video_texture); - for (var i = 1; i < this.outputs.length; ++i) { - if (!this.outputs[i]) { - continue; - } - switch (this.outputs[i].name) { - case "width": - this.setOutputData(i, this._video.videoWidth); - break; - case "height": - this.setOutputData(i, this._video.videoHeight); - break; - } - } - }; + LGAudio.createAudioNodeWrapper(LGAudioMixer); - LGraphTextureWebcam.prototype.onGetOutputs = function() { - return [ - ["width", "number"], - ["height", "number"], - ["stream_ready", LiteGraph.EVENT], - ["stream_closed", LiteGraph.EVENT], - ["stream_error", LiteGraph.EVENT] - ]; - }; + LGAudioMixer.title = "Mixer"; + LGAudioMixer.desc = "Audio mixer"; + LiteGraph.registerNodeType("audio/mixer", LGAudioMixer); - LiteGraph.registerNodeType("texture/webcam", LGraphTextureWebcam); + function LGAudioADSR() { + // default + this.properties = { + A: 0.1, + D: 0.1, + S: 0.1, + R: 0.1, + }; - //from https://github.com/spite/Wagner - function LGraphLensFX() { - this.addInput("in", "Texture"); - this.addInput("f", "number"); - this.addOutput("out", "Texture"); - this.properties = { - enabled: true, - factor: 1, - precision: LGraphTexture.LOW - }; + this.audionode = LGAudio.getAudioContext().createGain(); + this.audionode.gain.value = 0; + this.addInput("in", "audio"); + this.addInput("gate", "boolean"); + this.addOutput("out", "audio"); + this.gate = false; + } - this._uniforms = { u_texture: 0, u_factor: 1 }; - } + LGAudioADSR.prototype.onExecute = function() { + var audioContext = LGAudio.getAudioContext(); + var now = audioContext.currentTime; + var node = this.audionode; + var gain = node.gain; + var current_gate = this.getInputData(1); - LGraphLensFX.title = "Lens FX"; - LGraphLensFX.desc = "distortion and chromatic aberration"; + var A = this.getInputOrProperty("A"); + var D = this.getInputOrProperty("D"); + var S = this.getInputOrProperty("S"); + var R = this.getInputOrProperty("R"); - LGraphLensFX.widgets_info = { - precision: { widget: "combo", values: LGraphTexture.MODE_VALUES } - }; + if (!this.gate && current_gate) { + gain.cancelScheduledValues(0); + gain.setValueAtTime(0, now); + gain.linearRampToValueAtTime(1, now + A); + gain.linearRampToValueAtTime(S, now + A + D); + } else if (this.gate && !current_gate) { + gain.cancelScheduledValues(0); + gain.setValueAtTime(gain.value, now); + gain.linearRampToValueAtTime(0, now + R); + } - LGraphLensFX.prototype.onGetInputs = function() { - return [["enabled", "boolean"]]; - }; + this.gate = current_gate; + }; - LGraphLensFX.prototype.onExecute = function() { - var tex = this.getInputData(0); - if (!tex) { - return; - } + LGAudioADSR.prototype.onGetInputs = function() { + return [ + ["A", "number"], + ["D", "number"], + ["S", "number"], + ["R", "number"], + ]; + }; - if (!this.isOutputConnected(0)) { - return; - } //saves work + LGAudio.createAudioNodeWrapper(LGAudioADSR); - if ( - this.properties.precision === LGraphTexture.PASS_THROUGH || - this.getInputOrProperty("enabled") === false - ) { - this.setOutputData(0, tex); - return; - } + LGAudioADSR.title = "ADSR"; + LGAudioADSR.desc = "Audio envelope"; + LiteGraph.registerNodeType("audio/adsr", LGAudioADSR); - var temp = this._temp_texture; - if ( - !temp || - temp.width != tex.width || - temp.height != tex.height || - temp.type != tex.type - ) { - temp = this._temp_texture = new GL.Texture( - tex.width, - tex.height, - { type: tex.type, format: gl.RGBA, filter: gl.LINEAR } - ); - } + function LGAudioDelay() { + // default + this.properties = {delayTime: 0.5}; - var shader = LGraphLensFX._shader; - if (!shader) { - shader = LGraphLensFX._shader = new GL.Shader( - GL.Shader.SCREEN_VERTEX_SHADER, - LGraphLensFX.pixel_shader - ); - } + this.audionode = LGAudio.getAudioContext().createDelay(10); + this.audionode.delayTime.value = this.properties.delayTime; + this.addInput("in", "audio"); + this.addInput("time", "number"); + this.addOutput("out", "audio"); + } - var factor = this.getInputData(1); - if (factor == null) { - factor = this.properties.factor; - } + LGAudio.createAudioNodeWrapper(LGAudioDelay); - var uniforms = this._uniforms; - uniforms.u_factor = factor; + LGAudioDelay.prototype.onExecute = function() { + var v = this.getInputData(1); + if (v !== undefined) { + this.audionode.delayTime.value = v; + } + }; - //apply shader - gl.disable(gl.DEPTH_TEST); - temp.drawTo(function() { - tex.bind(0); - shader.uniforms(uniforms).draw(GL.Mesh.getScreenQuad()); - }); + LGAudioDelay.title = "Delay"; + LGAudioDelay.desc = "Audio delay"; + LiteGraph.registerNodeType("audio/delay", LGAudioDelay); - this.setOutputData(0, temp); - }; + function LGAudioBiquadFilter() { + // default + this.properties = { + frequency: 350, + detune: 0, + Q: 1, + }; + this.addProperty("type", "lowpass", "enum", { + values: [ + "lowpass", + "highpass", + "bandpass", + "lowshelf", + "highshelf", + "peaking", + "notch", + "allpass", + ], + }); - LGraphLensFX.pixel_shader = - "precision highp float;\n\ - varying vec2 v_coord;\n\ - uniform sampler2D u_texture;\n\ - uniform float u_factor;\n\ - vec2 barrelDistortion(vec2 coord, float amt) {\n\ - vec2 cc = coord - 0.5;\n\ - float dist = dot(cc, cc);\n\ - return coord + cc * dist * amt;\n\ - }\n\ - \n\ - float sat( float t )\n\ - {\n\ - return clamp( t, 0.0, 1.0 );\n\ - }\n\ - \n\ - float linterp( float t ) {\n\ - return sat( 1.0 - abs( 2.0*t - 1.0 ) );\n\ - }\n\ - \n\ - float remap( float t, float a, float b ) {\n\ - return sat( (t - a) / (b - a) );\n\ - }\n\ - \n\ - vec4 spectrum_offset( float t ) {\n\ - vec4 ret;\n\ - float lo = step(t,0.5);\n\ - float hi = 1.0-lo;\n\ - float w = linterp( remap( t, 1.0/6.0, 5.0/6.0 ) );\n\ - ret = vec4(lo,1.0,hi, 1.) * vec4(1.0-w, w, 1.0-w, 1.);\n\ - \n\ - return pow( ret, vec4(1.0/2.2) );\n\ - }\n\ - \n\ - const float max_distort = 2.2;\n\ - const int num_iter = 12;\n\ - const float reci_num_iter_f = 1.0 / float(num_iter);\n\ - \n\ - void main()\n\ - { \n\ - vec2 uv=v_coord;\n\ - vec4 sumcol = vec4(0.0);\n\ - vec4 sumw = vec4(0.0); \n\ - for ( int i=0; i= 0) { + var samplerate = LGAudio.getAudioContext().sampleRate; + var binfreq = samplerate / buffer.length; + var x = (2 * (this.properties.mark / binfreq)) / delta; + if (x >= this.size[0]) { + x = this.size[0] - 1; + } + ctx.strokeStyle = "red"; + ctx.beginPath(); + ctx.moveTo(x, h); + ctx.lineTo(x, 0); + ctx.stroke(); + } + }; - if(this.properties.split_channels) - { - if(channel == "RGB") - { - this.widgets[1].value = channel = "R"; - this.widgets[1].disabled = false; - } - this.curve_editor.points = this._points.R; - this.curve_editor.draw( ctx, [this.size[0],this.size[1] - this.curve_offset], graphcanvas, "#111", LGraphTextureCurve.channel_line_colors.R, true ); - ctx.globalCompositeOperation = "lighten"; - this.curve_editor.points = this._points.G; - this.curve_editor.draw( ctx, [this.size[0],this.size[1] - this.curve_offset], graphcanvas, null, LGraphTextureCurve.channel_line_colors.G, true ); - this.curve_editor.points = this._points.B; - this.curve_editor.draw( ctx, [this.size[0],this.size[1] - this.curve_offset], graphcanvas, null, LGraphTextureCurve.channel_line_colors.B, true ); - ctx.globalCompositeOperation = "source-over"; - } - else - { - this.widgets[1].value = channel = "RGB"; - this.widgets[1].disabled = true; - } + LGAudioVisualization.title = "Visualization"; + LGAudioVisualization.desc = "Audio Visualization"; + LiteGraph.registerNodeType("audio/visualization", LGAudioVisualization); - this.curve_editor.points = this._points[channel]; - this.curve_editor.draw( ctx, [this.size[0],this.size[1] - this.curve_offset], graphcanvas, this.properties.split_channels ? null : "#111", LGraphTextureCurve.channel_line_colors[channel] ); - ctx.restore(); - } + function LGAudioBandSignal() { + // default + this.properties = { + band: 440, + amplitude: 1, + }; - LGraphTextureCurve.pixel_shader = - "precision highp float;\n\ - varying vec2 v_coord;\n\ - uniform sampler2D u_texture;\n\ - uniform sampler2D u_curve;\n\ - uniform float u_range;\n\ - \n\ - void main() {\n\ - vec4 color = texture2D( u_texture, v_coord ) * u_range;\n\ - color.x = texture2D( u_curve, vec2( color.x, 0.5 ) ).x;\n\ - color.y = texture2D( u_curve, vec2( color.y, 0.5 ) ).y;\n\ - color.z = texture2D( u_curve, vec2( color.z, 0.5 ) ).z;\n\ - //color.w = texture2D( u_curve, vec2( color.w, 0.5 ) ).w;\n\ - gl_FragColor = color;\n\ - }"; + this.addInput("freqs", "array"); + this.addOutput("signal", "number"); + } - LiteGraph.registerNodeType("texture/curve", LGraphTextureCurve); + LGAudioBandSignal.prototype.onExecute = function() { + this._freqs = this.getInputData(0); + if (!this._freqs) { + return; + } - //simple exposition, but plan to expand it to support different gamma curves - function LGraphExposition() { - this.addInput("in", "Texture"); - this.addInput("exp", "number"); - this.addOutput("out", "Texture"); - this.properties = { exposition: 1, precision: LGraphTexture.LOW }; - this._uniforms = { u_texture: 0, u_exposition: 1 }; - } + var band = this.properties.band; + var v = this.getInputData(1); + if (v !== undefined) { + band = v; + } - LGraphExposition.title = "Exposition"; - LGraphExposition.desc = "Controls texture exposition"; + var samplerate = LGAudio.getAudioContext().sampleRate; + var binfreq = samplerate / this._freqs.length; + var index = 2 * (band / binfreq); + var v = 0; + if (index < 0) { + v = this._freqs[0]; + } + if (index >= this._freqs.length) { + v = this._freqs[this._freqs.length - 1]; + } else { + var pos = index | 0; + var v0 = this._freqs[pos]; + var v1 = this._freqs[pos + 1]; + var f = index - pos; + v = v0 * (1 - f) + v1 * f; + } - LGraphExposition.widgets_info = { - exposition: { widget: "slider", min: 0, max: 3 }, - precision: { widget: "combo", values: LGraphTexture.MODE_VALUES } - }; + this.setOutputData(0, (v / 255) * this.properties.amplitude); + }; - LGraphExposition.prototype.onExecute = function() { - var tex = this.getInputData(0); - if (!tex) { - return; - } + LGAudioBandSignal.prototype.onGetInputs = function() { + return [["band", "number"]]; + }; - if (!this.isOutputConnected(0)) { - return; - } //saves work + LGAudioBandSignal.title = "Signal"; + LGAudioBandSignal.desc = "extract the signal of some frequency"; + LiteGraph.registerNodeType("audio/signal", LGAudioBandSignal); - var temp = this._temp_texture; - if ( - !temp || - temp.width != tex.width || - temp.height != tex.height || - temp.type != tex.type - ) { - temp = this._temp_texture = new GL.Texture( - tex.width, - tex.height, - { type: tex.type, format: gl.RGBA, filter: gl.LINEAR } - ); - } + function LGAudioScript() { + if (!LGAudioScript.default_code) { + var code = LGAudioScript.default_function.toString(); + var index = code.indexOf("{") + 1; + var index2 = code.lastIndexOf("}"); + LGAudioScript.default_code = code.substr(index, index2 - index); + } - var shader = LGraphExposition._shader; - if (!shader) { - shader = LGraphExposition._shader = new GL.Shader( - GL.Shader.SCREEN_VERTEX_SHADER, - LGraphExposition.pixel_shader - ); - } + // default + this.properties = {code: LGAudioScript.default_code}; - var exp = this.properties.exposition; - var exp_input = this.getInputData(1); - if (exp_input != null) { - exp = this.properties.exposition = exp_input; - } - var uniforms = this._uniforms; + // create node + var ctx = LGAudio.getAudioContext(); + if (ctx.createScriptProcessor) { + this.audionode = ctx.createScriptProcessor(4096, 1, 1); + } else { // buffer size, input channels, output channels + console.warn("ScriptProcessorNode deprecated"); + this.audionode = ctx.createGain(); // bypass audio + } - //apply shader - temp.drawTo(function() { - gl.disable(gl.DEPTH_TEST); - tex.bind(0); - shader.uniforms(uniforms).draw(GL.Mesh.getScreenQuad()); - }); + this.processCode(); + if (!LGAudioScript._bypass_function) { + LGAudioScript._bypass_function = this.audionode.onaudioprocess; + } - this.setOutputData(0, temp); - }; + // slots + this.addInput("in", "audio"); + this.addOutput("out", "audio"); + } - LGraphExposition.pixel_shader = - "precision highp float;\n\ - varying vec2 v_coord;\n\ - uniform sampler2D u_texture;\n\ - uniform float u_exposition;\n\ - \n\ - void main() {\n\ - vec4 color = texture2D( u_texture, v_coord );\n\ - gl_FragColor = vec4( color.xyz * u_exposition, color.a );\n\ - }"; + LGAudioScript.prototype.onAdded = function(graph) { + if (graph.status == LGraph.STATUS_RUNNING) { + this.audionode.onaudioprocess = this._callback; + } + }; - LiteGraph.registerNodeType("texture/exposition", LGraphExposition); + LGAudioScript["@code"] = { widget: "code", type: "code" }; - function LGraphToneMapping() { - this.addInput("in", "Texture"); - this.addInput("avg", "number,Texture"); - this.addOutput("out", "Texture"); - this.properties = { - enabled: true, - scale: 1, - gamma: 1, - average_lum: 1, - lum_white: 1, - precision: LGraphTexture.LOW - }; + LGAudioScript.prototype.onStart = function() { + this.audionode.onaudioprocess = this._callback; + }; - this._uniforms = { - u_texture: 0, - u_lumwhite2: 1, - u_igamma: 1, - u_scale: 1, - u_average_lum: 1 - }; - } + LGAudioScript.prototype.onStop = function() { + this.audionode.onaudioprocess = LGAudioScript._bypass_function; + }; - LGraphToneMapping.title = "Tone Mapping"; - LGraphToneMapping.desc = - "Applies Tone Mapping to convert from high to low"; + LGAudioScript.prototype.onPause = function() { + this.audionode.onaudioprocess = LGAudioScript._bypass_function; + }; - LGraphToneMapping.widgets_info = { - precision: { widget: "combo", values: LGraphTexture.MODE_VALUES } - }; + LGAudioScript.prototype.onUnpause = function() { + this.audionode.onaudioprocess = this._callback; + }; - LGraphToneMapping.prototype.onGetInputs = function() { - return [["enabled", "boolean"]]; - }; + LGAudioScript.prototype.onExecute = function() { + // nothing! because we need an onExecute to receive onStart... fix that + }; - LGraphToneMapping.prototype.onExecute = function() { - var tex = this.getInputData(0); - if (!tex) { - return; - } + LGAudioScript.prototype.onRemoved = function() { + this.audionode.onaudioprocess = LGAudioScript._bypass_function; + }; - if (!this.isOutputConnected(0)) { - return; - } //saves work + LGAudioScript.prototype.processCode = function() { + try { + var func = new Function("properties", this.properties.code); + this._script = new func(this.properties); + this._old_code = this.properties.code; + this._callback = this._script.onaudioprocess; + } catch (err) { + console.error("Error in onaudioprocess code", err); + this._callback = LGAudioScript._bypass_function; + this.audionode.onaudioprocess = this._callback; + } + }; - if ( - this.properties.precision === LGraphTexture.PASS_THROUGH || - this.getInputOrProperty("enabled") === false - ) { - this.setOutputData(0, tex); - return; - } + LGAudioScript.prototype.onPropertyChanged = function(name, value) { + if (name == "code") { + this.properties.code = value; + this.processCode(); + if (this.graph && this.graph.status == LGraph.STATUS_RUNNING) { + this.audionode.onaudioprocess = this._callback; + } + } + }; - var temp = this._temp_texture; + LGAudioScript.default_function = function() { + this.onaudioprocess = function(audioProcessingEvent) { + // The input buffer is the song we loaded earlier + var inputBuffer = audioProcessingEvent.inputBuffer; - if ( - !temp || - temp.width != tex.width || - temp.height != tex.height || - temp.type != tex.type - ) { - temp = this._temp_texture = new GL.Texture( - tex.width, - tex.height, - { type: tex.type, format: gl.RGBA, filter: gl.LINEAR } - ); - } + // The output buffer contains the samples that will be modified and played + var outputBuffer = audioProcessingEvent.outputBuffer; - var avg = this.getInputData(1); - if (avg == null) { - avg = this.properties.average_lum; - } + // Loop through the output channels (in this case there is only one) + for ( + var channel = 0; + channel < outputBuffer.numberOfChannels; + channel++ + ) { + var inputData = inputBuffer.getChannelData(channel); + var outputData = outputBuffer.getChannelData(channel); - var uniforms = this._uniforms; - var shader = null; + // Loop through the 4096 samples + for (var sample = 0; sample < inputBuffer.length; sample++) { + // make output equal to the same as the input + outputData[sample] = inputData[sample]; + } + } + }; + }; - if (avg.constructor === Number) { - this.properties.average_lum = avg; - uniforms.u_average_lum = this.properties.average_lum; - shader = LGraphToneMapping._shader; - if (!shader) { - shader = LGraphToneMapping._shader = new GL.Shader( - GL.Shader.SCREEN_VERTEX_SHADER, - LGraphToneMapping.pixel_shader - ); - } - } else if (avg.constructor === GL.Texture) { - uniforms.u_average_texture = avg.bind(1); - shader = LGraphToneMapping._shader_texture; - if (!shader) { - shader = LGraphToneMapping._shader_texture = new GL.Shader( - GL.Shader.SCREEN_VERTEX_SHADER, - LGraphToneMapping.pixel_shader, - { AVG_TEXTURE: "" } - ); - } - } + LGAudio.createAudioNodeWrapper(LGAudioScript); - uniforms.u_lumwhite2 = - this.properties.lum_white * this.properties.lum_white; - uniforms.u_scale = this.properties.scale; - uniforms.u_igamma = 1 / this.properties.gamma; + LGAudioScript.title = "Script"; + LGAudioScript.desc = "apply script to signal"; + LiteGraph.registerNodeType("audio/script", LGAudioScript); - //apply shader - gl.disable(gl.DEPTH_TEST); - temp.drawTo(function() { - tex.bind(0); - shader.uniforms(uniforms).draw(GL.Mesh.getScreenQuad()); - }); + function LGAudioDestination() { + this.audionode = LGAudio.getAudioContext().destination; + this.addInput("in", "audio"); + } - this.setOutputData(0, this._temp_texture); - }; + LGAudioDestination.title = "Destination"; + LGAudioDestination.desc = "Audio output"; + LiteGraph.registerNodeType("audio/destination", LGAudioDestination); +})(this); - LGraphToneMapping.pixel_shader = - "precision highp float;\n\ - varying vec2 v_coord;\n\ - uniform sampler2D u_texture;\n\ - uniform float u_scale;\n\ - #ifdef AVG_TEXTURE\n\ - uniform sampler2D u_average_texture;\n\ - #else\n\ - uniform float u_average_lum;\n\ - #endif\n\ - uniform float u_lumwhite2;\n\ - uniform float u_igamma;\n\ - vec3 RGB2xyY (vec3 rgb)\n\ - {\n\ - const mat3 RGB2XYZ = mat3(0.4124, 0.3576, 0.1805,\n\ - 0.2126, 0.7152, 0.0722,\n\ - 0.0193, 0.1192, 0.9505);\n\ - vec3 XYZ = RGB2XYZ * rgb;\n\ - \n\ - float f = (XYZ.x + XYZ.y + XYZ.z);\n\ - return vec3(XYZ.x / f,\n\ - XYZ.y / f,\n\ - XYZ.y);\n\ - }\n\ - \n\ - void main() {\n\ - vec4 color = texture2D( u_texture, v_coord );\n\ - vec3 rgb = color.xyz;\n\ - float average_lum = 0.0;\n\ - #ifdef AVG_TEXTURE\n\ - vec3 pixel = texture2D(u_average_texture,vec2(0.5)).xyz;\n\ - average_lum = (pixel.x + pixel.y + pixel.z) / 3.0;\n\ - #else\n\ - average_lum = u_average_lum;\n\ - #endif\n\ - //Ld - this part of the code is the same for both versions\n\ - float lum = dot(rgb, vec3(0.2126, 0.7152, 0.0722));\n\ - float L = (u_scale / average_lum) * lum;\n\ - float Ld = (L * (1.0 + L / u_lumwhite2)) / (1.0 + L);\n\ - //first\n\ - //vec3 xyY = RGB2xyY(rgb);\n\ - //xyY.z *= Ld;\n\ - //rgb = xyYtoRGB(xyY);\n\ - //second\n\ - rgb = (rgb / lum) * Ld;\n\ - rgb = max(rgb,vec3(0.001));\n\ - rgb = pow( rgb, vec3( u_igamma ) );\n\ - gl_FragColor = vec4( rgb, color.a );\n\ - }"; +// event related nodes +(function(global) { + var LiteGraph = global.LiteGraph; + + function LGWebSocket() { + this.size = [60, 20]; + this.addInput("send", LiteGraph.ACTION); + this.addOutput("received", LiteGraph.EVENT); + this.addInput("in", 0); + this.addOutput("out", 0); + this.properties = { + url: "", + room: "lgraph", // allows to filter messages, + only_send_changes: true, + }; + this._ws = null; + this._last_sent_data = []; + this._last_received_data = []; + } - LiteGraph.registerNodeType("texture/tonemapping", LGraphToneMapping); + LGWebSocket.title = "WebSocket"; + LGWebSocket.desc = "Send data through a websocket"; - function LGraphTexturePerlin() { - this.addOutput("out", "Texture"); - this.properties = { - width: 512, - height: 512, - seed: 0, - persistence: 0.1, - octaves: 8, - scale: 1, - offset: [0, 0], - amplitude: 1, - precision: LGraphTexture.DEFAULT - }; - this._key = 0; - this._texture = null; - this._uniforms = { - u_persistence: 0.1, - u_seed: 0, - u_offset: vec2.create(), - u_scale: 1, - u_viewport: vec2.create() - }; - } + LGWebSocket.prototype.onPropertyChanged = function(name, value) { + if (name == "url") { + this.connectSocket(); + } + }; - LGraphTexturePerlin.title = "Perlin"; - LGraphTexturePerlin.desc = "Generates a perlin noise texture"; + LGWebSocket.prototype.onExecute = function() { + if (!this._ws && this.properties.url) { + this.connectSocket(); + } - LGraphTexturePerlin.widgets_info = { - precision: { widget: "combo", values: LGraphTexture.MODE_VALUES }, - width: { type: "number", precision: 0, step: 1 }, - height: { type: "number", precision: 0, step: 1 }, - octaves: { type: "number", precision: 0, step: 1, min: 1, max: 50 } - }; + if (!this._ws || this._ws.readyState != WebSocket.OPEN) { + return; + } - LGraphTexturePerlin.prototype.onGetInputs = function() { - return [ - ["seed", "number"], - ["persistence", "number"], - ["octaves", "number"], - ["scale", "number"], - ["amplitude", "number"], - ["offset", "vec2"] - ]; - }; + var room = this.properties.room; + var only_changes = this.properties.only_send_changes; - LGraphTexturePerlin.prototype.onExecute = function() { - if (!this.isOutputConnected(0)) { - return; - } //saves work - - var w = this.properties.width | 0; - var h = this.properties.height | 0; - if (w == 0) { - w = gl.viewport_data[2]; - } //0 means default - if (h == 0) { - h = gl.viewport_data[3]; - } //0 means default - var type = LGraphTexture.getTextureType(this.properties.precision); - - var temp = this._texture; - if ( - !temp || - temp.width != w || - temp.height != h || - temp.type != type - ) { - temp = this._texture = new GL.Texture(w, h, { - type: type, - format: gl.RGB, - filter: gl.LINEAR - }); - } + for (var i = 1; i < this.inputs.length; ++i) { + var data = this.getInputData(i); + if (data == null) { + continue; + } + var json; + try { + json = JSON.stringify({ + type: 0, + room: room, + channel: i, + data: data, + }); + } catch (err) { + continue; + } + if (only_changes && this._last_sent_data[i] == json) { + continue; + } - var persistence = this.getInputOrProperty("persistence"); - var octaves = this.getInputOrProperty("octaves"); - var offset = this.getInputOrProperty("offset"); - var scale = this.getInputOrProperty("scale"); - var amplitude = this.getInputOrProperty("amplitude"); - var seed = this.getInputOrProperty("seed"); + this._last_sent_data[i] = json; + this._ws.send(json); + } - //reusing old texture - var key = - "" + - w + - h + - type + - persistence + - octaves + - scale + - seed + - offset[0] + - offset[1] + - amplitude; - if (key == this._key) { - this.setOutputData(0, temp); - return; - } - this._key = key; - - //gather uniforms - var uniforms = this._uniforms; - uniforms.u_persistence = persistence; - uniforms.u_octaves = octaves; - uniforms.u_offset.set(offset); - uniforms.u_scale = scale; - uniforms.u_amplitude = amplitude; - uniforms.u_seed = seed * 128; - uniforms.u_viewport[0] = w; - uniforms.u_viewport[1] = h; - - //render - var shader = LGraphTexturePerlin._shader; - if (!shader) { - shader = LGraphTexturePerlin._shader = new GL.Shader( - GL.Shader.SCREEN_VERTEX_SHADER, - LGraphTexturePerlin.pixel_shader - ); - } + for (var i = 1; i < this.outputs.length; ++i) { + this.setOutputData(i, this._last_received_data[i]); + } - gl.disable(gl.BLEND); - gl.disable(gl.DEPTH_TEST); + if (this.boxcolor == "#AFA") { + this.boxcolor = "#6C6"; + } + }; - temp.drawTo(function() { - shader.uniforms(uniforms).draw(GL.Mesh.getScreenQuad()); - }); + LGWebSocket.prototype.connectSocket = function() { + var that = this; + var url = this.properties.url; + if (url.substr(0, 2) != "ws") { + url = "ws://" + url; + } + this._ws = new WebSocket(url); + this._ws.onopen = function() { + console.log("ready"); + that.boxcolor = "#6C6"; + }; + this._ws.onmessage = function(e) { + that.boxcolor = "#AFA"; + var data = JSON.parse(e.data); + if (data.room && data.room != that.properties.room) { + return; + } + if (data.type == 1) { + if ( + data.data.object_class && + LiteGraph[data.data.object_class] + ) { + var obj = null; + try { + obj = new LiteGraph[data.data.object_class](data.data); + that.triggerSlot(0, obj); + } catch (err) { + return; + } + } else { + that.triggerSlot(0, data.data); + } + } else { + that._last_received_data[data.channel || 0] = data.data; + } + }; + this._ws.onerror = function(e) { + console.log("couldnt connect to websocket"); + that.boxcolor = "#E88"; + }; + this._ws.onclose = function(e) { + console.log("connection closed"); + that.boxcolor = "#000"; + }; + }; - this.setOutputData(0, temp); - }; + LGWebSocket.prototype.send = function(data) { + if (!this._ws || this._ws.readyState != WebSocket.OPEN) { + return; + } + this._ws.send(JSON.stringify({ type: 1, msg: data })); + }; - LGraphTexturePerlin.pixel_shader = - "precision highp float;\n\ - varying vec2 v_coord;\n\ - uniform vec2 u_offset;\n\ - uniform float u_scale;\n\ - uniform float u_persistence;\n\ - uniform int u_octaves;\n\ - uniform float u_amplitude;\n\ - uniform vec2 u_viewport;\n\ - uniform float u_seed;\n\ - #define M_PI 3.14159265358979323846\n\ - \n\ - float rand(vec2 c){ return fract(sin(dot(c.xy ,vec2( 12.9898 + u_seed,78.233 + u_seed))) * 43758.5453); }\n\ - \n\ - float noise(vec2 p, float freq ){\n\ - float unit = u_viewport.x/freq;\n\ - vec2 ij = floor(p/unit);\n\ - vec2 xy = mod(p,unit)/unit;\n\ - //xy = 3.*xy*xy-2.*xy*xy*xy;\n\ - xy = .5*(1.-cos(M_PI*xy));\n\ - float a = rand((ij+vec2(0.,0.)));\n\ - float b = rand((ij+vec2(1.,0.)));\n\ - float c = rand((ij+vec2(0.,1.)));\n\ - float d = rand((ij+vec2(1.,1.)));\n\ - float x1 = mix(a, b, xy.x);\n\ - float x2 = mix(c, d, xy.x);\n\ - return mix(x1, x2, xy.y);\n\ - }\n\ - \n\ - float pNoise(vec2 p, int res){\n\ - float persistance = u_persistence;\n\ - float n = 0.;\n\ - float normK = 0.;\n\ - float f = 4.;\n\ - float amp = 1.0;\n\ - int iCount = 0;\n\ - for (int i = 0; i<50; i++){\n\ - n+=amp*noise(p, f);\n\ - f*=2.;\n\ - normK+=amp;\n\ - amp*=persistance;\n\ - if (iCount >= res)\n\ - break;\n\ - iCount++;\n\ - }\n\ - float nf = n/normK;\n\ - return nf*nf*nf*nf;\n\ - }\n\ - void main() {\n\ - vec2 uv = v_coord * u_scale * u_viewport + u_offset * u_scale;\n\ - vec4 color = vec4( pNoise( uv, u_octaves ) * u_amplitude );\n\ - gl_FragColor = color;\n\ - }"; + LGWebSocket.prototype.onAction = function(action, param) { + if (!this._ws || this._ws.readyState != WebSocket.OPEN) { + return; + } + this._ws.send({ + type: 1, + room: this.properties.room, + action: action, + data: param, + }); + }; - LiteGraph.registerNodeType("texture/perlin", LGraphTexturePerlin); + LGWebSocket.prototype.onGetInputs = function() { + return [["in", 0]]; + }; - function LGraphTextureCanvas2D() { - this.addInput("v"); - this.addOutput("out", "Texture"); - this.properties = { - code: LGraphTextureCanvas2D.default_code, - width: 512, - height: 512, - clear: true, - precision: LGraphTexture.DEFAULT, - use_html_canvas: false - }; - this._func = null; - this._temp_texture = null; - this.compileCode(); - } + LGWebSocket.prototype.onGetOutputs = function() { + return [["out", 0]]; + }; - LGraphTextureCanvas2D.title = "Canvas2D"; - LGraphTextureCanvas2D.desc = "Executes Canvas2D code inside a texture or the viewport."; - LGraphTextureCanvas2D.help = "Set width and height to 0 to match viewport size."; + LiteGraph.registerNodeType("network/websocket", LGWebSocket); - LGraphTextureCanvas2D.default_code = "//vars: canvas,ctx,time\nctx.fillStyle='red';\nctx.fillRect(0,0,50,50);\n"; + // It is like a websocket but using the SillyServer.js server that bounces packets back to all clients connected: + // For more information: https://github.com/jagenjo/SillyServer.js - LGraphTextureCanvas2D.widgets_info = { - precision: { widget: "combo", values: LGraphTexture.MODE_VALUES }, - code: { type: "code" }, - width: { type: "number", precision: 0, step: 1 }, - height: { type: "number", precision: 0, step: 1 } - }; + function LGSillyClient() { + // this.size = [60,20]; + this.room_widget = this.addWidget( + "text", + "Room", + "lgraph", + this.setRoom.bind(this), + ); + this.addWidget( + "button", + "Reconnect", + null, + this.connectSocket.bind(this), + ); - LGraphTextureCanvas2D.prototype.onPropertyChanged = function( name, value ) { - if (name == "code" ) - this.compileCode( value ); - } - - LGraphTextureCanvas2D.prototype.compileCode = function( code ) { - this._func = null; - if( !LiteGraph.allow_scripts ) - return; + this.addInput("send", LiteGraph.ACTION); + this.addOutput("received", LiteGraph.EVENT); + this.addInput("in", 0); + this.addOutput("out", 0); + this.properties = { + url: "tamats.com:55000", + room: "lgraph", + only_send_changes: true, + }; - try { - this._func = new Function( "canvas", "ctx", "time", "script","v", code ); - this.boxcolor = "#00FF00"; - } catch (err) { - this.boxcolor = "#FF0000"; - console.error("Error parsing script"); - console.error(err); - } - }; + this._server = null; + this.connectSocket(); + this._last_sent_data = []; + this._last_received_data = []; - LGraphTextureCanvas2D.prototype.onExecute = function() { - var func = this._func; - if (!func || !this.isOutputConnected(0)) { - return; - } - this.executeDraw( func ); - } + if(typeof(SillyClient) == "undefined") + console.warn("remember to add SillyClient.js to your project: https://tamats.com/projects/sillyserver/src/sillyclient.js"); + } - LGraphTextureCanvas2D.prototype.executeDraw = function( func_context ) { - - var width = this.properties.width || gl.canvas.width; - var height = this.properties.height || gl.canvas.height; - var temp = this._temp_texture; - var type = LGraphTexture.getTextureType( this.properties.precision ); - if (!temp || temp.width != width || temp.height != height || temp.type != type ) { - temp = this._temp_texture = new GL.Texture(width, height, { - format: gl.RGBA, - filter: gl.LINEAR, - type: type - }); - } + LGSillyClient.title = "SillyClient"; + LGSillyClient.desc = "Connects to SillyServer to broadcast messages"; - var v = this.getInputData(0); + LGSillyClient.prototype.onPropertyChanged = function(name, value) { + if (name == "room") { + this.room_widget.value = value; + } + this.connectSocket(); + }; - var properties = this.properties; - var that = this; - var time = this.graph.getTime(); - var ctx = gl; - var canvas = gl.canvas; - if( this.properties.use_html_canvas || !global.enableWebGLCanvas ) - { - if(!this._canvas) - { - canvas = this._canvas = createCanvas(width.height); - ctx = this._ctx = canvas.getContext("2d"); - } - else - { - canvas = this._canvas; - ctx = this._ctx; - } - canvas.width = width; - canvas.height = height; - } + LGSillyClient.prototype.setRoom = function(room_name) { + this.properties.room = room_name; + this.room_widget.value = room_name; + this.connectSocket(); + }; - if(ctx == gl) //using Canvas2DtoWebGL - temp.drawTo(function() { - gl.start2D(); - if(properties.clear) - { - gl.clearColor(0,0,0,0); - gl.clear( gl.COLOR_BUFFER_BIT ); - } + // force label names + LGSillyClient.prototype.onDrawForeground = function() { + for (var i = 1; i < this.inputs.length; ++i) { + var slot = this.inputs[i]; + slot.label = "in_" + i; + } + for (var i = 1; i < this.outputs.length; ++i) { + var slot = this.outputs[i]; + slot.label = "out_" + i; + } + }; - try { - if (func_context.draw) { - func_context.draw.call(that, canvas, ctx, time, func_context, v); - } else { - func_context.call(that, canvas, ctx, time, func_context,v); - } - that.boxcolor = "#00FF00"; - } catch (err) { - that.boxcolor = "#FF0000"; - console.error("Error executing script"); - console.error(err); - } - gl.finish2D(); - }); - else //rendering to offscreen canvas and uploading to texture - { - if(properties.clear) - ctx.clearRect(0,0,canvas.width,canvas.height); - - try { - if (func_context.draw) { - func_context.draw.call(this, canvas, ctx, time, func_context, v); - } else { - func_context.call(this, canvas, ctx, time, func_context,v); - } - this.boxcolor = "#00FF00"; - } catch (err) { - this.boxcolor = "#FF0000"; - console.error("Error executing script"); - console.error(err); - } - temp.uploadImage( canvas ); - } + LGSillyClient.prototype.onExecute = function() { + if (!this._server || !this._server.is_connected) { + return; + } - this.setOutputData(0, temp); - }; + var only_send_changes = this.properties.only_send_changes; - LiteGraph.registerNodeType("texture/canvas2D", LGraphTextureCanvas2D); + for (var i = 1; i < this.inputs.length; ++i) { + var data = this.getInputData(i); + var prev_data = this._last_sent_data[i]; + if (data != null) { + if (only_send_changes) { + var is_equal = true; + if( data && data.length && prev_data && prev_data.length == data.length && data.constructor !== String) { + for(var j = 0; j < data.length; ++j) + if( prev_data[j] != data[j] ) { + is_equal = false; + break; + } + } else if(this._last_sent_data[i] != data) + is_equal = false; + if(is_equal) + continue; + } + this._server.sendMessage({ type: 0, channel: i, data: data }); + if( data.length && data.constructor !== String ) { + if( this._last_sent_data[i] ) { + this._last_sent_data[i].length = data.length; + for(var j = 0; j < data.length; ++j) + this._last_sent_data[i][j] = data[j]; + } else { // create + if(data.constructor === Array) + this._last_sent_data[i] = data.concat(); + else + this._last_sent_data[i] = new data.constructor( data ); + } + } else + this._last_sent_data[i] = data; // should be cloned + } + } - // To do chroma keying ***************** + for (var i = 1; i < this.outputs.length; ++i) { + this.setOutputData(i, this._last_received_data[i]); + } - function LGraphTextureMatte() { - this.addInput("in", "Texture"); + if (this.boxcolor == "#AFA") { + this.boxcolor = "#6C6"; + } + }; - this.addOutput("out", "Texture"); - this.properties = { - key_color: vec3.fromValues(0, 1, 0), - threshold: 0.8, - slope: 0.2, - precision: LGraphTexture.DEFAULT - }; - } + LGSillyClient.prototype.connectSocket = function() { + var that = this; + if (typeof SillyClient == "undefined") { + if (!this._error) { + console.error("SillyClient node cannot be used, you must include SillyServer.js"); + } + this._error = true; + return; + } - LGraphTextureMatte.title = "Matte"; - LGraphTextureMatte.desc = "Extracts background"; + this._server = new SillyClient(); + this._server.on_ready = function() { + console.log("ready"); + that.boxcolor = "#6C6"; + }; + this._server.on_message = function(id, msg) { + var data = null; + try { + data = JSON.parse(msg); + } catch (err) { + return; + } - LGraphTextureMatte.widgets_info = { - key_color: { widget: "color" }, - precision: { widget: "combo", values: LGraphTexture.MODE_VALUES } - }; + if (data.type == 1) { + // EVENT slot + if ( + data.data.object_class && + LiteGraph[data.data.object_class] + ) { + var obj = null; + try { + obj = new LiteGraph[data.data.object_class](data.data); + that.triggerSlot(0, obj); + } catch (err) { + return; + } + } else { + that.triggerSlot(0, data.data); + } + } else { + that._last_received_data[data.channel || 0] = data.data; + } + that.boxcolor = "#AFA"; + }; + this._server.on_error = function(e) { + console.log("couldnt connect to websocket"); + that.boxcolor = "#E88"; + }; + this._server.on_close = function(e) { + console.log("connection closed"); + that.boxcolor = "#000"; + }; - LGraphTextureMatte.prototype.onExecute = function() { - if (!this.isOutputConnected(0)) { - return; - } //saves work + if (this.properties.url && this.properties.room) { + try { + this._server.connect(this.properties.url, this.properties.room); + } catch (err) { + console.error("SillyServer error: " + err); + this._server = null; + return; + } + this._final_url = this.properties.url + "/" + this.properties.room; + } + }; - var tex = this.getInputData(0); + LGSillyClient.prototype.send = function(data) { + if (!this._server || !this._server.is_connected) { + return; + } + this._server.sendMessage({ type: 1, data: data }); + }; - if (this.properties.precision === LGraphTexture.PASS_THROUGH) { - this.setOutputData(0, tex); - return; - } + LGSillyClient.prototype.onAction = function(action, param) { + if (!this._server || !this._server.is_connected) { + return; + } + this._server.sendMessage({ type: 1, action: action, data: param }); + }; - if (!tex) { - return; - } + LGSillyClient.prototype.onGetInputs = function() { + return [["in", 0]]; + }; - this._tex = LGraphTexture.getTargetTexture( - tex, - this._tex, - this.properties.precision - ); - - gl.disable(gl.BLEND); - gl.disable(gl.DEPTH_TEST); - - if (!this._uniforms) { - this._uniforms = { - u_texture: 0, - u_key_color: this.properties.key_color, - u_threshold: 1, - u_slope: 1 - }; - } - var uniforms = this._uniforms; - - var mesh = Mesh.getScreenQuad(); - var shader = LGraphTextureMatte._shader; - if (!shader) { - shader = LGraphTextureMatte._shader = new GL.Shader( - GL.Shader.SCREEN_VERTEX_SHADER, - LGraphTextureMatte.pixel_shader - ); - } + LGSillyClient.prototype.onGetOutputs = function() { + return [["out", 0]]; + }; - uniforms.u_key_color = this.properties.key_color; - uniforms.u_threshold = this.properties.threshold; - uniforms.u_slope = this.properties.slope; + LiteGraph.registerNodeType("network/sillyclient", LGSillyClient); - this._tex.drawTo(function() { - tex.bind(0); - shader.uniforms(uniforms).draw(mesh); - }); + // HTTP Request + function HTTPRequestNode() { + this.addInput("request", LiteGraph.ACTION); + this.addInput("url", "string"); + this.addProperty("url", ""); + this.addOutput("ready", LiteGraph.EVENT); + this.addOutput("data", "string"); + this.addWidget("button", "Fetch", null, this.fetch.bind(this)); + this._data = null; + this._fetching = null; + } - this.setOutputData(0, this._tex); - }; + HTTPRequestNode.title = "HTTP Request"; + HTTPRequestNode.desc = "Fetch data through HTTP"; - LGraphTextureMatte.pixel_shader = - "precision highp float;\n\ - varying vec2 v_coord;\n\ - uniform sampler2D u_texture;\n\ - uniform vec3 u_key_color;\n\ - uniform float u_threshold;\n\ - uniform float u_slope;\n\ - \n\ - void main() {\n\ - vec3 color = texture2D( u_texture, v_coord ).xyz;\n\ - float diff = length( normalize(color) - normalize(u_key_color) );\n\ - float edge = u_threshold * (1.0 - u_slope);\n\ - float alpha = smoothstep( edge, u_threshold, diff);\n\ - gl_FragColor = vec4( color, alpha );\n\ - }"; + HTTPRequestNode.prototype.fetch = function() { + var url = this.getInputData(1) || this.properties.url; + if(!url) + return; - LiteGraph.registerNodeType("texture/matte", LGraphTextureMatte); + this.boxcolor = "#FF0"; + var that = this; + this._fetching = fetch(url) + .then((resp) => { + if(!resp.ok) { + this.boxcolor = "#F00"; + that.trigger("error"); + } else { + this.boxcolor = "#0F0"; + return resp.text(); + } + }) + .then((data) => { + that._data = data; + that._fetching = null; + that.trigger("ready"); + }); + } - //*********************************** - function LGraphCubemapToTexture2D() { - this.addInput("in", "texture"); - this.addInput("yaw", "number"); - this.addOutput("out", "texture"); - this.properties = { yaw: 0 }; - } + HTTPRequestNode.prototype.onAction = function(evt) { + if(evt == "request") + this.fetch(); + } - LGraphCubemapToTexture2D.title = "CubemapToTexture2D"; - LGraphCubemapToTexture2D.desc = "Transforms a CUBEMAP texture into a TEXTURE2D in Polar Representation"; + HTTPRequestNode.prototype.onExecute = function() { + this.setOutputData(1, this._data); + }; - LGraphCubemapToTexture2D.prototype.onExecute = function() { - if (!this.isOutputConnected(0)) - return; + HTTPRequestNode.prototype.onGetOutputs = function() { + return [["error",LiteGraph.EVENT]]; + } + + LiteGraph.registerNodeType("network/httprequest", HTTPRequestNode); - var tex = this.getInputData(0); - if ( !tex || tex.texture_type != GL.TEXTURE_CUBE_MAP ) - return; - if( this._last_tex && ( this._last_tex.height != tex.height || this._last_tex.type != tex.type )) - this._last_tex = null; - var yaw = this.getInputOrProperty("yaw"); - this._last_tex = GL.Texture.cubemapToTexture2D( tex, tex.height, this._last_tex, true, yaw ); - this.setOutputData( 0, this._last_tex ); - }; - LiteGraph.registerNodeType( "texture/cubemapToTexture2D", LGraphCubemapToTexture2D ); -})(this); -(function(global) { - - if (typeof GL == "undefined") - return; - - var LiteGraph = global.LiteGraph; - var LGraphCanvas = global.LGraphCanvas; - - var SHADERNODES_COLOR = "#345"; - - var LGShaders = LiteGraph.Shaders = {}; - - var GLSL_types = LGShaders.GLSL_types = ["float","vec2","vec3","vec4","mat3","mat4","sampler2D","samplerCube"]; - var GLSL_types_const = LGShaders.GLSL_types_const = ["float","vec2","vec3","vec4"]; - - var GLSL_functions_desc = { - "radians": "T radians(T degrees)", - "degrees": "T degrees(T radians)", - "sin": "T sin(T angle)", - "cos": "T cos(T angle)", - "tan": "T tan(T angle)", - "asin": "T asin(T x)", - "acos": "T acos(T x)", - "atan": "T atan(T x)", - "atan2": "T atan(T x,T y)", - "pow": "T pow(T x,T y)", - "exp": "T exp(T x)", - "log": "T log(T x)", - "exp2": "T exp2(T x)", - "log2": "T log2(T x)", - "sqrt": "T sqrt(T x)", - "inversesqrt": "T inversesqrt(T x)", - "abs": "T abs(T x)", - "sign": "T sign(T x)", - "floor": "T floor(T x)", - "round": "T round(T x)", - "ceil": "T ceil(T x)", - "fract": "T fract(T x)", - "mod": "T mod(T x,T y)", //"T mod(T x,float y)" - "min": "T min(T x,T y)", - "max": "T max(T x,T y)", - "clamp": "T clamp(T x,T minVal = 0.0,T maxVal = 1.0)", - "mix": "T mix(T x,T y,T a)", //"T mix(T x,T y,float a)" - "step": "T step(T edge, T edge2, T x)", //"T step(float edge, T x)" - "smoothstep": "T smoothstep(T edge, T edge2, T x)", //"T smoothstep(float edge, T x)" - "length":"float length(T x)", - "distance":"float distance(T p0, T p1)", - "normalize":"T normalize(T x)", - "dot": "float dot(T x,T y)", - "cross": "vec3 cross(vec3 x,vec3 y)", - "reflect": "vec3 reflect(vec3 V,vec3 N)", - "refract": "vec3 refract(vec3 V,vec3 N, float IOR)" - }; - - //parse them - var GLSL_functions = {}; - var GLSL_functions_name = []; - parseGLSLDescriptions(); - - LGShaders.ALL_TYPES = "float,vec2,vec3,vec4"; - - function parseGLSLDescriptions() - { - GLSL_functions_name.length = 0; - - for(var i in GLSL_functions_desc) - { - var op = GLSL_functions_desc[i]; - var index = op.indexOf(" "); - var return_type = op.substr(0,index); - var index2 = op.indexOf("(",index); - var func_name = op.substr(index,index2-index).trim(); - var params = op.substr(index2 + 1, op.length - index2 - 2).split(","); - for(var j in params) - { - var p = params[j].split(" ").filter(function(a){ return a; }); - params[j] = { type: p[0].trim(), name: p[1].trim() }; - if(p[2] == "=") - params[j].value = p[3].trim(); - } - GLSL_functions[i] = { return_type: return_type, func: func_name, params: params }; - GLSL_functions_name.push( func_name ); - //console.log( GLSL_functions[i] ); - } - } - - //common actions to all shader node classes - function registerShaderNode( type, node_ctor ) - { - //static attributes - node_ctor.color = SHADERNODES_COLOR; - node_ctor.filter = "shader"; - - //common methods - node_ctor.prototype.clearDestination = function(){ this.shader_destination = {}; } - node_ctor.prototype.propagateDestination = function propagateDestination( dest_name ) - { - this.shader_destination[ dest_name ] = true; - if(this.inputs) - for(var i = 0; i < this.inputs.length; ++i) - { - var origin_node = this.getInputNode(i); - if(origin_node) - origin_node.propagateDestination( dest_name ); - } - } - if(!node_ctor.prototype.onPropertyChanged) - node_ctor.prototype.onPropertyChanged = function() - { - if(this.graph) - this.graph._version++; - } - - /* - if(!node_ctor.prototype.onGetCode) - node_ctor.prototype.onGetCode = function() - { - //check destination to avoid lonely nodes - if(!this.shader_destination) - return; - //grab inputs with types - var inputs = []; - if(this.inputs) - for(var i = 0; i < this.inputs.length; ++i) - inputs.push({ type: this.getInputData(i), name: getInputLinkID(this,i) }); - var outputs = []; - if(this.outputs) - for(var i = 0; i < this.outputs.length; ++i) - outputs.push({ name: getOutputLinkID(this,i) }); - //pass to code func - var results = this.extractCode(inputs); - //grab output, pass to next - if(results) - for(var i = 0; i < results.length; ++i) - { - var r = results[i]; - if(!r) - continue; - this.setOutputData(i,r.value); - } - } - */ - - LiteGraph.registerNodeType( "shader::" + type, node_ctor ); - } - - function getShaderNodeVarName( node, name ) - { - return "VAR_" + (name || "TEMP") + "_" + node.id; - } - - function getInputLinkID( node, slot ) - { - if(!node.inputs) - return null; - var link = node.getInputLink( slot ); - if( !link ) - return null; - var origin_node = node.graph.getNodeById( link.origin_id ); - if( !origin_node ) - return null; - if(origin_node.getOutputVarName) - return origin_node.getOutputVarName(link.origin_slot); - //generate - return "link_" + origin_node.id + "_" + link.origin_slot; - } - - function getOutputLinkID( node, slot ) - { - if (!node.isOutputConnected(slot)) - return null; - return "link_" + node.id + "_" + slot; - } - - LGShaders.registerShaderNode = registerShaderNode; - LGShaders.getInputLinkID = getInputLinkID; - LGShaders.getOutputLinkID = getOutputLinkID; - LGShaders.getShaderNodeVarName = getShaderNodeVarName; - LGShaders.parseGLSLDescriptions = parseGLSLDescriptions; - - //given a const number, it transform it to a string that matches a type - var valueToGLSL = LiteGraph.valueToGLSL = function valueToGLSL( v, type, precision ) - { - var n = 5; //num decimals - if(precision != null) - n = precision; - if(!type) - { - if(v.constructor === Number) - type = "float"; - else if(v.length) - { - switch(v.length) - { - case 2: type = "vec2"; break; - case 3: type = "vec3"; break; - case 4: type = "vec4"; break; - case 9: type = "mat3"; break; - case 16: type = "mat4"; break; - default: - throw("unknown type for glsl value size"); - } - } - else - throw("unknown type for glsl value: " + v.constructor); - } - switch(type) - { - case 'float': return v.toFixed(n); break; - case 'vec2': return "vec2(" + v[0].toFixed(n) + "," + v[1].toFixed(n) + ")"; break; - case 'color3': - case 'vec3': return "vec3(" + v[0].toFixed(n) + "," + v[1].toFixed(n) + "," + v[2].toFixed(n) + ")"; break; - case 'color4': - case 'vec4': return "vec4(" + v[0].toFixed(n) + "," + v[1].toFixed(n) + "," + v[2].toFixed(n) + "," + v[3].toFixed(n) + ")"; break; - case 'mat3': return "mat3(1.0,0.0,0.0,0.0,1.0,0.0,0.0,0.0,1.0)"; break; //not fully supported yet - case 'mat4': return "mat4(1.0,0.0,0.0,0.0,0.0,1.0,0.0,0.0,0.0,0.0,0.0,1.0,0.0,0.0,0.0,0.0,1.0)"; break;//not fully supported yet - default: - throw("unknown glsl type in valueToGLSL:", type); - } - - return ""; - } - - //makes sure that a var is of a type, and if not, it converts it - var varToTypeGLSL = LiteGraph.varToTypeGLSL = function varToTypeGLSL( v, input_type, output_type ) - { - if(input_type == output_type) - return v; - if(v == null) - switch(output_type) - { - case "float": return "0.0"; - case "vec2": return "vec2(0.0)"; - case "vec3": return "vec3(0.0)"; - case "vec4": return "vec4(0.0,0.0,0.0,1.0)"; - default: //null - return null; - } - - if(!output_type) - throw("error: no output type specified"); - if(output_type == "float") - { - switch(input_type) - { - //case "float": - case "vec2": - case "vec3": - case "vec4": - return v + ".x"; - break; - default: //null - return "0.0"; - break; - } - } - else if(output_type == "vec2") - { - switch(input_type) - { - case "float": - return "vec2("+v+")"; - //case "vec2": - case "vec3": - case "vec4": - return v + ".xy"; - default: //null - return "vec2(0.0)"; - } - } - else if(output_type == "vec3") - { - switch(input_type) - { - case "float": - return "vec3("+v+")"; - case "vec2": - return "vec3(" + v + ",0.0)"; - //case "vec3": - case "vec4": - return v + ".xyz"; - default: //null - return "vec3(0.0)"; - } - } - else if(output_type == "vec4") - { - switch(input_type) - { - case "float": - return "vec4("+v+")"; - case "vec2": - return "vec4(" + v + ",0.0,1.0)"; - case "vec3": - return "vec4(" + v + ",1.0)"; - default: //null - return "vec4(0.0,0.0,0.0,1.0)"; - } - } - throw("type cannot be converted"); - } - - - //used to plug incompatible stuff - var convertVarToGLSLType = LiteGraph.convertVarToGLSLType = function convertVarToGLSLType( varname, type, target_type ) - { - if(type == target_type) - return varname; - if(type == "float") - return target_type + "(" + varname + ")"; - if(target_type == "vec2") //works for vec2,vec3 and vec4 - return "vec2(" + varname + ".xy)"; - if(target_type == "vec3") //works for vec2,vec3 and vec4 - { - if(type == "vec2") - return "vec3(" + varname + ",0.0)"; - if(type == "vec4") - return "vec4(" + varname + ".xyz)"; - } - if(target_type == "vec4") - { - if(type == "vec2") - return "vec4(" + varname + ",0.0,0.0)"; - if(target_type == "vec3") - return "vec4(" + varname + ",1.0)"; - } - return null; - } - - //used to host a shader body ************************************** - function LGShaderContext() - { - //to store the code template - this.vs_template = ""; - this.fs_template = ""; - - //required so nodes now where to fetch the input data - this.buffer_names = { - uvs: "v_coord" - }; - - this.extra = {}; //to store custom info from the nodes (like if this shader supports a feature, etc) - - this._functions = {}; - this._uniforms = {}; - this._codeparts = {}; - this._uniform_value = null; - } - - LGShaderContext.prototype.clear = function() - { - this._uniforms = {}; - this._functions = {}; - this._codeparts = {}; - this._uniform_value = null; - - this.extra = {}; - } - - LGShaderContext.prototype.addUniform = function( name, type, value ) - { - this._uniforms[ name ] = type; - if(value != null) - { - if(!this._uniform_value) - this._uniform_value = {}; - this._uniform_value[name] = value; - } - } - - LGShaderContext.prototype.addFunction = function( name, code ) - { - this._functions[name] = code; - } - - LGShaderContext.prototype.addCode = function( hook, code, destinations ) - { - destinations = destinations || {"":""}; - for(var i in destinations) - { - var h = i ? i + "_" + hook : hook; - if(!this._codeparts[ h ]) - this._codeparts[ h ] = code + "\n"; - else - this._codeparts[ h ] += code + "\n"; - } - } - - //the system works by grabbing code fragments from every node and concatenating them in blocks depending on where must they be attached - LGShaderContext.prototype.computeCodeBlocks = function( graph, extra_uniforms ) - { - //prepare context - this.clear(); - - //grab output nodes - var vertexout = graph.findNodesByType("shader::output/vertex"); - vertexout = vertexout && vertexout.length ? vertexout[0] : null; - var fragmentout = graph.findNodesByType("shader::output/fragcolor"); - fragmentout = fragmentout && fragmentout.length ? fragmentout[0] : null; - if(!fragmentout) //?? - return null; - - //propagate back destinations - graph.sendEventToAllNodes( "clearDestination" ); - if(vertexout) - vertexout.propagateDestination("vs"); - if(fragmentout) - fragmentout.propagateDestination("fs"); - - //gets code from graph - graph.sendEventToAllNodes("onGetCode", this ); - - var uniforms = ""; - for(var i in this._uniforms) - uniforms += "uniform " + this._uniforms[i] + " " + i + ";\n"; - if(extra_uniforms) - for(var i in extra_uniforms) - uniforms += "uniform " + extra_uniforms[i] + " " + i + ";\n"; - - var functions = ""; - for(var i in this._functions) - functions += "//" + i + "\n" + this._functions[i] + "\n"; - - var blocks = this._codeparts; - blocks.uniforms = uniforms; - blocks.functions = functions; - return blocks; - } - - //replaces blocks using the vs and fs template and returns the final codes - LGShaderContext.prototype.computeShaderCode = function( graph ) - { - var blocks = this.computeCodeBlocks( graph ); - var vs_code = GL.Shader.replaceCodeUsingContext( this.vs_template, blocks ); - var fs_code = GL.Shader.replaceCodeUsingContext( this.fs_template, blocks ); - return { - vs_code: vs_code, - fs_code: fs_code - }; - } - - //generates the shader code from the template and the - LGShaderContext.prototype.computeShader = function( graph, shader ) - { - var finalcode = this.computeShaderCode( graph ); - console.log( finalcode.vs_code, finalcode.fs_code ); - - if(!LiteGraph.catch_exceptions) - { - this._shader_error = true; - if(shader) - shader.updateShader( finalcode.vs_code, finalcode.fs_code ); - else - shader = new GL.Shader( finalcode.vs_code, finalcode.fs_code ); - this._shader_error = false; - return shader; - } - - try - { - if(shader) - shader.updateShader( finalcode.vs_code, finalcode.fs_code ); - else - shader = new GL.Shader( finalcode.vs_code, finalcode.fs_code ); - this._shader_error = false; - return shader; - } - catch (err) - { - if(!this._shader_error) - { - console.error(err); - if(err.indexOf("Fragment shader") != -1) - console.log( finalcode.fs_code.split("\n").map(function(v,i){ return i + ".- " + v; }).join("\n") ); - else - console.log( finalcode.vs_code ); - } - this._shader_error = true; - return null; - } - - return null;//never here - } - - LGShaderContext.prototype.getShader = function( graph ) - { - //if graph not changed? - if(this._shader && this._shader._version == graph._version) - return this._shader; - - //compile shader - var shader = this.computeShader( graph, this._shader ); - if(!shader) - return null; - - this._shader = shader; - shader._version = graph._version; - return shader; - } - - //some shader nodes could require to fill the box with some uniforms - LGShaderContext.prototype.fillUniforms = function( uniforms, param ) - { - if(!this._uniform_value) - return; - - for(var i in this._uniform_value) - { - var v = this._uniform_value[i]; - if(v == null) - continue; - if(v.constructor === Function) - uniforms[i] = v.call( this, param ); - else if(v.constructor === GL.Texture) - { - //todo... - } - else - uniforms[i] = v; - } - } - - LiteGraph.ShaderContext = LiteGraph.Shaders.Context = LGShaderContext; - - // LGraphShaderGraph ***************************** - // applies a shader graph to texture, it can be uses as an example - - function LGraphShaderGraph() { - - //before inputs - this.subgraph = new LiteGraph.LGraph(); - this.subgraph._subgraph_node = this; - this.subgraph._is_subgraph = true; - this.subgraph.filter = "shader"; - - this.addInput("in", "texture"); - this.addOutput("out", "texture"); - this.properties = { width: 0, height: 0, alpha: false, precision: typeof(LGraphTexture) != "undefined" ? LGraphTexture.DEFAULT : 2 }; - - var inputNode = this.subgraph.findNodesByType("shader::input/uniform")[0]; - inputNode.pos = [200,300]; - - var sampler = LiteGraph.createNode("shader::texture/sampler2D"); - sampler.pos = [400,300]; - this.subgraph.add( sampler ); - - var outnode = LiteGraph.createNode("shader::output/fragcolor"); - outnode.pos = [600,300]; - this.subgraph.add( outnode ); - - inputNode.connect( 0, sampler ); - sampler.connect( 0, outnode ); - - this.size = [180,60]; - this.redraw_on_mouse = true; //force redraw - - this._uniforms = {}; - this._shader = null; - this._context = new LGShaderContext(); - this._context.vs_template = "#define VERTEX\n" + GL.Shader.SCREEN_VERTEX_SHADER; - this._context.fs_template = LGraphShaderGraph.template; - } - - LGraphShaderGraph.template = "\n\ -#define FRAGMENT\n\ -precision highp float;\n\ -varying vec2 v_coord;\n\ -{{varying}}\n\ -{{uniforms}}\n\ -{{functions}}\n\ -{{fs_functions}}\n\ -void main() {\n\n\ -vec2 uv = v_coord;\n\ -vec4 fragcolor = vec4(0.0);\n\ -vec4 fragcolor1 = vec4(0.0);\n\ -{{fs_code}}\n\ -gl_FragColor = fragcolor;\n\ -}\n\ - "; - - LGraphShaderGraph.widgets_info = { - precision: { widget: "combo", values: LGraphTexture.MODE_VALUES } - }; - - LGraphShaderGraph.title = "ShaderGraph"; - LGraphShaderGraph.desc = "Builds a shader using a graph"; - LGraphShaderGraph.input_node_type = "input/uniform"; - LGraphShaderGraph.output_node_type = "output/fragcolor"; - LGraphShaderGraph.title_color = SHADERNODES_COLOR; - - LGraphShaderGraph.prototype.onSerialize = function(o) - { - o.subgraph = this.subgraph.serialize(); - } - - LGraphShaderGraph.prototype.onConfigure = function(o) - { - this.subgraph.configure(o.subgraph); - } - - LGraphShaderGraph.prototype.onExecute = function() { - if (!this.isOutputConnected(0)) - return; - - //read input texture - var intex = this.getInputData(0); - if(intex && intex.constructor != GL.Texture) - intex = null; - - var w = this.properties.width | 0; - var h = this.properties.height | 0; - if (w == 0) { - w = intex ? intex.width : gl.viewport_data[2]; - } //0 means default - if (h == 0) { - h = intex ? intex.height : gl.viewport_data[3]; - } //0 means default - - var type = LGraphTexture.getTextureType( this.properties.precision, intex ); - - var texture = this._texture; - if ( !texture || texture.width != w || texture.height != h || texture.type != type ) { - texture = this._texture = new GL.Texture(w, h, { - type: type, - format: this.alpha ? gl.RGBA : gl.RGB, - filter: gl.LINEAR - }); - } - - var shader = this.getShader( this.subgraph ); - if(!shader) - return; - - var uniforms = this._uniforms; - this._context.fillUniforms( uniforms ); - - var tex_slot = 0; - if(this.inputs) - for(var i = 0; i < this.inputs.length; ++i) - { - var input = this.inputs[i]; - var data = this.getInputData(i); - if(input.type == "texture") - { - if(!data) - data = GL.Texture.getWhiteTexture(); - data = data.bind(tex_slot++); - } - - if(data != null) - uniforms[ "u_" + input.name ] = data; - } - - var mesh = GL.Mesh.getScreenQuad(); - - gl.disable( gl.DEPTH_TEST ); - gl.disable( gl.BLEND ); - - texture.drawTo(function(){ - shader.uniforms( uniforms ); - shader.draw( mesh ); - }); - - //use subgraph output - this.setOutputData(0, texture ); - }; - - //add input node inside subgraph - LGraphShaderGraph.prototype.onInputAdded = function( slot_info ) - { - var subnode = LiteGraph.createNode("shader::input/uniform"); - subnode.setProperty("name",slot_info.name); - subnode.setProperty("type",slot_info.type); - this.subgraph.add( subnode ); - } - - //remove all - LGraphShaderGraph.prototype.onInputRemoved = function( slot, slot_info ) - { - var nodes = this.subgraph.findNodesByType("shader::input/uniform"); - for(var i = 0; i < nodes.length; ++i) - { - var node = nodes[i]; - if(node.properties.name == slot_info.name ) - this.subgraph.remove( node ); - } - } - - LGraphShaderGraph.prototype.computeSize = function() - { - var num_inputs = this.inputs ? this.inputs.length : 0; - var num_outputs = this.outputs ? this.outputs.length : 0; - return [ 200, Math.max(num_inputs,num_outputs) * LiteGraph.NODE_SLOT_HEIGHT + LiteGraph.NODE_TITLE_HEIGHT + 10]; - } - - LGraphShaderGraph.prototype.getShader = function() - { - var shader = this._context.getShader( this.subgraph ); - if(!shader) - this.boxcolor = "red"; - else - this.boxcolor = null; - return shader; - } - - LGraphShaderGraph.prototype.onDrawBackground = function(ctx, graphcanvas, canvas, pos) - { - if(this.flags.collapsed) - return; - - //allows to preview the node if the canvas is a webgl canvas - var tex = this.getOutputData(0); - var inputs_y = this.inputs ? this.inputs.length * LiteGraph.NODE_SLOT_HEIGHT : 0; - if (tex && ctx == tex.gl && this.size[1] > inputs_y + LiteGraph.NODE_TITLE_HEIGHT ) { - ctx.drawImage( tex, 10,y, this.size[0] - 20, this.size[1] - inputs_y - LiteGraph.NODE_TITLE_HEIGHT ); - } - - var y = this.size[1] - LiteGraph.NODE_TITLE_HEIGHT + 0.5; - - //button - var over = LiteGraph.isInsideRectangle(pos[0],pos[1],this.pos[0],this.pos[1] + y,this.size[0],LiteGraph.NODE_TITLE_HEIGHT); - ctx.fillStyle = over ? "#555" : "#222"; - ctx.beginPath(); - if (this._shape == LiteGraph.BOX_SHAPE) - ctx.rect(0, y, this.size[0]+1, LiteGraph.NODE_TITLE_HEIGHT); - else - ctx.roundRect( 0, y, this.size[0]+1, LiteGraph.NODE_TITLE_HEIGHT, 0, 8); - ctx.fill(); - - //button - ctx.textAlign = "center"; - ctx.font = "24px Arial"; - ctx.fillStyle = over ? "#DDD" : "#999"; - ctx.fillText( "+", this.size[0] * 0.5, y + 24 ); - } - - LGraphShaderGraph.prototype.onMouseDown = function(e, localpos, graphcanvas) - { - var y = this.size[1] - LiteGraph.NODE_TITLE_HEIGHT + 0.5; - if(localpos[1] > y) - { - graphcanvas.showSubgraphPropertiesDialog(this); - } - } - - LGraphShaderGraph.prototype.onDrawSubgraphBackground = function(graphcanvas) - { - //TODO - } - - LGraphShaderGraph.prototype.getExtraMenuOptions = function(graphcanvas) - { - var that = this; - var options = [{ content: "Print Code", callback: function(){ - var code = that._context.computeShaderCode(); - console.log( code.vs_code, code.fs_code ); - }}]; - - return options; - } - - LiteGraph.registerNodeType( "texture/shaderGraph", LGraphShaderGraph ); - - function shaderNodeFromFunction( classname, params, return_type, code ) - { - //TODO - } - - //Shader Nodes *********************************************************** - - //applies a shader graph to a code - function LGraphShaderUniform() { - this.addOutput("out", ""); - this.properties = { name: "", type: "" }; - } - - LGraphShaderUniform.title = "Uniform"; - LGraphShaderUniform.desc = "Input data for the shader"; - - LGraphShaderUniform.prototype.getTitle = function() - { - if( this.properties.name && this.flags.collapsed) - return this.properties.type + " " + this.properties.name; - return "Uniform"; - } - - LGraphShaderUniform.prototype.onPropertyChanged = function(name,value) - { - this.outputs[0].name = this.properties.type + " " + this.properties.name; - } - - LGraphShaderUniform.prototype.onGetCode = function( context ) - { - if(!this.shader_destination) - return; - - var type = this.properties.type; - if( !type ) - { - if( !context.onGetPropertyInfo ) - return; - var info = context.onGetPropertyInfo( this.property.name ); - if(!info) - return; - type = info.type; - } - if(type == "number") - type = "float"; - else if(type == "texture") - type = "sampler2D"; - if ( LGShaders.GLSL_types.indexOf(type) == -1 ) - return; - - context.addUniform( "u_" + this.properties.name, type ); - this.setOutputData( 0, type ); - } - - LGraphShaderUniform.prototype.getOutputVarName = function(slot) - { - return "u_" + this.properties.name; - } - - registerShaderNode( "input/uniform", LGraphShaderUniform ); - - - function LGraphShaderAttribute() { - this.addOutput("out", "vec2"); - this.properties = { name: "coord", type: "vec2" }; - } - - LGraphShaderAttribute.title = "Attribute"; - LGraphShaderAttribute.desc = "Input data from mesh attribute"; - - LGraphShaderAttribute.prototype.getTitle = function() - { - return "att. " + this.properties.name; - } - - LGraphShaderAttribute.prototype.onGetCode = function( context ) - { - if(!this.shader_destination) - return; - - var type = this.properties.type; - if( !type || LGShaders.GLSL_types.indexOf(type) == -1 ) - return; - if(type == "number") - type = "float"; - if( this.properties.name != "coord") - { - context.addCode( "varying", " varying " + type +" v_" + this.properties.name + ";" ); - //if( !context.varyings[ this.properties.name ] ) - //context.addCode( "vs_code", "v_" + this.properties.name + " = " + input_name + ";" ); - } - this.setOutputData( 0, type ); - } - - LGraphShaderAttribute.prototype.getOutputVarName = function(slot) - { - return "v_" + this.properties.name; - } - - registerShaderNode( "input/attribute", LGraphShaderAttribute ); - - function LGraphShaderSampler2D() { - this.addInput("tex", "sampler2D"); - this.addInput("uv", "vec2"); - this.addOutput("rgba", "vec4"); - this.addOutput("rgb", "vec3"); - } - - LGraphShaderSampler2D.title = "Sampler2D"; - LGraphShaderSampler2D.desc = "Reads a pixel from a texture"; - - LGraphShaderSampler2D.prototype.onGetCode = function( context ) - { - if(!this.shader_destination) - return; - - var texname = getInputLinkID( this, 0 ); - var varname = getShaderNodeVarName(this); - var code = "vec4 " + varname + " = vec4(0.0);\n"; - if(texname) - { - var uvname = getInputLinkID( this, 1 ) || context.buffer_names.uvs; - code += varname + " = texture2D("+texname+","+uvname+");\n"; - } - - var link0 = getOutputLinkID( this, 0 ); - if(link0) - code += "vec4 " + getOutputLinkID( this, 0 ) + " = "+varname+";\n"; - - var link1 = getOutputLinkID( this, 1 ); - if(link1) - code += "vec3 " + getOutputLinkID( this, 1 ) + " = "+varname+".xyz;\n"; - - context.addCode( "code", code, this.shader_destination ); - this.setOutputData( 0, "vec4" ); - this.setOutputData( 1, "vec3" ); - } - - registerShaderNode( "texture/sampler2D", LGraphShaderSampler2D ); - - //********************************* - - function LGraphShaderConstant() - { - this.addOutput("","float"); - - this.properties = { - type: "float", - value: 0 - }; - - this.addWidget("combo","type","float",null, { values: GLSL_types_const, property: "type" } ); - this.updateWidgets(); - } - - LGraphShaderConstant.title = "const"; - - LGraphShaderConstant.prototype.getTitle = function() - { - if(this.flags.collapsed) - return valueToGLSL( this.properties.value, this.properties.type, 2 ); - return "Const"; - } - - LGraphShaderConstant.prototype.onPropertyChanged = function(name,value) - { - var that = this; - if(name == "type") - { - if(this.outputs[0].type != value) - { - this.disconnectOutput(0); - this.outputs[0].type = value; - } - this.widgets.length = 1; //remove extra widgets - this.updateWidgets(); - } - if(name == "value") - { - if(!value.length) - this.widgets[1].value = value; - else - { - this.widgets[1].value = value[1]; - if(value.length > 2) - this.widgets[2].value = value[2]; - if(value.length > 3) - this.widgets[3].value = value[3]; - } - } - } - - LGraphShaderConstant.prototype.updateWidgets = function( old_value ) - { - var that = this; - var old_value = this.properties.value; - var options = { step: 0.01 }; - switch(this.properties.type) - { - case 'float': - this.properties.value = 0; - this.addWidget("number","v",0,{ step:0.01, property: "value" }); - break; - case 'vec2': - this.properties.value = old_value && old_value.length == 2 ? [old_value[0],old_value[1]] : [0,0,0]; - this.addWidget("number","x",this.properties.value[0], function(v){ that.properties.value[0] = v; },options); - this.addWidget("number","y",this.properties.value[1], function(v){ that.properties.value[1] = v; },options); - break; - case 'vec3': - this.properties.value = old_value && old_value.length == 3 ? [old_value[0],old_value[1],old_value[2]] : [0,0,0]; - this.addWidget("number","x",this.properties.value[0], function(v){ that.properties.value[0] = v; },options); - this.addWidget("number","y",this.properties.value[1], function(v){ that.properties.value[1] = v; },options); - this.addWidget("number","z",this.properties.value[2], function(v){ that.properties.value[2] = v; },options); - break; - case 'vec4': - this.properties.value = old_value && old_value.length == 4 ? [old_value[0],old_value[1],old_value[2],old_value[3]] : [0,0,0,0]; - this.addWidget("number","x",this.properties.value[0], function(v){ that.properties.value[0] = v; },options); - this.addWidget("number","y",this.properties.value[1], function(v){ that.properties.value[1] = v; },options); - this.addWidget("number","z",this.properties.value[2], function(v){ that.properties.value[2] = v; },options); - this.addWidget("number","w",this.properties.value[3], function(v){ that.properties.value[3] = v; },options); - break; - default: - console.error("unknown type for constant"); - } - } - - LGraphShaderConstant.prototype.onGetCode = function( context ) - { - if(!this.shader_destination) - return; - - var value = valueToGLSL( this.properties.value, this.properties.type ); - var link_name = getOutputLinkID(this,0); - if(!link_name) //not connected - return; - - var code = " " + this.properties.type + " " + link_name + " = " + value + ";"; - context.addCode( "code", code, this.shader_destination ); - - this.setOutputData( 0, this.properties.type ); - } - - registerShaderNode( "const/const", LGraphShaderConstant ); - - function LGraphShaderVec2() - { - this.addInput("xy","vec2"); - this.addInput("x","float"); - this.addInput("y","float"); - this.addOutput("xy","vec2"); - this.addOutput("x","float"); - this.addOutput("y","float"); - - this.properties = { x: 0, y: 0 }; - } - - LGraphShaderVec2.title = "vec2"; - LGraphShaderVec2.varmodes = ["xy","x","y"]; - - LGraphShaderVec2.prototype.onPropertyChanged = function() - { - if(this.graph) - this.graph._version++; - } - - LGraphShaderVec2.prototype.onGetCode = function( context ) - { - if(!this.shader_destination) - return; - - var props = this.properties; - - var varname = getShaderNodeVarName(this); - var code = " vec2 " + varname + " = " + valueToGLSL([props.x,props.y]) + ";\n"; - - for(var i = 0;i < LGraphShaderVec2.varmodes.length; ++i) - { - var varmode = LGraphShaderVec2.varmodes[i]; - var inlink = getInputLinkID(this,i); - if(!inlink) - continue; - code += " " + varname + "."+varmode+" = " + inlink + ";\n"; - } - - for(var i = 0;i < LGraphShaderVec2.varmodes.length; ++i) - { - var varmode = LGraphShaderVec2.varmodes[i]; - var outlink = getOutputLinkID(this,i); - if(!outlink) - continue; - var type = GLSL_types_const[varmode.length - 1]; - code += " "+type+" " + outlink + " = " + varname + "." + varmode + ";\n"; - this.setOutputData( i, type ); - } - - context.addCode( "code", code, this.shader_destination ); - } - - registerShaderNode( "const/vec2", LGraphShaderVec2 ); - - function LGraphShaderVec3() - { - this.addInput("xyz","vec3"); - this.addInput("x","float"); - this.addInput("y","float"); - this.addInput("z","float"); - this.addInput("xy","vec2"); - this.addInput("xz","vec2"); - this.addInput("yz","vec2"); - this.addOutput("xyz","vec3"); - this.addOutput("x","float"); - this.addOutput("y","float"); - this.addOutput("z","float"); - this.addOutput("xy","vec2"); - this.addOutput("xz","vec2"); - this.addOutput("yz","vec2"); - - this.properties = { x:0, y: 0, z: 0 }; - } - - LGraphShaderVec3.title = "vec3"; - LGraphShaderVec3.varmodes = ["xyz","x","y","z","xy","xz","yz"]; - - LGraphShaderVec3.prototype.onPropertyChanged = function() - { - if(this.graph) - this.graph._version++; - } - - LGraphShaderVec3.prototype.onGetCode = function( context ) - { - if(!this.shader_destination) - return; - - var props = this.properties; - - var varname = getShaderNodeVarName(this); - var code = "vec3 " + varname + " = " + valueToGLSL([props.x,props.y,props.z]) + ";\n"; - - for(var i = 0;i < LGraphShaderVec3.varmodes.length; ++i) - { - var varmode = LGraphShaderVec3.varmodes[i]; - var inlink = getInputLinkID(this,i); - if(!inlink) - continue; - code += " " + varname + "."+varmode+" = " + inlink + ";\n"; - } - - for(var i = 0; i < LGraphShaderVec3.varmodes.length; ++i) - { - var varmode = LGraphShaderVec3.varmodes[i]; - var outlink = getOutputLinkID(this,i); - if(!outlink) - continue; - var type = GLSL_types_const[varmode.length - 1]; - code += " "+type+" " + outlink + " = " + varname + "." + varmode + ";\n"; - this.setOutputData( i, type ); - } - - context.addCode( "code", code, this.shader_destination ); - } - - registerShaderNode( "const/vec3", LGraphShaderVec3 ); - - - function LGraphShaderVec4() - { - this.addInput("xyzw","vec4"); - this.addInput("xyz","vec3"); - this.addInput("x","float"); - this.addInput("y","float"); - this.addInput("z","float"); - this.addInput("w","float"); - this.addInput("xy","vec2"); - this.addInput("yz","vec2"); - this.addInput("zw","vec2"); - this.addOutput("xyzw","vec4"); - this.addOutput("xyz","vec3"); - this.addOutput("x","float"); - this.addOutput("y","float"); - this.addOutput("z","float"); - this.addOutput("xy","vec2"); - this.addOutput("yz","vec2"); - this.addOutput("zw","vec2"); - - this.properties = { x:0, y: 0, z: 0, w: 0 }; - } - - LGraphShaderVec4.title = "vec4"; - LGraphShaderVec4.varmodes = ["xyzw","xyz","x","y","z","w","xy","yz","zw"]; - - LGraphShaderVec4.prototype.onPropertyChanged = function() - { - if(this.graph) - this.graph._version++; - } - - LGraphShaderVec4.prototype.onGetCode = function( context ) - { - if(!this.shader_destination) - return; - - var props = this.properties; - - var varname = getShaderNodeVarName(this); - var code = "vec4 " + varname + " = " + valueToGLSL([props.x,props.y,props.z,props.w]) + ";\n"; - - for(var i = 0;i < LGraphShaderVec4.varmodes.length; ++i) - { - var varmode = LGraphShaderVec4.varmodes[i]; - var inlink = getInputLinkID(this,i); - if(!inlink) - continue; - code += " " + varname + "."+varmode+" = " + inlink + ";\n"; - } - - for(var i = 0;i < LGraphShaderVec4.varmodes.length; ++i) - { - var varmode = LGraphShaderVec4.varmodes[i]; - var outlink = getOutputLinkID(this,i); - if(!outlink) - continue; - var type = GLSL_types_const[varmode.length - 1]; - code += " "+type+" " + outlink + " = " + varname + "." + varmode + ";\n"; - this.setOutputData( i, type ); - } - - context.addCode( "code", code, this.shader_destination ); - - } - - registerShaderNode( "const/vec4", LGraphShaderVec4 ); - - //********************************* - - function LGraphShaderFragColor() { - this.addInput("color", LGShaders.ALL_TYPES ); - this.block_delete = true; - } - - LGraphShaderFragColor.title = "FragColor"; - LGraphShaderFragColor.desc = "Pixel final color"; - - LGraphShaderFragColor.prototype.onGetCode = function( context ) - { - var link_name = getInputLinkID( this, 0 ); - if(!link_name) - return; - var type = this.getInputData(0); - var code = varToTypeGLSL( link_name, type, "vec4" ); - context.addCode("fs_code", "fragcolor = " + code + ";"); - } - - registerShaderNode( "output/fragcolor", LGraphShaderFragColor ); - - - /* - function LGraphShaderDiscard() - { - this.addInput("v","T"); - this.addInput("min","T"); - this.properties = { min_value: 0.0 }; - this.addWidget("number","min",0,{ step: 0.01, property: "min_value" }); - } - - LGraphShaderDiscard.title = "Discard"; - - LGraphShaderDiscard.prototype.onGetCode = function( context ) - { - if(!this.isOutputConnected(0)) - return; - - var inlink = getInputLinkID(this,0); - var inlink1 = getInputLinkID(this,1); - - if(!inlink && !inlink1) //not connected - return; - context.addCode("code", return_type + " " + outlink + " = ( (" + inlink + " - "+minv+") / ("+ maxv+" - "+minv+") ) * ("+ maxv2+" - "+minv2+") + " + minv2 + ";", this.shader_destination ); - this.setOutputData( 0, return_type ); - } - - registerShaderNode( "output/discard", LGraphShaderDiscard ); - */ - - - // ************************************************* - - function LGraphShaderOperation() - { - this.addInput("A", LGShaders.ALL_TYPES ); - this.addInput("B", LGShaders.ALL_TYPES ); - this.addOutput("out",""); - this.properties = { - operation: "*" - }; - this.addWidget("combo","op.",this.properties.operation,{ property: "operation", values: LGraphShaderOperation.operations }); - } - - LGraphShaderOperation.title = "Operation"; - LGraphShaderOperation.operations = ["+","-","*","/"]; - - LGraphShaderOperation.prototype.getTitle = function() - { - if(this.flags.collapsed) - return "A" + this.properties.operation + "B"; - else - return "Operation"; - } - - LGraphShaderOperation.prototype.onGetCode = function( context ) - { - if(!this.shader_destination) - return; - - if(!this.isOutputConnected(0)) - return; - - var inlinks = []; - for(var i = 0; i < 3; ++i) - inlinks.push( { name: getInputLinkID(this,i), type: this.getInputData(i) || "float" } ); - - var outlink = getOutputLinkID(this,0); - if(!outlink) //not connected - return; - - //func_desc - var base_type = inlinks[0].type; - var return_type = base_type; - var op = this.properties.operation; - - var params = []; - for(var i = 0; i < 2; ++i) - { - var param_code = inlinks[i].name; - if(param_code == null) //not plugged - { - param_code = p.value != null ? p.value : "(1.0)"; - inlinks[i].type = "float"; - } - - //convert - if( inlinks[i].type != base_type ) - { - if( inlinks[i].type == "float" && (op == "*" || op == "/") ) - { - //I find hard to create the opposite condition now, so I prefeer an else - } - else - param_code = convertVarToGLSLType( param_code, inlinks[i].type, base_type ); - } - params.push( param_code ); - } - - context.addCode("code", return_type + " " + outlink + " = "+ params[0] + op + params[1] + ";", this.shader_destination ); - this.setOutputData( 0, return_type ); - } - - registerShaderNode( "math/operation", LGraphShaderOperation ); - - - function LGraphShaderFunc() - { - this.addInput("A", LGShaders.ALL_TYPES ); - this.addInput("B", LGShaders.ALL_TYPES ); - this.addOutput("out",""); - this.properties = { - func: "floor" - }; - this._current = "floor"; - this.addWidget("combo","func",this.properties.func,{ property: "func", values: GLSL_functions_name }); - } - - LGraphShaderFunc.title = "Func"; - - LGraphShaderFunc.prototype.onPropertyChanged = function(name,value) - { - if(this.graph) - this.graph._version++; - - if(name == "func") - { - var func_desc = GLSL_functions[ value ]; - if(!func_desc) - return; - - //remove extra inputs - for(var i = func_desc.params.length; i < this.inputs.length; ++i) - this.removeInput(i); - - //add and update inputs - for(var i = 0; i < func_desc.params.length; ++i) - { - var p = func_desc.params[i]; - if( this.inputs[i] ) - this.inputs[i].name = p.name + (p.value ? " (" + p.value + ")" : ""); - else - this.addInput( p.name, LGShaders.ALL_TYPES ); - } - } - } - - LGraphShaderFunc.prototype.getTitle = function() - { - if(this.flags.collapsed) - return this.properties.func; - else - return "Func"; - } - - LGraphShaderFunc.prototype.onGetCode = function( context ) - { - if(!this.shader_destination) - return; - - if(!this.isOutputConnected(0)) - return; - - var inlinks = []; - for(var i = 0; i < 3; ++i) - inlinks.push( { name: getInputLinkID(this,i), type: this.getInputData(i) || "float" } ); - - var outlink = getOutputLinkID(this,0); - if(!outlink) //not connected - return; - - var func_desc = GLSL_functions[ this.properties.func ]; - if(!func_desc) - return; - - //func_desc - var base_type = inlinks[0].type; - var return_type = func_desc.return_type; - if( return_type == "T" ) - return_type = base_type; - - var params = []; - for(var i = 0; i < func_desc.params.length; ++i) - { - var p = func_desc.params[i]; - var param_code = inlinks[i].name; - if(param_code == null) //not plugged - { - param_code = p.value != null ? p.value : "(1.0)"; - inlinks[i].type = "float"; - } - if( (p.type == "T" && inlinks[i].type != base_type) || - (p.type != "T" && inlinks[i].type != base_type) ) - param_code = convertVarToGLSLType( param_code, inlinks[i].type, base_type ); - params.push( param_code ); - } - - context.addFunction("round","float round(float v){ return floor(v+0.5); }\nvec2 round(vec2 v){ return floor(v+vec2(0.5));}\nvec3 round(vec3 v){ return floor(v+vec3(0.5));}\nvec4 round(vec4 v){ return floor(v+vec4(0.5)); }\n"); - context.addCode("code", return_type + " " + outlink + " = "+func_desc.func+"("+params.join(",")+");", this.shader_destination ); - - this.setOutputData( 0, return_type ); - } - - registerShaderNode( "math/func", LGraphShaderFunc ); - - - - function LGraphShaderSnippet() - { - this.addInput("A", LGShaders.ALL_TYPES ); - this.addInput("B", LGShaders.ALL_TYPES ); - this.addOutput("C","vec4"); - this.properties = { - code:"C = A+B", - type: "vec4" - } - this.addWidget("text","code",this.properties.code,{ property: "code" }); - this.addWidget("combo","type",this.properties.type,{ values:["float","vec2","vec3","vec4"], property: "type" }); - } - - LGraphShaderSnippet.title = "Snippet"; - - LGraphShaderSnippet.prototype.onPropertyChanged = function(name,value) - { - if(this.graph) - this.graph._version++; - - if(name == "type"&& this.outputs[0].type != value) - { - this.disconnectOutput(0); - this.outputs[0].type = value; - } - } - - LGraphShaderSnippet.prototype.getTitle = function() - { - if(this.flags.collapsed) - return this.properties.code; - else - return "Snippet"; - } - - LGraphShaderSnippet.prototype.onGetCode = function( context ) - { - if(!this.shader_destination || !this.isOutputConnected(0)) - return; - - var inlinkA = getInputLinkID(this,0); - if(!inlinkA) - inlinkA = "1.0"; - var inlinkB = getInputLinkID(this,1); - if(!inlinkB) - inlinkB = "1.0"; - var outlink = getOutputLinkID(this,0); - if(!outlink) //not connected - return; - - var inA_type = this.getInputData(0) || "float"; - var inB_type = this.getInputData(1) || "float"; - var return_type = this.properties.type; - - //cannot resolve input - if(inA_type == "T" || inB_type == "T") - { - return null; - } - - var funcname = "funcSnippet" + this.id; - - var func_code = "\n" + return_type + " " + funcname + "( " + inA_type + " A, " + inB_type + " B) {\n"; - func_code += " " + return_type + " C = " + return_type + "(0.0);\n"; - func_code += " " + this.properties.code + ";\n"; - func_code += " return C;\n}\n"; - - context.addCode("functions", func_code, this.shader_destination ); - context.addCode("code", return_type + " " + outlink + " = "+funcname+"("+inlinkA+","+inlinkB+");", this.shader_destination ); - - this.setOutputData( 0, return_type ); - } - - registerShaderNode( "utils/snippet", LGraphShaderSnippet ); - - //************************************ - - function LGraphShaderRand() - { - this.addOutput("out","float"); - } - - LGraphShaderRand.title = "Rand"; - - LGraphShaderRand.prototype.onGetCode = function( context ) - { - if(!this.shader_destination || !this.isOutputConnected(0)) - return; - - var outlink = getOutputLinkID(this,0); - - context.addUniform( "u_rand" + this.id, "float", function(){ return Math.random(); }); - context.addCode("code", "float " + outlink + " = u_rand" + this.id +";", this.shader_destination ); - this.setOutputData( 0, "float" ); - } - - registerShaderNode( "input/rand", LGraphShaderRand ); - - //noise - //https://gist.github.com/patriciogonzalezvivo/670c22f3966e662d2f83 - function LGraphShaderNoise() - { - this.addInput("out", LGShaders.ALL_TYPES ); - this.addInput("scale", "float" ); - this.addOutput("out","float"); - this.properties = { - type: "noise", - scale: 1 - }; - this.addWidget("combo","type", this.properties.type, { property: "type", values: LGraphShaderNoise.NOISE_TYPES }); - this.addWidget("number","scale", this.properties.scale, { property: "scale" }); - } - - LGraphShaderNoise.NOISE_TYPES = ["noise","rand"]; - - LGraphShaderNoise.title = "noise"; - - LGraphShaderNoise.prototype.onGetCode = function( context ) - { - if(!this.shader_destination || !this.isOutputConnected(0)) - return; - - var inlink = getInputLinkID(this,0); - var outlink = getOutputLinkID(this,0); - - var intype = this.getInputData(0); - if(!inlink) - { - intype = "vec2"; - inlink = context.buffer_names.uvs; - } - - context.addFunction("noise",LGraphShaderNoise.shader_functions); - context.addUniform( "u_noise_scale" + this.id, "float", this.properties.scale ); - if( intype == "float" ) - context.addCode("code", "float " + outlink + " = snoise( vec2(" + inlink +") * u_noise_scale" + this.id +");", this.shader_destination ); - else if( intype == "vec2" || intype == "vec3" ) - context.addCode("code", "float " + outlink + " = snoise(" + inlink +" * u_noise_scale" + this.id +");", this.shader_destination ); - else if( intype == "vec4" ) - context.addCode("code", "float " + outlink + " = snoise(" + inlink +".xyz * u_noise_scale" + this.id +");", this.shader_destination ); - this.setOutputData( 0, "float" ); - } - - registerShaderNode( "math/noise", LGraphShaderNoise ); - -LGraphShaderNoise.shader_functions = "\n\ -vec3 permute(vec3 x) { return mod(((x*34.0)+1.0)*x, 289.0); }\n\ -\n\ -float snoise(vec2 v){\n\ - const vec4 C = vec4(0.211324865405187, 0.366025403784439,-0.577350269189626, 0.024390243902439);\n\ - vec2 i = floor(v + dot(v, C.yy) );\n\ - vec2 x0 = v - i + dot(i, C.xx);\n\ - vec2 i1;\n\ - i1 = (x0.x > x0.y) ? vec2(1.0, 0.0) : vec2(0.0, 1.0);\n\ - vec4 x12 = x0.xyxy + C.xxzz;\n\ - x12.xy -= i1;\n\ - i = mod(i, 289.0);\n\ - vec3 p = permute( permute( i.y + vec3(0.0, i1.y, 1.0 ))\n\ - + i.x + vec3(0.0, i1.x, 1.0 ));\n\ - vec3 m = max(0.5 - vec3(dot(x0,x0), dot(x12.xy,x12.xy),dot(x12.zw,x12.zw)), 0.0);\n\ - m = m*m ;\n\ - m = m*m ;\n\ - vec3 x = 2.0 * fract(p * C.www) - 1.0;\n\ - vec3 h = abs(x) - 0.5;\n\ - vec3 ox = floor(x + 0.5);\n\ - vec3 a0 = x - ox;\n\ - m *= 1.79284291400159 - 0.85373472095314 * ( a0*a0 + h*h );\n\ - vec3 g;\n\ - g.x = a0.x * x0.x + h.x * x0.y;\n\ - g.yz = a0.yz * x12.xz + h.yz * x12.yw;\n\ - return 130.0 * dot(m, g);\n\ -}\n\ -vec4 permute(vec4 x){return mod(((x*34.0)+1.0)*x, 289.0);}\n\ -vec4 taylorInvSqrt(vec4 r){return 1.79284291400159 - 0.85373472095314 * r;}\n\ -\n\ -float snoise(vec3 v){ \n\ - const vec2 C = vec2(1.0/6.0, 1.0/3.0) ;\n\ - const vec4 D = vec4(0.0, 0.5, 1.0, 2.0);\n\ -\n\ -// First corner\n\ - vec3 i = floor(v + dot(v, C.yyy) );\n\ - vec3 x0 = v - i + dot(i, C.xxx) ;\n\ -\n\ -// Other corners\n\ - vec3 g = step(x0.yzx, x0.xyz);\n\ - vec3 l = 1.0 - g;\n\ - vec3 i1 = min( g.xyz, l.zxy );\n\ - vec3 i2 = max( g.xyz, l.zxy );\n\ -\n\ - // x0 = x0 - 0. + 0.0 * C \n\ - vec3 x1 = x0 - i1 + 1.0 * C.xxx;\n\ - vec3 x2 = x0 - i2 + 2.0 * C.xxx;\n\ - vec3 x3 = x0 - 1. + 3.0 * C.xxx;\n\ -\n\ -// Permutations\n\ - i = mod(i, 289.0 ); \n\ - vec4 p = permute( permute( permute( \n\ - i.z + vec4(0.0, i1.z, i2.z, 1.0 ))\n\ - + i.y + vec4(0.0, i1.y, i2.y, 1.0 )) \n\ - + i.x + vec4(0.0, i1.x, i2.x, 1.0 ));\n\ -\n\ -// Gradients\n\ -// ( N*N points uniformly over a square, mapped onto an octahedron.)\n\ - float n_ = 1.0/7.0; // N=7\n\ - vec3 ns = n_ * D.wyz - D.xzx;\n\ -\n\ - vec4 j = p - 49.0 * floor(p * ns.z *ns.z); // mod(p,N*N)\n\ -\n\ - vec4 x_ = floor(j * ns.z);\n\ - vec4 y_ = floor(j - 7.0 * x_ ); // mod(j,N)\n\ -\n\ - vec4 x = x_ *ns.x + ns.yyyy;\n\ - vec4 y = y_ *ns.x + ns.yyyy;\n\ - vec4 h = 1.0 - abs(x) - abs(y);\n\ -\n\ - vec4 b0 = vec4( x.xy, y.xy );\n\ - vec4 b1 = vec4( x.zw, y.zw );\n\ -\n\ - vec4 s0 = floor(b0)*2.0 + 1.0;\n\ - vec4 s1 = floor(b1)*2.0 + 1.0;\n\ - vec4 sh = -step(h, vec4(0.0));\n\ -\n\ - vec4 a0 = b0.xzyw + s0.xzyw*sh.xxyy ;\n\ - vec4 a1 = b1.xzyw + s1.xzyw*sh.zzww ;\n\ -\n\ - vec3 p0 = vec3(a0.xy,h.x);\n\ - vec3 p1 = vec3(a0.zw,h.y);\n\ - vec3 p2 = vec3(a1.xy,h.z);\n\ - vec3 p3 = vec3(a1.zw,h.w);\n\ -\n\ -//Normalise gradients\n\ - vec4 norm = taylorInvSqrt(vec4(dot(p0,p0), dot(p1,p1), dot(p2, p2), dot(p3,p3)));\n\ - p0 *= norm.x;\n\ - p1 *= norm.y;\n\ - p2 *= norm.z;\n\ - p3 *= norm.w;\n\ -\n\ -// Mix final noise value\n\ - vec4 m = max(0.6 - vec4(dot(x0,x0), dot(x1,x1), dot(x2,x2), dot(x3,x3)), 0.0);\n\ - m = m * m;\n\ - return 42.0 * dot( m*m, vec4( dot(p0,x0), dot(p1,x1),dot(p2,x2), dot(p3,x3) ) );\n\ -}\n\ -\n\ -vec3 hash3( vec2 p ){\n\ - vec3 q = vec3( dot(p,vec2(127.1,311.7)), \n\ - dot(p,vec2(269.5,183.3)), \n\ - dot(p,vec2(419.2,371.9)) );\n\ - return fract(sin(q)*43758.5453);\n\ -}\n\ -vec4 hash4( vec3 p ){\n\ - vec4 q = vec4( dot(p,vec3(127.1,311.7,257.3)), \n\ - dot(p,vec3(269.5,183.3,335.1)), \n\ - dot(p,vec3(314.5,235.1,467.3)), \n\ - dot(p,vec3(419.2,371.9,114.9)) );\n\ - return fract(sin(q)*43758.5453);\n\ -}\n\ -\n\ -float iqnoise( in vec2 x, float u, float v ){\n\ - vec2 p = floor(x);\n\ - vec2 f = fract(x);\n\ - \n\ - float k = 1.0+63.0*pow(1.0-v,4.0);\n\ - \n\ - float va = 0.0;\n\ - float wt = 0.0;\n\ - for( int j=-2; j<=2; j++ )\n\ - for( int i=-2; i<=2; i++ )\n\ - {\n\ - vec2 g = vec2( float(i),float(j) );\n\ - vec3 o = hash3( p + g )*vec3(u,u,1.0);\n\ - vec2 r = g - f + o.xy;\n\ - float d = dot(r,r);\n\ - float ww = pow( 1.0-smoothstep(0.0,1.414,sqrt(d)), k );\n\ - va += o.z*ww;\n\ - wt += ww;\n\ - }\n\ - \n\ - return va/wt;\n\ -}\n\ -" - - function LGraphShaderTime() - { - this.addOutput("out","float"); - } - - LGraphShaderTime.title = "Time"; - - LGraphShaderTime.prototype.onGetCode = function( context ) - { - if(!this.shader_destination || !this.isOutputConnected(0)) - return; - - var outlink = getOutputLinkID(this,0); - - context.addUniform( "u_time" + this.id, "float", function(){ return getTime() * 0.001; }); - context.addCode("code", "float " + outlink + " = u_time" + this.id +";", this.shader_destination ); - this.setOutputData( 0, "float" ); - } - - registerShaderNode( "input/time", LGraphShaderTime ); - - - function LGraphShaderDither() - { - this.addInput("in","T"); - this.addOutput("out","float"); - } - - LGraphShaderDither.title = "Dither"; - - LGraphShaderDither.prototype.onGetCode = function( context ) - { - if(!this.shader_destination || !this.isOutputConnected(0)) - return; - - var inlink = getInputLinkID(this,0); - var return_type = "float"; - var outlink = getOutputLinkID(this,0); - var intype = this.getInputData(0); - inlink = varToTypeGLSL( inlink, intype, "float" ); - context.addFunction("dither8x8", LGraphShaderDither.dither_func); - context.addCode("code", return_type + " " + outlink + " = dither8x8("+ inlink +");", this.shader_destination ); - this.setOutputData( 0, return_type ); - } - - LGraphShaderDither.dither_values = [0.515625,0.140625,0.640625,0.046875,0.546875,0.171875,0.671875,0.765625,0.265625,0.890625,0.390625,0.796875,0.296875,0.921875,0.421875,0.203125,0.703125,0.078125,0.578125,0.234375,0.734375,0.109375,0.609375,0.953125,0.453125,0.828125,0.328125,0.984375,0.484375,0.859375,0.359375,0.0625,0.5625,0.1875,0.6875,0.03125,0.53125,0.15625,0.65625,0.8125,0.3125,0.9375,0.4375,0.78125,0.28125,0.90625,0.40625,0.25,0.75,0.125,0.625,0.21875,0.71875,0.09375,0.59375,1.0001,0.5,0.875,0.375,0.96875,0.46875,0.84375,0.34375]; - - LGraphShaderDither.dither_func = "\n\ - float dither8x8(float brightness) {\n\ - vec2 position = vec2(0.0);\n\ - #ifdef FRAGMENT\n\ - position = gl_FragCoord.xy;\n\ - #endif\n\ - int x = int(mod(position.x, 8.0));\n\ - int y = int(mod(position.y, 8.0));\n\ - int index = x + y * 8;\n\ - float limit = 0.0;\n\ - if (x < 8) {\n\ - if(index==0) limit = 0.015625;\n\ - "+(LGraphShaderDither.dither_values.map( function(v,i){ return "else if(index== "+(i+1)+") limit = " + v + ";"}).join("\n"))+"\n\ - }\n\ - return brightness < limit ? 0.0 : 1.0;\n\ - }\n", - - registerShaderNode( "math/dither", LGraphShaderDither ); - - function LGraphShaderRemap() - { - this.addInput("", LGShaders.ALL_TYPES ); - this.addOutput("",""); - this.properties = { - min_value: 0, - max_value: 1, - min_value2: 0, - max_value2: 1 - }; - this.addWidget("number","min",0,{ step: 0.1, property: "min_value" }); - this.addWidget("number","max",1,{ step: 0.1, property: "max_value" }); - this.addWidget("number","min2",0,{ step: 0.1, property: "min_value2"}); - this.addWidget("number","max2",1,{ step: 0.1, property: "max_value2"}); - } - - LGraphShaderRemap.title = "Remap"; - - LGraphShaderRemap.prototype.onPropertyChanged = function() - { - if(this.graph) - this.graph._version++; - } - - LGraphShaderRemap.prototype.onConnectionsChange = function() - { - var return_type = this.getInputDataType(0); - this.outputs[0].type = return_type || "T"; - } - - LGraphShaderRemap.prototype.onGetCode = function( context ) - { - if(!this.shader_destination || !this.isOutputConnected(0)) - return; - - var inlink = getInputLinkID(this,0); - var outlink = getOutputLinkID(this,0); - if(!inlink && !outlink) //not connected - return; - - var return_type = this.getInputDataType(0); - this.outputs[0].type = return_type; - if(return_type == "T") - { - console.warn("node type is T and cannot be resolved"); - return; - } - - if(!inlink) - { - context.addCode("code"," " + return_type + " " + outlink + " = " + return_type + "(0.0);\n"); - return; - } - - var minv = valueToGLSL( this.properties.min_value ); - var maxv = valueToGLSL( this.properties.max_value ); - var minv2 = valueToGLSL( this.properties.min_value2 ); - var maxv2 = valueToGLSL( this.properties.max_value2 ); - - context.addCode("code", return_type + " " + outlink + " = ( (" + inlink + " - "+minv+") / ("+ maxv+" - "+minv+") ) * ("+ maxv2+" - "+minv2+") + " + minv2 + ";", this.shader_destination ); - this.setOutputData( 0, return_type ); - } - - registerShaderNode( "math/remap", LGraphShaderRemap ); - -})(this); - - - -(function(global) { - var LiteGraph = global.LiteGraph; - - var view_matrix = new Float32Array(16); - var projection_matrix = new Float32Array(16); - var viewprojection_matrix = new Float32Array(16); - var model_matrix = new Float32Array(16); - var global_uniforms = { - u_view: view_matrix, - u_projection: projection_matrix, - u_viewprojection: viewprojection_matrix, - u_model: model_matrix - }; - - LiteGraph.LGraphRender = { - onRequestCameraMatrices: null //overwrite with your 3D engine specifics, it will receive (view_matrix, projection_matrix,viewprojection_matrix) and must be filled - }; - - function generateGeometryId() { - return (Math.random() * 100000)|0; - } - - function LGraphPoints3D() { - - this.addInput("obj", ""); - this.addInput("radius", "number"); - - this.addOutput("out", "geometry"); - this.addOutput("points", "[vec3]"); - this.properties = { - radius: 1, - num_points: 4096, - generate_normals: true, - regular: false, - mode: LGraphPoints3D.SPHERE, - force_update: false - }; - - this.points = new Float32Array( this.properties.num_points * 3 ); - this.normals = new Float32Array( this.properties.num_points * 3 ); - this.must_update = true; - this.version = 0; - - var that = this; - this.addWidget("button","update",null, function(){ that.must_update = true; }); - - this.geometry = { - vertices: null, - _id: generateGeometryId() - } - - this._old_obj = null; - this._last_radius = null; - } - - global.LGraphPoints3D = LGraphPoints3D; - - LGraphPoints3D.RECTANGLE = 1; - LGraphPoints3D.CIRCLE = 2; - - LGraphPoints3D.CUBE = 10; - LGraphPoints3D.SPHERE = 11; - LGraphPoints3D.HEMISPHERE = 12; - LGraphPoints3D.INSIDE_SPHERE = 13; - - LGraphPoints3D.OBJECT = 20; - LGraphPoints3D.OBJECT_UNIFORMLY = 21; - LGraphPoints3D.OBJECT_INSIDE = 22; - - LGraphPoints3D.MODE_VALUES = { "rectangle":LGraphPoints3D.RECTANGLE, "circle":LGraphPoints3D.CIRCLE, "cube":LGraphPoints3D.CUBE, "sphere":LGraphPoints3D.SPHERE, "hemisphere":LGraphPoints3D.HEMISPHERE, "inside_sphere":LGraphPoints3D.INSIDE_SPHERE, "object":LGraphPoints3D.OBJECT, "object_uniformly":LGraphPoints3D.OBJECT_UNIFORMLY, "object_inside":LGraphPoints3D.OBJECT_INSIDE }; - - LGraphPoints3D.widgets_info = { - mode: { widget: "combo", values: LGraphPoints3D.MODE_VALUES } - }; - - LGraphPoints3D.title = "list of points"; - LGraphPoints3D.desc = "returns an array of points"; - - LGraphPoints3D.prototype.onPropertyChanged = function(name,value) - { - this.must_update = true; - } - - LGraphPoints3D.prototype.onExecute = function() { - - var obj = this.getInputData(0); - if( obj != this._old_obj || (obj && obj._version != this._old_obj_version) ) - { - this._old_obj = obj; - this.must_update = true; - } - - var radius = this.getInputData(1); - if(radius == null) - radius = this.properties.radius; - if( this._last_radius != radius ) - { - this._last_radius = radius; - this.must_update = true; - } - - if(this.must_update || this.properties.force_update ) - { - this.must_update = false; - this.updatePoints(); - } - - this.geometry.vertices = this.points; - this.geometry.normals = this.normals; - this.geometry._version = this.version; - - this.setOutputData( 0, this.geometry ); - } - - LGraphPoints3D.prototype.updatePoints = function() { - var num_points = this.properties.num_points|0; - if(num_points < 1) - num_points = 1; - - if(!this.points || this.points.length != num_points * 3) - this.points = new Float32Array( num_points * 3 ); - - if(this.properties.generate_normals) - { - if (!this.normals || this.normals.length != this.points.length) - this.normals = new Float32Array( this.points.length ); - } - else - this.normals = null; - - var radius = this._last_radius || this.properties.radius; - var mode = this.properties.mode; - - var obj = this.getInputData(0); - this._old_obj_version = obj ? obj._version : null; - - this.points = LGraphPoints3D.generatePoints( radius, num_points, mode, this.points, this.normals, this.properties.regular, obj ); - - this.version++; - } - - //global - LGraphPoints3D.generatePoints = function( radius, num_points, mode, points, normals, regular, obj ) - { - var size = num_points * 3; - if(!points || points.length != size) - points = new Float32Array( size ); - var temp = new Float32Array(3); - var UP = new Float32Array([0,1,0]); - - if(regular) - { - if( mode == LGraphPoints3D.RECTANGLE) - { - var side = Math.floor(Math.sqrt(num_points)); - for(var i = 0; i < side; ++i) - for(var j = 0; j < side; ++j) - { - var pos = i*3 + j*3*side; - points[pos] = ((i/side) - 0.5) * radius * 2; - points[pos+1] = 0; - points[pos+2] = ((j/side) - 0.5) * radius * 2; - } - points = new Float32Array( points.subarray(0,side*side*3) ); - if(normals) - { - for(var i = 0; i < normals.length; i+=3) - normals.set(UP, i); - } - } - else if( mode == LGraphPoints3D.SPHERE) - { - var side = Math.floor(Math.sqrt(num_points)); - for(var i = 0; i < side; ++i) - for(var j = 0; j < side; ++j) - { - var pos = i*3 + j*3*side; - polarToCartesian( temp, (i/side) * 2 * Math.PI, ((j/side) - 0.5) * 2 * Math.PI, radius ); - points[pos] = temp[0]; - points[pos+1] = temp[1]; - points[pos+2] = temp[2]; - } - points = new Float32Array( points.subarray(0,side*side*3) ); - if(normals) - LGraphPoints3D.generateSphericalNormals( points, normals ); - } - else if( mode == LGraphPoints3D.CIRCLE) - { - for(var i = 0; i < size; i+=3) - { - var angle = 2 * Math.PI * (i/size); - points[i] = Math.cos( angle ) * radius; - points[i+1] = 0; - points[i+2] = Math.sin( angle ) * radius; - } - if(normals) - { - for(var i = 0; i < normals.length; i+=3) - normals.set(UP, i); - } - } - } - else //non regular - { - if( mode == LGraphPoints3D.RECTANGLE) - { - for(var i = 0; i < size; i+=3) - { - points[i] = (Math.random() - 0.5) * radius * 2; - points[i+1] = 0; - points[i+2] = (Math.random() - 0.5) * radius * 2; - } - if(normals) - { - for(var i = 0; i < normals.length; i+=3) - normals.set(UP, i); - } - } - else if( mode == LGraphPoints3D.CUBE) - { - for(var i = 0; i < size; i+=3) - { - points[i] = (Math.random() - 0.5) * radius * 2; - points[i+1] = (Math.random() - 0.5) * radius * 2; - points[i+2] = (Math.random() - 0.5) * radius * 2; - } - if(normals) - { - for(var i = 0; i < normals.length; i+=3) - normals.set(UP, i); - } - } - else if( mode == LGraphPoints3D.SPHERE) - { - LGraphPoints3D.generateSphere( points, size, radius ); - if(normals) - LGraphPoints3D.generateSphericalNormals( points, normals ); - } - else if( mode == LGraphPoints3D.HEMISPHERE) - { - LGraphPoints3D.generateHemisphere( points, size, radius ); - if(normals) - LGraphPoints3D.generateSphericalNormals( points, normals ); - } - else if( mode == LGraphPoints3D.CIRCLE) - { - LGraphPoints3D.generateInsideCircle( points, size, radius ); - if(normals) - LGraphPoints3D.generateSphericalNormals( points, normals ); - } - else if( mode == LGraphPoints3D.INSIDE_SPHERE) - { - LGraphPoints3D.generateInsideSphere( points, size, radius ); - if(normals) - LGraphPoints3D.generateSphericalNormals( points, normals ); - } - else if( mode == LGraphPoints3D.OBJECT) - { - LGraphPoints3D.generateFromObject( points, normals, size, obj, false ); - } - else if( mode == LGraphPoints3D.OBJECT_UNIFORMLY) - { - LGraphPoints3D.generateFromObject( points, normals, size, obj, true ); - } - else if( mode == LGraphPoints3D.OBJECT_INSIDE) - { - LGraphPoints3D.generateFromInsideObject( points, size, obj ); - //if(normals) - // LGraphPoints3D.generateSphericalNormals( points, normals ); - } - else - console.warn("wrong mode in LGraphPoints3D"); - } - - return points; - } - - LGraphPoints3D.generateSphericalNormals = function(points, normals) - { - var temp = new Float32Array(3); - for(var i = 0; i < normals.length; i+=3) - { - temp[0] = points[i]; - temp[1] = points[i+1]; - temp[2] = points[i+2]; - vec3.normalize(temp,temp); - normals.set(temp,i); - } - } - - LGraphPoints3D.generateSphere = function (points, size, radius) - { - for(var i = 0; i < size; i+=3) - { - var r1 = Math.random(); - var r2 = Math.random(); - var x = 2 * Math.cos( 2 * Math.PI * r1 ) * Math.sqrt( r2 * (1-r2) ); - var y = 1 - 2 * r2; - var z = 2 * Math.sin( 2 * Math.PI * r1 ) * Math.sqrt( r2 * (1-r2) ); - points[i] = x * radius; - points[i+1] = y * radius; - points[i+2] = z * radius; - } - } - - LGraphPoints3D.generateHemisphere = function (points, size, radius) - { - for(var i = 0; i < size; i+=3) - { - var r1 = Math.random(); - var r2 = Math.random(); - var x = Math.cos( 2 * Math.PI * r1 ) * Math.sqrt(1 - r2*r2 ); - var y = r2; - var z = Math.sin( 2 * Math.PI * r1 ) * Math.sqrt(1 - r2*r2 ); - points[i] = x * radius; - points[i+1] = y * radius; - points[i+2] = z * radius; - } - } - - LGraphPoints3D.generateInsideCircle = function (points, size, radius) - { - for(var i = 0; i < size; i+=3) - { - var r1 = Math.random(); - var r2 = Math.random(); - var x = Math.cos( 2 * Math.PI * r1 ) * Math.sqrt(1 - r2*r2 ); - var y = r2; - var z = Math.sin( 2 * Math.PI * r1 ) * Math.sqrt(1 - r2*r2 ); - points[i] = x * radius; - points[i+1] = 0; - points[i+2] = z * radius; - } - } - - LGraphPoints3D.generateInsideSphere = function (points, size, radius) - { - for(var i = 0; i < size; i+=3) - { - var u = Math.random(); - var v = Math.random(); - var theta = u * 2.0 * Math.PI; - var phi = Math.acos(2.0 * v - 1.0); - var r = Math.cbrt(Math.random()) * radius; - var sinTheta = Math.sin(theta); - var cosTheta = Math.cos(theta); - var sinPhi = Math.sin(phi); - var cosPhi = Math.cos(phi); - points[i] = r * sinPhi * cosTheta; - points[i+1] = r * sinPhi * sinTheta; - points[i+2] = r * cosPhi; - } - } - - function findRandomTriangle( areas, f ) - { - var l = areas.length; - var imin = 0; - var imid = 0; - var imax = l; - - if(l == 0) - return -1; - if(l == 1) - return 0; - //dichotomic search - while (imax >= imin) - { - imid = ((imax + imin)*0.5)|0; - var t = areas[ imid ]; - if( t == f ) - return imid; - if( imin == (imax - 1) ) - return imin; - if (t < f) - imin = imid; - else - imax = imid; - } - return imid; - } - - LGraphPoints3D.generateFromObject = function( points, normals, size, obj, evenly ) - { - if(!obj) - return; - - var vertices = null; - var mesh_normals = null; - var indices = null; - var areas = null; - if( obj.constructor === GL.Mesh ) - { - vertices = obj.vertexBuffers.vertices.data; - mesh_normals = obj.vertexBuffers.normals ? obj.vertexBuffers.normals.data : null; - indices = obj.indexBuffers.indices ? obj.indexBuffers.indices.data : null; - if(!indices) - indices = obj.indexBuffers.triangles ? obj.indexBuffers.triangles.data : null; - } - if(!vertices) - return null; - var num_triangles = indices ? indices.length / 3 : vertices.length / (3*3); - var total_area = 0; //sum of areas of all triangles - - if(evenly) - { - areas = new Float32Array(num_triangles); //accum - for(var i = 0; i < num_triangles; ++i) - { - if(indices) - { - a = indices[i*3]*3; - b = indices[i*3+1]*3; - c = indices[i*3+2]*3; - } - else - { - a = i*9; - b = i*9+3; - c = i*9+6; - } - var P1 = vertices.subarray(a,a+3); - var P2 = vertices.subarray(b,b+3); - var P3 = vertices.subarray(c,c+3); - var aL = vec3.distance( P1, P2 ); - var bL = vec3.distance( P2, P3 ); - var cL = vec3.distance( P3, P1 ); - var s = (aL + bL+ cL) / 2; - total_area += Math.sqrt(s * (s - aL) * (s - bL) * (s - cL)); - areas[i] = total_area; - } - for(var i = 0; i < num_triangles; ++i) //normalize - areas[i] /= total_area; - } - - for(var i = 0; i < size; i+=3) - { - var r = Math.random(); - var index = evenly ? findRandomTriangle( areas, r ) : Math.floor(r * num_triangles ); - //get random triangle - var a = 0; - var b = 0; - var c = 0; - if(indices) - { - a = indices[index*3]*3; - b = indices[index*3+1]*3; - c = indices[index*3+2]*3; - } - else - { - a = index*9; - b = index*9+3; - c = index*9+6; - } - var s = Math.random(); - var t = Math.random(); - var sqrt_s = Math.sqrt(s); - var af = 1 - sqrt_s; - var bf = sqrt_s * ( 1 - t); - var cf = t * sqrt_s; - points[i] = af * vertices[a] + bf*vertices[b] + cf*vertices[c]; - points[i+1] = af * vertices[a+1] + bf*vertices[b+1] + cf*vertices[c+1]; - points[i+2] = af * vertices[a+2] + bf*vertices[b+2] + cf*vertices[c+2]; - if(normals && mesh_normals) - { - normals[i] = af * mesh_normals[a] + bf*mesh_normals[b] + cf*mesh_normals[c]; - normals[i+1] = af * mesh_normals[a+1] + bf*mesh_normals[b+1] + cf*mesh_normals[c+1]; - normals[i+2] = af * mesh_normals[a+2] + bf*mesh_normals[b+2] + cf*mesh_normals[c+2]; - var N = normals.subarray(i,i+3); - vec3.normalize(N,N); - } - } - } - - LGraphPoints3D.generateFromInsideObject = function( points, size, mesh ) - { - if(!mesh || mesh.constructor !== GL.Mesh) - return; - - var aabb = mesh.getBoundingBox(); - if(!mesh.octree) - mesh.octree = new GL.Octree( mesh ); - var octree = mesh.octree; - var origin = vec3.create(); - var direction = vec3.fromValues(1,0,0); - var temp = vec3.create(); - var i = 0; - var tries = 0; - while(i < size && tries < points.length * 10) //limit to avoid problems - { - tries += 1 - var r = vec3.random(temp); //random point inside the aabb - r[0] = (r[0] * 2 - 1) * aabb[3] + aabb[0]; - r[1] = (r[1] * 2 - 1) * aabb[4] + aabb[1]; - r[2] = (r[2] * 2 - 1) * aabb[5] + aabb[2]; - origin.set(r); - var hit = octree.testRay( origin, direction, 0, 10000, true, GL.Octree.ALL ); - if(!hit || hit.length % 2 == 0) //not inside - continue; - points.set( r, i ); - i+=3; - } - } - - LiteGraph.registerNodeType( "geometry/points3D", LGraphPoints3D ); - - - - function LGraphPointsToInstances() { - this.addInput("points", "geometry"); - this.addOutput("instances", "[mat4]"); - this.properties = { - mode: 1, - autoupdate: true - }; - - this.must_update = true; - this.matrices = []; - this.first_time = true; - } - - LGraphPointsToInstances.NORMAL = 0; - LGraphPointsToInstances.VERTICAL = 1; - LGraphPointsToInstances.SPHERICAL = 2; - LGraphPointsToInstances.RANDOM = 3; - LGraphPointsToInstances.RANDOM_VERTICAL = 4; - - LGraphPointsToInstances.modes = {"normal":0,"vertical":1,"spherical":2,"random":3,"random_vertical":4}; - LGraphPointsToInstances.widgets_info = { - mode: { widget: "combo", values: LGraphPointsToInstances.modes } - }; - - LGraphPointsToInstances.title = "points to inst"; - - LGraphPointsToInstances.prototype.onExecute = function() - { - var geo = this.getInputData(0); - if( !geo ) - { - this.setOutputData(0,null); - return; - } - - if( !this.isOutputConnected(0) ) - return; - - var has_changed = (geo._version != this._version || geo._id != this._geometry_id); - - if( has_changed && this.properties.autoupdate || this.first_time ) - { - this.first_time = false; - this.updateInstances( geo ); - } - - this.setOutputData( 0, this.matrices ); - } - - LGraphPointsToInstances.prototype.updateInstances = function( geometry ) - { - var vertices = geometry.vertices; - if(!vertices) - return null; - var normals = geometry.normals; - - var matrices = this.matrices; - var num_points = vertices.length / 3; - if( matrices.length != num_points) - matrices.length = num_points; - var identity = mat4.create(); - var temp = vec3.create(); - var zero = vec3.create(); - var UP = vec3.fromValues(0,1,0); - var FRONT = vec3.fromValues(0,0,-1); - var RIGHT = vec3.fromValues(1,0,0); - var R = quat.create(); - - var front = vec3.create(); - var right = vec3.create(); - var top = vec3.create(); - - for(var i = 0; i < vertices.length; i += 3) - { - var index = i/3; - var m = matrices[index]; - if(!m) - m = matrices[index] = mat4.create(); - m.set( identity ); - var point = vertices.subarray(i,i+3); - - switch(this.properties.mode) - { - case LGraphPointsToInstances.NORMAL: - mat4.setTranslation( m, point ); - if(normals) - { - var normal = normals.subarray(i,i+3); - top.set( normal ); - vec3.normalize( top, top ); - vec3.cross( right, FRONT, top ); - vec3.normalize( right, right ); - vec3.cross( front, right, top ); - vec3.normalize( front, front ); - m.set(right,0); - m.set(top,4); - m.set(front,8); - mat4.setTranslation( m, point ); - } - break; - case LGraphPointsToInstances.VERTICAL: - mat4.setTranslation( m, point ); - break; - case LGraphPointsToInstances.SPHERICAL: - front.set( point ); - vec3.normalize( front, front ); - vec3.cross( right, UP, front ); - vec3.normalize( right, right ); - vec3.cross( top, front, right ); - vec3.normalize( top, top ); - m.set(right,0); - m.set(top,4); - m.set(front,8); - mat4.setTranslation( m, point ); - break; - case LGraphPointsToInstances.RANDOM: - temp[0] = Math.random()*2 - 1; - temp[1] = Math.random()*2 - 1; - temp[2] = Math.random()*2 - 1; - vec3.normalize( temp, temp ); - quat.setAxisAngle( R, temp, Math.random() * 2 * Math.PI ); - mat4.fromQuat(m, R); - mat4.setTranslation( m, point ); - break; - case LGraphPointsToInstances.RANDOM_VERTICAL: - quat.setAxisAngle( R, UP, Math.random() * 2 * Math.PI ); - mat4.fromQuat(m, R); - mat4.setTranslation( m, point ); - break; - } - } - - this._version = geometry._version; - this._geometry_id = geometry._id; - } - - LiteGraph.registerNodeType( "geometry/points_to_instances", LGraphPointsToInstances ); - - - function LGraphGeometryTransform() { - this.addInput("in", "geometry,[mat4]"); - this.addInput("mat4", "mat4"); - this.addOutput("out", "geometry"); - this.properties = {}; - - this.geometry = { - type: "triangles", - vertices: null, - _id: generateGeometryId(), - _version: 0 - }; - - this._last_geometry_id = -1; - this._last_version = -1; - this._last_key = ""; - - this.must_update = true; - } - - LGraphGeometryTransform.title = "Transform"; - - LGraphGeometryTransform.prototype.onExecute = function() { - - var input = this.getInputData(0); - var model = this.getInputData(1); - - if(!input) - return; - - //array of matrices - if(input.constructor === Array) - { - if(input.length == 0) - return; - this.outputs[0].type = "[mat4]"; - if( !this.isOutputConnected(0) ) - return; - - if(!model) - { - this.setOutputData(0,input); - return; - } - - if(!this._output) - this._output = new Array(); - if(this._output.length != input.length) - this._output.length = input.length; - for(var i = 0; i < input.length; ++i) - { - var m = this._output[i]; - if(!m) - m = this._output[i] = mat4.create(); - mat4.multiply(m,input[i],model); - } - this.setOutputData(0,this._output); - return; - } - - //geometry - if(!input.vertices || !input.vertices.length) - return; - var geo = input; - this.outputs[0].type = "geometry"; - if( !this.isOutputConnected(0) ) - return; - if(!model) - { - this.setOutputData(0,geo); - return; - } - - var key = typedArrayToArray(model).join(","); - - if( this.must_update || geo._id != this._last_geometry_id || geo._version != this._last_version || key != this._last_key ) - { - this.updateGeometry(geo, model); - this._last_key = key; - this._last_version = geo._version; - this._last_geometry_id = geo._id; - this.must_update = false; - } - - this.setOutputData(0,this.geometry); - } - - LGraphGeometryTransform.prototype.updateGeometry = function(geometry, model) { - var old_vertices = geometry.vertices; - var vertices = this.geometry.vertices; - if( !vertices || vertices.length != old_vertices.length ) - vertices = this.geometry.vertices = new Float32Array( old_vertices.length ); - var temp = vec3.create(); - - for(var i = 0, l = vertices.length; i < l; i+=3) - { - temp[0] = old_vertices[i]; temp[1] = old_vertices[i+1]; temp[2] = old_vertices[i+2]; - mat4.multiplyVec3( temp, model, temp ); - vertices[i] = temp[0]; vertices[i+1] = temp[1]; vertices[i+2] = temp[2]; - } - - if(geometry.normals) - { - if( !this.geometry.normals || this.geometry.normals.length != geometry.normals.length ) - this.geometry.normals = new Float32Array( geometry.normals.length ); - var normals = this.geometry.normals; - var normal_model = mat4.invert(mat4.create(), model); - if(normal_model) - mat4.transpose(normal_model, normal_model); - var old_normals = geometry.normals; - for(var i = 0, l = normals.length; i < l; i+=3) - { - temp[0] = old_normals[i]; temp[1] = old_normals[i+1]; temp[2] = old_normals[i+2]; - mat4.multiplyVec3( temp, normal_model, temp ); - normals[i] = temp[0]; normals[i+1] = temp[1]; normals[i+2] = temp[2]; - } - } - - this.geometry.type = geometry.type; - this.geometry._version++; - } - - LiteGraph.registerNodeType( "geometry/transform", LGraphGeometryTransform ); - - - function LGraphGeometryPolygon() { - this.addInput("sides", "number"); - this.addInput("radius", "number"); - this.addOutput("out", "geometry"); - this.properties = { sides: 6, radius: 1, uvs: false } - - this.geometry = { - type: "line_loop", - vertices: null, - _id: generateGeometryId() - }; - this.geometry_id = -1; - this.version = -1; - this.must_update = true; - - this.last_info = { sides: -1, radius: -1 }; - } - - LGraphGeometryPolygon.title = "Polygon"; - - LGraphGeometryPolygon.prototype.onExecute = function() { - - if( !this.isOutputConnected(0) ) - return; - - var sides = this.getInputOrProperty("sides"); - var radius = this.getInputOrProperty("radius"); - sides = Math.max(3,sides)|0; - - //update - if( this.last_info.sides != sides || this.last_info.radius != radius ) - this.updateGeometry(sides, radius); - - this.setOutputData(0,this.geometry); - } - - LGraphGeometryPolygon.prototype.updateGeometry = function(sides, radius) { - var num = 3*sides; - var vertices = this.geometry.vertices; - if( !vertices || vertices.length != num ) - vertices = this.geometry.vertices = new Float32Array( 3*sides ); - var delta = (Math.PI * 2) / sides; - var gen_uvs = this.properties.uvs; - if(gen_uvs) - { - uvs = this.geometry.coords = new Float32Array( 3*sides ); - } - - - for(var i = 0; i < sides; ++i) - { - var angle = delta * -i; - var x = Math.cos( angle ) * radius; - var y = 0; - var z = Math.sin( angle ) * radius; - vertices[i*3] = x; - vertices[i*3+1] = y; - vertices[i*3+2] = z; - - if(gen_uvs) - { - - - } - } - this.geometry._id = ++this.geometry_id; - this.geometry._version = ++this.version; - this.last_info.sides = sides; - this.last_info.radius = radius; - } - - LiteGraph.registerNodeType( "geometry/polygon", LGraphGeometryPolygon ); - - - function LGraphGeometryExtrude() { - - this.addInput("", "geometry"); - this.addOutput("", "geometry"); - this.properties = { top_cap: true, bottom_cap: true, offset: [0,100,0] }; - this.version = -1; - - this._last_geo_version = -1; - this._must_update = true; - } - - LGraphGeometryExtrude.title = "extrude"; - - LGraphGeometryExtrude.prototype.onPropertyChanged = function(name, value) - { - this._must_update = true; - } - - LGraphGeometryExtrude.prototype.onExecute = function() - { - var geo = this.getInputData(0); - if( !geo || !this.isOutputConnected(0) ) - return; - - if(geo.version != this._last_geo_version || this._must_update) - { - this._geo = this.extrudeGeometry( geo, this._geo ); - if(this._geo) - this._geo.version = this.version++; - this._must_update = false; - } - - this.setOutputData(0, this._geo); - } - - LGraphGeometryExtrude.prototype.extrudeGeometry = function( geo ) - { - //for every pair of vertices - var vertices = geo.vertices; - var num_points = vertices.length / 3; - - var tempA = vec3.create(); - var tempB = vec3.create(); - var tempC = vec3.create(); - var tempD = vec3.create(); - var offset = new Float32Array( this.properties.offset ); - - if(geo.type == "line_loop") - { - var new_vertices = new Float32Array( num_points * 6 * 3 ); //every points become 6 ( caps not included ) - var npos = 0; - for(var i = 0, l = vertices.length; i < l; i += 3) - { - tempA[0] = vertices[i]; tempA[1] = vertices[i+1]; tempA[2] = vertices[i+2]; - - if( i+3 < l ) //loop - { - tempB[0] = vertices[i+3]; tempB[1] = vertices[i+4]; tempB[2] = vertices[i+5]; - } - else - { - tempB[0] = vertices[0]; tempB[1] = vertices[1]; tempB[2] = vertices[2]; - } - - vec3.add( tempC, tempA, offset ); - vec3.add( tempD, tempB, offset ); - - new_vertices.set( tempA, npos ); npos += 3; - new_vertices.set( tempB, npos ); npos += 3; - new_vertices.set( tempC, npos ); npos += 3; - - new_vertices.set( tempB, npos ); npos += 3; - new_vertices.set( tempD, npos ); npos += 3; - new_vertices.set( tempC, npos ); npos += 3; - } - } - - var out_geo = { - _id: generateGeometryId(), - type: "triangles", - vertices: new_vertices - }; - - return out_geo; - } - - LiteGraph.registerNodeType( "geometry/extrude", LGraphGeometryExtrude ); - - - function LGraphGeometryEval() { - this.addInput("in", "geometry"); - this.addOutput("out", "geometry"); - - this.properties = { - code: "V[1] += 0.01 * Math.sin(I + T*0.001);", - execute_every_frame: false - }; - - this.geometry = null; - this.geometry_id = -1; - this.version = -1; - this.must_update = true; - - this.vertices = null; - this.func = null; - } - - LGraphGeometryEval.title = "geoeval"; - LGraphGeometryEval.desc = "eval code"; - - LGraphGeometryEval.widgets_info = { - code: { widget: "code" } - }; - - LGraphGeometryEval.prototype.onConfigure = function(o) - { - this.compileCode(); - } - - LGraphGeometryEval.prototype.compileCode = function() - { - if(!this.properties.code) - return; - - try - { - this.func = new Function("V","I","T", this.properties.code); - this.boxcolor = "#AFA"; - this.must_update = true; - } - catch (err) - { - this.boxcolor = "red"; - } - } - - LGraphGeometryEval.prototype.onPropertyChanged = function(name, value) - { - if(name == "code") - { - this.properties.code = value; - this.compileCode(); - } - } - - LGraphGeometryEval.prototype.onExecute = function() { - var geometry = this.getInputData(0); - if(!geometry) - return; - - if(!this.func) - { - this.setOutputData(0,geometry); - return; - } - - if( this.geometry_id != geometry._id || this.version != geometry._version || this.must_update || this.properties.execute_every_frame ) - { - this.must_update = false; - this.geometry_id = geometry._id; - if(this.properties.execute_every_frame) - this.version++; - else - this.version = geometry._version; - var func = this.func; - var T = getTime(); - - //clone - if(!this.geometry) - this.geometry = {}; - for(var i in geometry) - { - if(geometry[i] == null) - continue; - if( geometry[i].constructor == Float32Array ) - this.geometry[i] = new Float32Array( geometry[i] ); - else - this.geometry[i] = geometry[i]; - } - this.geometry._id = geometry._id; - if(this.properties.execute_every_frame) - this.geometry._version = this.version; - else - this.geometry._version = geometry._version + 1; - - var V = vec3.create(); - var vertices = this.vertices; - if(!vertices || this.vertices.length != geometry.vertices.length) - vertices = this.vertices = new Float32Array( geometry.vertices ); - else - vertices.set( geometry.vertices ); - for(var i = 0; i < vertices.length; i+=3) - { - V[0] = vertices[i]; - V[1] = vertices[i+1]; - V[2] = vertices[i+2]; - func(V,i/3,T); - vertices[i] = V[0]; - vertices[i+1] = V[1]; - vertices[i+2] = V[2]; - } - this.geometry.vertices = vertices; - } - - this.setOutputData(0,this.geometry); - } - - LiteGraph.registerNodeType( "geometry/eval", LGraphGeometryEval ); - -/* -function LGraphGeometryDisplace() { - this.addInput("in", "geometry"); - this.addInput("img", "image"); - this.addOutput("out", "geometry"); - - this.properties = { - grid_size: 1 - }; - - this.geometry = null; - this.geometry_id = -1; - this.version = -1; - this.must_update = true; - - this.vertices = null; - } - - LGraphGeometryDisplace.title = "displace"; - LGraphGeometryDisplace.desc = "displace points"; - - LGraphGeometryDisplace.prototype.onExecute = function() { - var geometry = this.getInputData(0); - var image = this.getInputData(1); - if(!geometry) - return; - - if(!image) - { - this.setOutputData(0,geometry); - return; - } - - if( this.geometry_id != geometry._id || this.version != geometry._version || this.must_update ) - { - this.must_update = false; - this.geometry_id = geometry._id; - this.version = geometry._version; - - //copy - this.geometry = {}; - for(var i in geometry) - this.geometry[i] = geometry[i]; - this.geometry._id = geometry._id; - this.geometry._version = geometry._version + 1; - - var grid_size = this.properties.grid_size; - if(grid_size != 0) - { - var vertices = this.vertices; - if(!vertices || this.vertices.length != this.geometry.vertices.length) - vertices = this.vertices = new Float32Array( this.geometry.vertices ); - for(var i = 0; i < vertices.length; i+=3) - { - vertices[i] = Math.round(vertices[i]/grid_size) * grid_size; - vertices[i+1] = Math.round(vertices[i+1]/grid_size) * grid_size; - vertices[i+2] = Math.round(vertices[i+2]/grid_size) * grid_size; - } - this.geometry.vertices = vertices; - } - } - - this.setOutputData(0,this.geometry); - } - - LiteGraph.registerNodeType( "geometry/displace", LGraphGeometryDisplace ); -*/ - - function LGraphConnectPoints() { - this.addInput("in", "geometry"); - this.addOutput("out", "geometry"); - - this.properties = { - min_dist: 0.4, - max_dist: 0.5, - max_connections: 0, - probability: 1 - }; - - this.geometry_id = -1; - this.version = -1; - this.my_version = 1; - this.must_update = true; - } - - LGraphConnectPoints.title = "connect points"; - LGraphConnectPoints.desc = "adds indices between near points"; - - LGraphConnectPoints.prototype.onPropertyChanged = function(name,value) - { - this.must_update = true; - } - - LGraphConnectPoints.prototype.onExecute = function() { - var geometry = this.getInputData(0); - if(!geometry) - return; - - if( this.geometry_id != geometry._id || this.version != geometry._version || this.must_update ) - { - this.must_update = false; - this.geometry_id = geometry._id; - this.version = geometry._version; - - //copy - this.geometry = {}; - for(var i in geometry) - this.geometry[i] = geometry[i]; - this.geometry._id = generateGeometryId(); - this.geometry._version = this.my_version++; - - var vertices = geometry.vertices; - var l = vertices.length; - var min_dist = this.properties.min_dist; - var max_dist = this.properties.max_dist; - var probability = this.properties.probability; - var max_connections = this.properties.max_connections; - var indices = []; - - for(var i = 0; i < l; i+=3) - { - var x = vertices[i]; - var y = vertices[i+1]; - var z = vertices[i+2]; - var connections = 0; - for(var j = i+3; j < l; j+=3) - { - var x2 = vertices[j]; - var y2 = vertices[j+1]; - var z2 = vertices[j+2]; - var dist = Math.sqrt( (x-x2)*(x-x2) + (y-y2)*(y-y2) + (z-z2)*(z-z2)); - if(dist > max_dist || dist < min_dist || (probability < 1 && probability < Math.random()) ) - continue; - indices.push(i/3,j/3); - connections += 1; - if(max_connections && connections > max_connections) - break; - } - } - this.geometry.indices = this.indices = new Uint32Array(indices); - } - - if(this.indices && this.indices.length) - { - this.geometry.indices = this.indices; - this.setOutputData( 0, this.geometry ); - } - else - this.setOutputData( 0, null ); - } - - LiteGraph.registerNodeType( "geometry/connectPoints", LGraphConnectPoints ); - - //Works with Litegl.js to create WebGL nodes - if (typeof GL == "undefined") //LiteGL RELATED ********************************************** - return; - - function LGraphToGeometry() { - this.addInput("mesh", "mesh"); - this.addOutput("out", "geometry"); - - this.geometry = {}; - this.last_mesh = null; - } - - LGraphToGeometry.title = "to geometry"; - LGraphToGeometry.desc = "converts a mesh to geometry"; - - LGraphToGeometry.prototype.onExecute = function() { - var mesh = this.getInputData(0); - if(!mesh) - return; - - if(mesh != this.last_mesh) - { - this.last_mesh = mesh; - for(i in mesh.vertexBuffers) - { - var buffer = mesh.vertexBuffers[i]; - this.geometry[i] = buffer.data - } - if(mesh.indexBuffers["triangles"]) - this.geometry.indices = mesh.indexBuffers["triangles"].data; - - this.geometry._id = generateGeometryId(); - this.geometry._version = 0; - } - - this.setOutputData(0,this.geometry); - if(this.geometry) - this.setOutputData(1,this.geometry.vertices); - } - - LiteGraph.registerNodeType( "geometry/toGeometry", LGraphToGeometry ); - - function LGraphGeometryToMesh() { - this.addInput("in", "geometry"); - this.addOutput("mesh", "mesh"); - this.properties = {}; - this.version = -1; - this.mesh = null; - } - - LGraphGeometryToMesh.title = "Geo to Mesh"; - - LGraphGeometryToMesh.prototype.updateMesh = function(geometry) - { - if(!this.mesh) - this.mesh = new GL.Mesh(); - - for(var i in geometry) - { - if(i[0] == "_") - continue; - - var buffer_data = geometry[i]; - - var info = GL.Mesh.common_buffers[i]; - if(!info && i != "indices") //unknown buffer - continue; - var spacing = info ? info.spacing : 3; - var mesh_buffer = this.mesh.vertexBuffers[i]; - - if(!mesh_buffer || mesh_buffer.data.length != buffer_data.length) - { - mesh_buffer = new GL.Buffer( i == "indices" ? GL.ELEMENT_ARRAY_BUFFER : GL.ARRAY_BUFFER, buffer_data, spacing, GL.DYNAMIC_DRAW ); - } - else - { - mesh_buffer.data.set( buffer_data ); - mesh_buffer.upload(GL.DYNAMIC_DRAW); - } - - this.mesh.addBuffer( i, mesh_buffer ); - } - - if(this.mesh.vertexBuffers.normals &&this.mesh.vertexBuffers.normals.data.length != this.mesh.vertexBuffers.vertices.data.length ) - { - var n = new Float32Array([0,1,0]); - var normals = new Float32Array( this.mesh.vertexBuffers.vertices.data.length ); - for(var i = 0; i < normals.length; i+= 3) - normals.set( n, i ); - mesh_buffer = new GL.Buffer( GL.ARRAY_BUFFER, normals, 3 ); - this.mesh.addBuffer( "normals", mesh_buffer ); - } - - this.mesh.updateBoundingBox(); - this.geometry_id = this.mesh.id = geometry._id; - this.version = this.mesh.version = geometry._version; - return this.mesh; - } - - LGraphGeometryToMesh.prototype.onExecute = function() { - - var geometry = this.getInputData(0); - if(!geometry) - return; - if( this.version != geometry._version || this.geometry_id != geometry._id ) - this.updateMesh( geometry ); - this.setOutputData(0, this.mesh); - } - - LiteGraph.registerNodeType( "geometry/toMesh", LGraphGeometryToMesh ); - - function LGraphRenderMesh() { - this.addInput("mesh", "mesh"); - this.addInput("mat4", "mat4"); - this.addInput("tex", "texture"); - - this.properties = { - enabled: true, - primitive: GL.TRIANGLES, - additive: false, - color: [1,1,1], - opacity: 1 - }; - - this.color = vec4.create([1,1,1,1]); - this.model_matrix = mat4.create(); - this.uniforms = { - u_color: this.color, - u_model: this.model_matrix - }; - } - - LGraphRenderMesh.title = "Render Mesh"; - LGraphRenderMesh.desc = "renders a mesh flat"; - - LGraphRenderMesh.PRIMITIVE_VALUES = { "points":GL.POINTS, "lines":GL.LINES, "line_loop":GL.LINE_LOOP,"line_strip":GL.LINE_STRIP, "triangles":GL.TRIANGLES, "triangle_fan":GL.TRIANGLE_FAN, "triangle_strip":GL.TRIANGLE_STRIP }; - - LGraphRenderMesh.widgets_info = { - primitive: { widget: "combo", values: LGraphRenderMesh.PRIMITIVE_VALUES }, - color: { widget: "color" } - }; - - LGraphRenderMesh.prototype.onExecute = function() { - - if(!this.properties.enabled) - return; - - var mesh = this.getInputData(0); - if(!mesh) - return; - - if(!LiteGraph.LGraphRender.onRequestCameraMatrices) - { - console.warn("cannot render geometry, LiteGraph.onRequestCameraMatrices is null, remember to fill this with a callback(view_matrix, projection_matrix,viewprojection_matrix) to use 3D rendering from the graph"); - return; - } - - LiteGraph.LGraphRender.onRequestCameraMatrices( view_matrix, projection_matrix,viewprojection_matrix ); - var shader = null; - var texture = this.getInputData(2); - if(texture) - { - shader = gl.shaders["textured"]; - if(!shader) - shader = gl.shaders["textured"] = new GL.Shader( LGraphRenderPoints.vertex_shader_code, LGraphRenderPoints.fragment_shader_code, { USE_TEXTURE:"" }); - } - else - { - shader = gl.shaders["flat"]; - if(!shader) - shader = gl.shaders["flat"] = new GL.Shader( LGraphRenderPoints.vertex_shader_code, LGraphRenderPoints.fragment_shader_code ); - } - - this.color.set( this.properties.color ); - this.color[3] = this.properties.opacity; - - var model_matrix = this.model_matrix; - var m = this.getInputData(1); - if(m) - model_matrix.set(m); - else - mat4.identity( model_matrix ); - - this.uniforms.u_point_size = 1; - var primitive = this.properties.primitive; - - shader.uniforms( global_uniforms ); - shader.uniforms( this.uniforms ); - - if(this.properties.opacity >= 1) - gl.disable( gl.BLEND ); - else - gl.enable( gl.BLEND ); - gl.enable( gl.DEPTH_TEST ); - if( this.properties.additive ) - { - gl.blendFunc( gl.SRC_ALPHA, gl.ONE ); - gl.depthMask( false ); - } - else - gl.blendFunc( gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA ); - - var indices = "indices"; - if( mesh.indexBuffers.triangles ) - indices = "triangles"; - shader.draw( mesh, primitive, indices ); - gl.disable( gl.BLEND ); - gl.depthMask( true ); - } - - LiteGraph.registerNodeType( "geometry/render_mesh", LGraphRenderMesh ); - - //************************** - - - function LGraphGeometryPrimitive() { - this.addInput("size", "number"); - this.addOutput("out", "mesh"); - this.properties = { type: 1, size: 1, subdivisions: 32 }; - - this.version = (Math.random() * 100000)|0; - this.last_info = { type: -1, size: -1, subdivisions: -1 }; - } - - LGraphGeometryPrimitive.title = "Primitive"; - - LGraphGeometryPrimitive.VALID = { "CUBE":1, "PLANE":2, "CYLINDER":3, "SPHERE":4, "CIRCLE":5, "HEMISPHERE":6, "ICOSAHEDRON":7, "CONE":8, "QUAD":9 }; - LGraphGeometryPrimitive.widgets_info = { - type: { widget: "combo", values: LGraphGeometryPrimitive.VALID } - }; - - LGraphGeometryPrimitive.prototype.onExecute = function() { - - if( !this.isOutputConnected(0) ) - return; - - var size = this.getInputOrProperty("size"); - - //update - if( this.last_info.type != this.properties.type || this.last_info.size != size || this.last_info.subdivisions != this.properties.subdivisions ) - this.updateMesh( this.properties.type, size, this.properties.subdivisions ); - - this.setOutputData(0,this._mesh); - } - - LGraphGeometryPrimitive.prototype.updateMesh = function(type, size, subdivisions) - { - subdivisions = Math.max(0,subdivisions)|0; - - switch (type) - { - case 1: //CUBE: - this._mesh = GL.Mesh.cube({size: size, normals:true,coords:true}); - break; - case 2: //PLANE: - this._mesh = GL.Mesh.plane({size: size, xz: true, detail: subdivisions, normals:true,coords:true}); - break; - case 3: //CYLINDER: - this._mesh = GL.Mesh.cylinder({size: size, subdivisions: subdivisions, normals:true,coords:true}); - break; - case 4: //SPHERE: - this._mesh = GL.Mesh.sphere({size: size, "long": subdivisions, lat: subdivisions, normals:true,coords:true}); - break; - case 5: //CIRCLE: - this._mesh = GL.Mesh.circle({size: size, slices: subdivisions, normals:true, coords:true}); - break; - case 6: //HEMISPHERE: - this._mesh = GL.Mesh.sphere({size: size, "long": subdivisions, lat: subdivisions, normals:true, coords:true, hemi: true}); - break; - case 7: //ICOSAHEDRON: - this._mesh = GL.Mesh.icosahedron({size: size, subdivisions:subdivisions }); - break; - case 8: //CONE: - this._mesh = GL.Mesh.cone({radius: size, height: size, subdivisions:subdivisions }); - break; - case 9: //QUAD: - this._mesh = GL.Mesh.plane({size: size, xz: false, detail: subdivisions, normals:true, coords:true }); - break; - } - - this.last_info.type = type; - this.last_info.size = size; - this.last_info.subdivisions = subdivisions; - this._mesh.version = this.version++; - } - - LiteGraph.registerNodeType( "geometry/mesh_primitive", LGraphGeometryPrimitive ); - - - function LGraphRenderPoints() { - this.addInput("in", "geometry"); - this.addInput("mat4", "mat4"); - this.addInput("tex", "texture"); - this.properties = { - enabled: true, - point_size: 0.1, - fixed_size: false, - additive: true, - color: [1,1,1], - opacity: 1 - }; - - this.color = vec4.create([1,1,1,1]); - - this.uniforms = { - u_point_size: 1, - u_perspective: 1, - u_point_perspective: 1, - u_color: this.color - }; - - this.geometry_id = -1; - this.version = -1; - this.mesh = null; - } - - LGraphRenderPoints.title = "renderPoints"; - LGraphRenderPoints.desc = "render points with a texture"; - - LGraphRenderPoints.widgets_info = { - color: { widget: "color" } - }; - - LGraphRenderPoints.prototype.updateMesh = function(geometry) - { - var buffer = this.buffer; - if(!this.buffer || !this.buffer.data || this.buffer.data.length != geometry.vertices.length) - this.buffer = new GL.Buffer( GL.ARRAY_BUFFER, geometry.vertices,3,GL.DYNAMIC_DRAW); - else - { - this.buffer.data.set( geometry.vertices ); - this.buffer.upload(GL.DYNAMIC_DRAW); - } - - if(!this.mesh) - this.mesh = new GL.Mesh(); - - this.mesh.addBuffer("vertices",this.buffer); - this.geometry_id = this.mesh.id = geometry._id; - this.version = this.mesh.version = geometry._version; - } - - LGraphRenderPoints.prototype.onExecute = function() { - - if(!this.properties.enabled) - return; - - var geometry = this.getInputData(0); - if(!geometry) - return; - if(this.version != geometry._version || this.geometry_id != geometry._id ) - this.updateMesh( geometry ); - - if(!LiteGraph.LGraphRender.onRequestCameraMatrices) - { - console.warn("cannot render geometry, LiteGraph.onRequestCameraMatrices is null, remember to fill this with a callback(view_matrix, projection_matrix,viewprojection_matrix) to use 3D rendering from the graph"); - return; - } - - LiteGraph.LGraphRender.onRequestCameraMatrices( view_matrix, projection_matrix,viewprojection_matrix ); - var shader = null; - - var texture = this.getInputData(2); - - if(texture) - { - shader = gl.shaders["textured_points"]; - if(!shader) - shader = gl.shaders["textured_points"] = new GL.Shader( LGraphRenderPoints.vertex_shader_code, LGraphRenderPoints.fragment_shader_code, { USE_TEXTURED_POINTS:"" }); - } - else - { - shader = gl.shaders["points"]; - if(!shader) - shader = gl.shaders["points"] = new GL.Shader( LGraphRenderPoints.vertex_shader_code, LGraphRenderPoints.fragment_shader_code, { USE_POINTS: "" }); - } - - this.color.set( this.properties.color ); - this.color[3] = this.properties.opacity; - - var m = this.getInputData(1); - if(m) - model_matrix.set(m); - else - mat4.identity( model_matrix ); - - this.uniforms.u_point_size = this.properties.point_size; - this.uniforms.u_point_perspective = this.properties.fixed_size ? 0 : 1; - this.uniforms.u_perspective = gl.viewport_data[3] * projection_matrix[5]; - - shader.uniforms( global_uniforms ); - shader.uniforms( this.uniforms ); - - if(this.properties.opacity >= 1) - gl.disable( gl.BLEND ); - else - gl.enable( gl.BLEND ); - - gl.enable( gl.DEPTH_TEST ); - if( this.properties.additive ) - { - gl.blendFunc( gl.SRC_ALPHA, gl.ONE ); - gl.depthMask( false ); - } - else - gl.blendFunc( gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA ); - - shader.draw( this.mesh, GL.POINTS ); - - gl.disable( gl.BLEND ); - gl.depthMask( true ); - } - - LiteGraph.registerNodeType( "geometry/render_points", LGraphRenderPoints ); - - LGraphRenderPoints.vertex_shader_code = '\ - precision mediump float;\n\ - attribute vec3 a_vertex;\n\ - varying vec3 v_vertex;\n\ - attribute vec3 a_normal;\n\ - varying vec3 v_normal;\n\ - #ifdef USE_COLOR\n\ - attribute vec4 a_color;\n\ - varying vec4 v_color;\n\ - #endif\n\ - attribute vec2 a_coord;\n\ - varying vec2 v_coord;\n\ - #ifdef USE_SIZE\n\ - attribute float a_extra;\n\ - #endif\n\ - #ifdef USE_INSTANCING\n\ - attribute mat4 u_model;\n\ - #else\n\ - uniform mat4 u_model;\n\ - #endif\n\ - uniform mat4 u_viewprojection;\n\ - uniform float u_point_size;\n\ - uniform float u_perspective;\n\ - uniform float u_point_perspective;\n\ - float computePointSize(float radius, float w)\n\ - {\n\ - if(radius < 0.0)\n\ - return -radius;\n\ - return u_perspective * radius / w;\n\ - }\n\ - void main() {\n\ - v_coord = a_coord;\n\ - #ifdef USE_COLOR\n\ - v_color = a_color;\n\ - #endif\n\ - v_vertex = ( u_model * vec4( a_vertex, 1.0 )).xyz;\n\ - v_normal = ( u_model * vec4( a_normal, 0.0 )).xyz;\n\ - gl_Position = u_viewprojection * vec4(v_vertex,1.0);\n\ - gl_PointSize = u_point_size;\n\ - #ifdef USE_SIZE\n\ - gl_PointSize = a_extra;\n\ - #endif\n\ - if(u_point_perspective != 0.0)\n\ - gl_PointSize = computePointSize( gl_PointSize, gl_Position.w );\n\ - }\ - '; - - LGraphRenderPoints.fragment_shader_code = '\ - precision mediump float;\n\ - uniform vec4 u_color;\n\ - #ifdef USE_COLOR\n\ - varying vec4 v_color;\n\ - #endif\n\ - varying vec2 v_coord;\n\ - uniform sampler2D u_texture;\n\ - void main() {\n\ - vec4 color = u_color;\n\ - #ifdef USE_TEXTURED_POINTS\n\ - color *= texture2D(u_texture, gl_PointCoord.xy);\n\ - #else\n\ - #ifdef USE_TEXTURE\n\ - color *= texture2D(u_texture, v_coord);\n\ - if(color.a < 0.1)\n\ - discard;\n\ - #endif\n\ - #ifdef USE_POINTS\n\ - float dist = length( gl_PointCoord.xy - vec2(0.5) );\n\ - if( dist > 0.45 )\n\ - discard;\n\ - #endif\n\ - #endif\n\ - #ifdef USE_COLOR\n\ - color *= v_color;\n\ - #endif\n\ - gl_FragColor = color;\n\ - }\ - '; - - //based on https://inconvergent.net/2019/depth-of-field/ - /* - function LGraphRenderGeometryDOF() { - this.addInput("in", "geometry"); - this.addInput("mat4", "mat4"); - this.addInput("tex", "texture"); - this.properties = { - enabled: true, - lines: true, - point_size: 0.1, - fixed_size: false, - additive: true, - color: [1,1,1], - opacity: 1 - }; - - this.color = vec4.create([1,1,1,1]); - - this.uniforms = { - u_point_size: 1, - u_perspective: 1, - u_point_perspective: 1, - u_color: this.color - }; - - this.geometry_id = -1; - this.version = -1; - this.mesh = null; - } - - LGraphRenderGeometryDOF.widgets_info = { - color: { widget: "color" } - }; - - LGraphRenderGeometryDOF.prototype.updateMesh = function(geometry) - { - var buffer = this.buffer; - if(!this.buffer || this.buffer.data.length != geometry.vertices.length) - this.buffer = new GL.Buffer( GL.ARRAY_BUFFER, geometry.vertices,3,GL.DYNAMIC_DRAW); - else - { - this.buffer.data.set( geometry.vertices ); - this.buffer.upload(GL.DYNAMIC_DRAW); - } - - if(!this.mesh) - this.mesh = new GL.Mesh(); - - this.mesh.addBuffer("vertices",this.buffer); - this.geometry_id = this.mesh.id = geometry._id; - this.version = this.mesh.version = geometry._version; - } - - LGraphRenderGeometryDOF.prototype.onExecute = function() { - - if(!this.properties.enabled) - return; - - var geometry = this.getInputData(0); - if(!geometry) - return; - if(this.version != geometry._version || this.geometry_id != geometry._id ) - this.updateMesh( geometry ); - - if(!LiteGraph.LGraphRender.onRequestCameraMatrices) - { - console.warn("cannot render geometry, LiteGraph.onRequestCameraMatrices is null, remember to fill this with a callback(view_matrix, projection_matrix,viewprojection_matrix) to use 3D rendering from the graph"); - return; - } - - LiteGraph.LGraphRender.onRequestCameraMatrices( view_matrix, projection_matrix,viewprojection_matrix ); - var shader = null; - - var texture = this.getInputData(2); - - if(texture) - { - shader = gl.shaders["textured_points"]; - if(!shader) - shader = gl.shaders["textured_points"] = new GL.Shader( LGraphRenderGeometryDOF.vertex_shader_code, LGraphRenderGeometryDOF.fragment_shader_code, { USE_TEXTURED_POINTS:"" }); - } - else - { - shader = gl.shaders["points"]; - if(!shader) - shader = gl.shaders["points"] = new GL.Shader( LGraphRenderGeometryDOF.vertex_shader_code, LGraphRenderGeometryDOF.fragment_shader_code, { USE_POINTS: "" }); - } - - this.color.set( this.properties.color ); - this.color[3] = this.properties.opacity; - - var m = this.getInputData(1); - if(m) - model_matrix.set(m); - else - mat4.identity( model_matrix ); - - this.uniforms.u_point_size = this.properties.point_size; - this.uniforms.u_point_perspective = this.properties.fixed_size ? 0 : 1; - this.uniforms.u_perspective = gl.viewport_data[3] * projection_matrix[5]; - - shader.uniforms( global_uniforms ); - shader.uniforms( this.uniforms ); - - if(this.properties.opacity >= 1) - gl.disable( gl.BLEND ); - else - gl.enable( gl.BLEND ); - - gl.enable( gl.DEPTH_TEST ); - if( this.properties.additive ) - { - gl.blendFunc( gl.SRC_ALPHA, gl.ONE ); - gl.depthMask( false ); - } - else - gl.blendFunc( gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA ); - - shader.draw( this.mesh, GL.POINTS ); - - gl.disable( gl.BLEND ); - gl.depthMask( true ); - } - - LiteGraph.registerNodeType( "geometry/render_dof", LGraphRenderGeometryDOF ); - - LGraphRenderGeometryDOF.vertex_shader_code = '\ - precision mediump float;\n\ - attribute vec3 a_vertex;\n\ - varying vec3 v_vertex;\n\ - attribute vec3 a_normal;\n\ - varying vec3 v_normal;\n\ - #ifdef USE_COLOR\n\ - attribute vec4 a_color;\n\ - varying vec4 v_color;\n\ - #endif\n\ - attribute vec2 a_coord;\n\ - varying vec2 v_coord;\n\ - #ifdef USE_SIZE\n\ - attribute float a_extra;\n\ - #endif\n\ - #ifdef USE_INSTANCING\n\ - attribute mat4 u_model;\n\ - #else\n\ - uniform mat4 u_model;\n\ - #endif\n\ - uniform mat4 u_viewprojection;\n\ - uniform float u_point_size;\n\ - uniform float u_perspective;\n\ - uniform float u_point_perspective;\n\ - float computePointSize(float radius, float w)\n\ - {\n\ - if(radius < 0.0)\n\ - return -radius;\n\ - return u_perspective * radius / w;\n\ - }\n\ - void main() {\n\ - v_coord = a_coord;\n\ - #ifdef USE_COLOR\n\ - v_color = a_color;\n\ - #endif\n\ - v_vertex = ( u_model * vec4( a_vertex, 1.0 )).xyz;\n\ - v_normal = ( u_model * vec4( a_normal, 0.0 )).xyz;\n\ - gl_Position = u_viewprojection * vec4(v_vertex,1.0);\n\ - gl_PointSize = u_point_size;\n\ - #ifdef USE_SIZE\n\ - gl_PointSize = a_extra;\n\ - #endif\n\ - if(u_point_perspective != 0.0)\n\ - gl_PointSize = computePointSize( gl_PointSize, gl_Position.w );\n\ - }\ - '; - - LGraphRenderGeometryDOF.fragment_shader_code = '\ - precision mediump float;\n\ - uniform vec4 u_color;\n\ - #ifdef USE_COLOR\n\ - varying vec4 v_color;\n\ - #endif\n\ - varying vec2 v_coord;\n\ - uniform sampler2D u_texture;\n\ - void main() {\n\ - vec4 color = u_color;\n\ - #ifdef USE_TEXTURED_POINTS\n\ - color *= texture2D(u_texture, gl_PointCoord.xy);\n\ - #else\n\ - #ifdef USE_TEXTURE\n\ - color *= texture2D(u_texture, v_coord);\n\ - if(color.a < 0.1)\n\ - discard;\n\ - #endif\n\ - #ifdef USE_POINTS\n\ - float dist = length( gl_PointCoord.xy - vec2(0.5) );\n\ - if( dist > 0.45 )\n\ - discard;\n\ - #endif\n\ - #endif\n\ - #ifdef USE_COLOR\n\ - color *= v_color;\n\ - #endif\n\ - gl_FragColor = color;\n\ - }\ - '; - */ - - - })(this); -(function(global) { - var LiteGraph = global.LiteGraph; - var LGraphTexture = global.LGraphTexture; - - //Works with Litegl.js to create WebGL nodes - if (typeof GL != "undefined") { - // Texture Lens ***************************************** - function LGraphFXLens() { - this.addInput("Texture", "Texture"); - this.addInput("Aberration", "number"); - this.addInput("Distortion", "number"); - this.addInput("Blur", "number"); - this.addOutput("Texture", "Texture"); - this.properties = { - aberration: 1.0, - distortion: 1.0, - blur: 1.0, - precision: LGraphTexture.DEFAULT - }; - - if (!LGraphFXLens._shader) { - LGraphFXLens._shader = new GL.Shader( - GL.Shader.SCREEN_VERTEX_SHADER, - LGraphFXLens.pixel_shader - ); - LGraphFXLens._texture = new GL.Texture(3, 1, { - format: gl.RGB, - wrap: gl.CLAMP_TO_EDGE, - magFilter: gl.LINEAR, - minFilter: gl.LINEAR, - pixel_data: [255, 0, 0, 0, 255, 0, 0, 0, 255] - }); - } - } - - LGraphFXLens.title = "Lens"; - LGraphFXLens.desc = "Camera Lens distortion"; - LGraphFXLens.widgets_info = { - precision: { widget: "combo", values: LGraphTexture.MODE_VALUES } - }; - - LGraphFXLens.prototype.onExecute = function() { - var tex = this.getInputData(0); - if (this.properties.precision === LGraphTexture.PASS_THROUGH) { - this.setOutputData(0, tex); - return; - } - - if (!tex) { - return; - } - - this._tex = LGraphTexture.getTargetTexture( - tex, - this._tex, - this.properties.precision - ); - - var aberration = this.properties.aberration; - if (this.isInputConnected(1)) { - aberration = this.getInputData(1); - this.properties.aberration = aberration; - } - - var distortion = this.properties.distortion; - if (this.isInputConnected(2)) { - distortion = this.getInputData(2); - this.properties.distortion = distortion; - } - - var blur = this.properties.blur; - if (this.isInputConnected(3)) { - blur = this.getInputData(3); - this.properties.blur = blur; - } - - gl.disable(gl.BLEND); - gl.disable(gl.DEPTH_TEST); - var mesh = Mesh.getScreenQuad(); - var shader = LGraphFXLens._shader; - //var camera = LS.Renderer._current_camera; - - this._tex.drawTo(function() { - tex.bind(0); - shader - .uniforms({ - u_texture: 0, - u_aberration: aberration, - u_distortion: distortion, - u_blur: blur - }) - .draw(mesh); - }); - - this.setOutputData(0, this._tex); - }; - - LGraphFXLens.pixel_shader = - "precision highp float;\n\ - precision highp float;\n\ - varying vec2 v_coord;\n\ - uniform sampler2D u_texture;\n\ - uniform vec2 u_camera_planes;\n\ - uniform float u_aberration;\n\ - uniform float u_distortion;\n\ - uniform float u_blur;\n\ - \n\ - void main() {\n\ - vec2 coord = v_coord;\n\ - float dist = distance(vec2(0.5), coord);\n\ - vec2 dist_coord = coord - vec2(0.5);\n\ - float percent = 1.0 + ((0.5 - dist) / 0.5) * u_distortion;\n\ - dist_coord *= percent;\n\ - coord = dist_coord + vec2(0.5);\n\ - vec4 color = texture2D(u_texture,coord, u_blur * dist);\n\ - color.r = texture2D(u_texture,vec2(0.5) + dist_coord * (1.0+0.01*u_aberration), u_blur * dist ).r;\n\ - color.b = texture2D(u_texture,vec2(0.5) + dist_coord * (1.0-0.01*u_aberration), u_blur * dist ).b;\n\ - gl_FragColor = color;\n\ - }\n\ - "; - /* - float normalized_tunable_sigmoid(float xs, float k)\n\ - {\n\ - xs = xs * 2.0 - 1.0;\n\ - float signx = sign(xs);\n\ - float absx = abs(xs);\n\ - return signx * ((-k - 1.0)*absx)/(2.0*(-2.0*k*absx+k-1.0)) + 0.5;\n\ - }\n\ - */ - - LiteGraph.registerNodeType("fx/lens", LGraphFXLens); - global.LGraphFXLens = LGraphFXLens; - - /* not working yet - function LGraphDepthOfField() - { - this.addInput("Color","Texture"); - this.addInput("Linear Depth","Texture"); - this.addInput("Camera","camera"); - this.addOutput("Texture","Texture"); - this.properties = { high_precision: false }; - } - - LGraphDepthOfField.title = "Depth Of Field"; - LGraphDepthOfField.desc = "Applies a depth of field effect"; - - LGraphDepthOfField.prototype.onExecute = function() - { - var tex = this.getInputData(0); - var depth = this.getInputData(1); - var camera = this.getInputData(2); - - if(!tex || !depth || !camera) - { - this.setOutputData(0, tex); - return; - } - - var precision = gl.UNSIGNED_BYTE; - if(this.properties.high_precision) - precision = gl.half_float_ext ? gl.HALF_FLOAT_OES : gl.FLOAT; - if(!this._temp_texture || this._temp_texture.type != precision || - this._temp_texture.width != tex.width || this._temp_texture.height != tex.height) - this._temp_texture = new GL.Texture( tex.width, tex.height, { type: precision, format: gl.RGBA, filter: gl.LINEAR }); - - var shader = LGraphDepthOfField._shader = new GL.Shader( GL.Shader.SCREEN_VERTEX_SHADER, LGraphDepthOfField._pixel_shader ); - - var screen_mesh = Mesh.getScreenQuad(); - - gl.disable( gl.DEPTH_TEST ); - gl.disable( gl.BLEND ); - - var camera_position = camera.getEye(); - var focus_point = camera.getCenter(); - var distance = vec3.distance( camera_position, focus_point ); - var far = camera.far; - var focus_range = distance * 0.5; - - this._temp_texture.drawTo( function() { - tex.bind(0); - depth.bind(1); - shader.uniforms({u_texture:0, u_depth_texture:1, u_resolution: [1/tex.width, 1/tex.height], u_far: far, u_focus_point: distance, u_focus_scale: focus_range }).draw(screen_mesh); - }); - - this.setOutputData(0, this._temp_texture); - } - - //from http://tuxedolabs.blogspot.com.es/2018/05/bokeh-depth-of-field-in-single-pass.html - LGraphDepthOfField._pixel_shader = "\n\ - precision highp float;\n\ - varying vec2 v_coord;\n\ - uniform sampler2D u_texture; //Image to be processed\n\ - uniform sampler2D u_depth_texture; //Linear depth, where 1.0 == far plane\n\ - uniform vec2 u_iresolution; //The size of a pixel: vec2(1.0/width, 1.0/height)\n\ - uniform float u_far; // Far plane\n\ - uniform float u_focus_point;\n\ - uniform float u_focus_scale;\n\ - \n\ - const float GOLDEN_ANGLE = 2.39996323;\n\ - const float MAX_BLUR_SIZE = 20.0;\n\ - const float RAD_SCALE = 0.5; // Smaller = nicer blur, larger = faster\n\ - \n\ - float getBlurSize(float depth, float focusPoint, float focusScale)\n\ - {\n\ - float coc = clamp((1.0 / focusPoint - 1.0 / depth)*focusScale, -1.0, 1.0);\n\ - return abs(coc) * MAX_BLUR_SIZE;\n\ - }\n\ - \n\ - vec3 depthOfField(vec2 texCoord, float focusPoint, float focusScale)\n\ - {\n\ - float centerDepth = texture2D(u_depth_texture, texCoord).r * u_far;\n\ - float centerSize = getBlurSize(centerDepth, focusPoint, focusScale);\n\ - vec3 color = texture2D(u_texture, v_coord).rgb;\n\ - float tot = 1.0;\n\ - \n\ - float radius = RAD_SCALE;\n\ - for (float ang = 0.0; ang < 100.0; ang += GOLDEN_ANGLE)\n\ - {\n\ - vec2 tc = texCoord + vec2(cos(ang), sin(ang)) * u_iresolution * radius;\n\ - \n\ - vec3 sampleColor = texture2D(u_texture, tc).rgb;\n\ - float sampleDepth = texture2D(u_depth_texture, tc).r * u_far;\n\ - float sampleSize = getBlurSize( sampleDepth, focusPoint, focusScale );\n\ - if (sampleDepth > centerDepth)\n\ - sampleSize = clamp(sampleSize, 0.0, centerSize*2.0);\n\ - \n\ - float m = smoothstep(radius-0.5, radius+0.5, sampleSize);\n\ - color += mix(color/tot, sampleColor, m);\n\ - tot += 1.0;\n\ - radius += RAD_SCALE/radius;\n\ - if(radius>=MAX_BLUR_SIZE)\n\ - return color / tot;\n\ - }\n\ - return color / tot;\n\ - }\n\ - void main()\n\ - {\n\ - gl_FragColor = vec4( depthOfField( v_coord, u_focus_point, u_focus_scale ), 1.0 );\n\ - //gl_FragColor = vec4( texture2D(u_depth_texture, v_coord).r );\n\ - }\n\ - "; - - LiteGraph.registerNodeType("fx/DOF", LGraphDepthOfField ); - global.LGraphDepthOfField = LGraphDepthOfField; - */ - - //******************************************************* - - function LGraphFXBokeh() { - this.addInput("Texture", "Texture"); - this.addInput("Blurred", "Texture"); - this.addInput("Mask", "Texture"); - this.addInput("Threshold", "number"); - this.addOutput("Texture", "Texture"); - this.properties = { - shape: "", - size: 10, - alpha: 1.0, - threshold: 1.0, - high_precision: false - }; - } - - LGraphFXBokeh.title = "Bokeh"; - LGraphFXBokeh.desc = "applies an Bokeh effect"; - - LGraphFXBokeh.widgets_info = { shape: { widget: "texture" } }; - - LGraphFXBokeh.prototype.onExecute = function() { - var tex = this.getInputData(0); - var blurred_tex = this.getInputData(1); - var mask_tex = this.getInputData(2); - if (!tex || !mask_tex || !this.properties.shape) { - this.setOutputData(0, tex); - return; - } - - if (!blurred_tex) { - blurred_tex = tex; - } - - var shape_tex = LGraphTexture.getTexture(this.properties.shape); - if (!shape_tex) { - return; - } - - var threshold = this.properties.threshold; - if (this.isInputConnected(3)) { - threshold = this.getInputData(3); - this.properties.threshold = threshold; - } - - var precision = gl.UNSIGNED_BYTE; - if (this.properties.high_precision) { - precision = gl.half_float_ext ? gl.HALF_FLOAT_OES : gl.FLOAT; - } - if ( - !this._temp_texture || - this._temp_texture.type != precision || - this._temp_texture.width != tex.width || - this._temp_texture.height != tex.height - ) { - this._temp_texture = new GL.Texture(tex.width, tex.height, { - type: precision, - format: gl.RGBA, - filter: gl.LINEAR - }); - } - - //iterations - var size = this.properties.size; - - var first_shader = LGraphFXBokeh._first_shader; - if (!first_shader) { - first_shader = LGraphFXBokeh._first_shader = new GL.Shader( - Shader.SCREEN_VERTEX_SHADER, - LGraphFXBokeh._first_pixel_shader - ); - } - - var second_shader = LGraphFXBokeh._second_shader; - if (!second_shader) { - second_shader = LGraphFXBokeh._second_shader = new GL.Shader( - LGraphFXBokeh._second_vertex_shader, - LGraphFXBokeh._second_pixel_shader - ); - } - - var points_mesh = this._points_mesh; - if ( - !points_mesh || - points_mesh._width != tex.width || - points_mesh._height != tex.height || - points_mesh._spacing != 2 - ) { - points_mesh = this.createPointsMesh(tex.width, tex.height, 2); - } - - var screen_mesh = Mesh.getScreenQuad(); - - var point_size = this.properties.size; - var min_light = this.properties.min_light; - var alpha = this.properties.alpha; - - gl.disable(gl.DEPTH_TEST); - gl.disable(gl.BLEND); - - this._temp_texture.drawTo(function() { - tex.bind(0); - blurred_tex.bind(1); - mask_tex.bind(2); - first_shader - .uniforms({ - u_texture: 0, - u_texture_blur: 1, - u_mask: 2, - u_texsize: [tex.width, tex.height] - }) - .draw(screen_mesh); - }); - - this._temp_texture.drawTo(function() { - //clear because we use blending - //gl.clearColor(0.0,0.0,0.0,1.0); - //gl.clear( gl.COLOR_BUFFER_BIT ); - gl.enable(gl.BLEND); - gl.blendFunc(gl.ONE, gl.ONE); - - tex.bind(0); - shape_tex.bind(3); - second_shader - .uniforms({ - u_texture: 0, - u_mask: 2, - u_shape: 3, - u_alpha: alpha, - u_threshold: threshold, - u_pointSize: point_size, - u_itexsize: [1.0 / tex.width, 1.0 / tex.height] - }) - .draw(points_mesh, gl.POINTS); - }); - - this.setOutputData(0, this._temp_texture); - }; - - LGraphFXBokeh.prototype.createPointsMesh = function( - width, - height, - spacing - ) { - var nwidth = Math.round(width / spacing); - var nheight = Math.round(height / spacing); - - var vertices = new Float32Array(nwidth * nheight * 2); - - var ny = -1; - var dx = (2 / width) * spacing; - var dy = (2 / height) * spacing; - for (var y = 0; y < nheight; ++y) { - var nx = -1; - for (var x = 0; x < nwidth; ++x) { - var pos = y * nwidth * 2 + x * 2; - vertices[pos] = nx; - vertices[pos + 1] = ny; - nx += dx; - } - ny += dy; - } - - this._points_mesh = GL.Mesh.load({ vertices2D: vertices }); - this._points_mesh._width = width; - this._points_mesh._height = height; - this._points_mesh._spacing = spacing; - - return this._points_mesh; - }; - - /* - LGraphTextureBokeh._pixel_shader = "precision highp float;\n\ - varying vec2 a_coord;\n\ - uniform sampler2D u_texture;\n\ - uniform sampler2D u_shape;\n\ - \n\ - void main() {\n\ - vec4 color = texture2D( u_texture, gl_PointCoord );\n\ - color *= v_color * u_alpha;\n\ - gl_FragColor = color;\n\ - }\n"; - */ - - LGraphFXBokeh._first_pixel_shader = - "precision highp float;\n\ - precision highp float;\n\ - varying vec2 v_coord;\n\ - uniform sampler2D u_texture;\n\ - uniform sampler2D u_texture_blur;\n\ - uniform sampler2D u_mask;\n\ - \n\ - void main() {\n\ - vec4 color = texture2D(u_texture, v_coord);\n\ - vec4 blurred_color = texture2D(u_texture_blur, v_coord);\n\ - float mask = texture2D(u_mask, v_coord).x;\n\ - gl_FragColor = mix(color, blurred_color, mask);\n\ - }\n\ - "; - - LGraphFXBokeh._second_vertex_shader = - "precision highp float;\n\ - attribute vec2 a_vertex2D;\n\ - varying vec4 v_color;\n\ - uniform sampler2D u_texture;\n\ - uniform sampler2D u_mask;\n\ - uniform vec2 u_itexsize;\n\ - uniform float u_pointSize;\n\ - uniform float u_threshold;\n\ - void main() {\n\ - vec2 coord = a_vertex2D * 0.5 + 0.5;\n\ - v_color = texture2D( u_texture, coord );\n\ - v_color += texture2D( u_texture, coord + vec2(u_itexsize.x, 0.0) );\n\ - v_color += texture2D( u_texture, coord + vec2(0.0, u_itexsize.y));\n\ - v_color += texture2D( u_texture, coord + u_itexsize);\n\ - v_color *= 0.25;\n\ - float mask = texture2D(u_mask, coord).x;\n\ - float luminance = length(v_color) * mask;\n\ - /*luminance /= (u_pointSize*u_pointSize)*0.01 */;\n\ - luminance -= u_threshold;\n\ - if(luminance < 0.0)\n\ - {\n\ - gl_Position.x = -100.0;\n\ - return;\n\ - }\n\ - gl_PointSize = u_pointSize;\n\ - gl_Position = vec4(a_vertex2D,0.0,1.0);\n\ - }\n\ - "; - - LGraphFXBokeh._second_pixel_shader = - "precision highp float;\n\ - varying vec4 v_color;\n\ - uniform sampler2D u_shape;\n\ - uniform float u_alpha;\n\ - \n\ - void main() {\n\ - vec4 color = texture2D( u_shape, gl_PointCoord );\n\ - color *= v_color * u_alpha;\n\ - gl_FragColor = color;\n\ - }\n"; - - LiteGraph.registerNodeType("fx/bokeh", LGraphFXBokeh); - global.LGraphFXBokeh = LGraphFXBokeh; - - //************************************************ - - function LGraphFXGeneric() { - this.addInput("Texture", "Texture"); - this.addInput("value1", "number"); - this.addInput("value2", "number"); - this.addOutput("Texture", "Texture"); - this.properties = { - fx: "halftone", - value1: 1, - value2: 1, - precision: LGraphTexture.DEFAULT - }; - } - - LGraphFXGeneric.title = "FX"; - LGraphFXGeneric.desc = "applies an FX from a list"; - - LGraphFXGeneric.widgets_info = { - fx: { - widget: "combo", - values: ["halftone", "pixelate", "lowpalette", "noise", "gamma"] - }, - precision: { widget: "combo", values: LGraphTexture.MODE_VALUES } - }; - LGraphFXGeneric.shaders = {}; - - LGraphFXGeneric.prototype.onExecute = function() { - if (!this.isOutputConnected(0)) { - return; - } //saves work - - var tex = this.getInputData(0); - if (this.properties.precision === LGraphTexture.PASS_THROUGH) { - this.setOutputData(0, tex); - return; - } - - if (!tex) { - return; - } - - this._tex = LGraphTexture.getTargetTexture( - tex, - this._tex, - this.properties.precision - ); - - //iterations - var value1 = this.properties.value1; - if (this.isInputConnected(1)) { - value1 = this.getInputData(1); - this.properties.value1 = value1; - } - - var value2 = this.properties.value2; - if (this.isInputConnected(2)) { - value2 = this.getInputData(2); - this.properties.value2 = value2; - } - - var fx = this.properties.fx; - var shader = LGraphFXGeneric.shaders[fx]; - if (!shader) { - var pixel_shader_code = LGraphFXGeneric["pixel_shader_" + fx]; - if (!pixel_shader_code) { - return; - } - - shader = LGraphFXGeneric.shaders[fx] = new GL.Shader( - Shader.SCREEN_VERTEX_SHADER, - pixel_shader_code - ); - } - - gl.disable(gl.BLEND); - gl.disable(gl.DEPTH_TEST); - var mesh = Mesh.getScreenQuad(); - var camera = global.LS ? LS.Renderer._current_camera : null; - var camera_planes; - if (camera) { - camera_planes = [ - LS.Renderer._current_camera.near, - LS.Renderer._current_camera.far - ]; - } else { - camera_planes = [1, 100]; - } - - var noise = null; - if (fx == "noise") { - noise = LGraphTexture.getNoiseTexture(); - } - - this._tex.drawTo(function() { - tex.bind(0); - if (fx == "noise") { - noise.bind(1); - } - - shader - .uniforms({ - u_texture: 0, - u_noise: 1, - u_size: [tex.width, tex.height], - u_rand: [Math.random(), Math.random()], - u_value1: value1, - u_value2: value2, - u_camera_planes: camera_planes - }) - .draw(mesh); - }); - - this.setOutputData(0, this._tex); - }; - - LGraphFXGeneric.pixel_shader_halftone = - "precision highp float;\n\ - varying vec2 v_coord;\n\ - uniform sampler2D u_texture;\n\ - uniform vec2 u_camera_planes;\n\ - uniform vec2 u_size;\n\ - uniform float u_value1;\n\ - uniform float u_value2;\n\ - \n\ - float pattern() {\n\ - float s = sin(u_value1 * 3.1415), c = cos(u_value1 * 3.1415);\n\ - vec2 tex = v_coord * u_size.xy;\n\ - vec2 point = vec2(\n\ - c * tex.x - s * tex.y ,\n\ - s * tex.x + c * tex.y \n\ - ) * u_value2;\n\ - return (sin(point.x) * sin(point.y)) * 4.0;\n\ - }\n\ - void main() {\n\ - vec4 color = texture2D(u_texture, v_coord);\n\ - float average = (color.r + color.g + color.b) / 3.0;\n\ - gl_FragColor = vec4(vec3(average * 10.0 - 5.0 + pattern()), color.a);\n\ - }\n"; - - LGraphFXGeneric.pixel_shader_pixelate = - "precision highp float;\n\ - varying vec2 v_coord;\n\ - uniform sampler2D u_texture;\n\ - uniform vec2 u_camera_planes;\n\ - uniform vec2 u_size;\n\ - uniform float u_value1;\n\ - uniform float u_value2;\n\ - \n\ - void main() {\n\ - vec2 coord = vec2( floor(v_coord.x * u_value1) / u_value1, floor(v_coord.y * u_value2) / u_value2 );\n\ - vec4 color = texture2D(u_texture, coord);\n\ - gl_FragColor = color;\n\ - }\n"; - - LGraphFXGeneric.pixel_shader_lowpalette = - "precision highp float;\n\ - varying vec2 v_coord;\n\ - uniform sampler2D u_texture;\n\ - uniform vec2 u_camera_planes;\n\ - uniform vec2 u_size;\n\ - uniform float u_value1;\n\ - uniform float u_value2;\n\ - \n\ - void main() {\n\ - vec4 color = texture2D(u_texture, v_coord);\n\ - gl_FragColor = floor(color * u_value1) / u_value1;\n\ - }\n"; - - LGraphFXGeneric.pixel_shader_noise = - "precision highp float;\n\ - varying vec2 v_coord;\n\ - uniform sampler2D u_texture;\n\ - uniform sampler2D u_noise;\n\ - uniform vec2 u_size;\n\ - uniform float u_value1;\n\ - uniform float u_value2;\n\ - uniform vec2 u_rand;\n\ - \n\ - void main() {\n\ - vec4 color = texture2D(u_texture, v_coord);\n\ - vec3 noise = texture2D(u_noise, v_coord * vec2(u_size.x / 512.0, u_size.y / 512.0) + u_rand).xyz - vec3(0.5);\n\ - gl_FragColor = vec4( color.xyz + noise * u_value1, color.a );\n\ - }\n"; - - LGraphFXGeneric.pixel_shader_gamma = - "precision highp float;\n\ - varying vec2 v_coord;\n\ - uniform sampler2D u_texture;\n\ - uniform float u_value1;\n\ - \n\ - void main() {\n\ - vec4 color = texture2D(u_texture, v_coord);\n\ - float gamma = 1.0 / u_value1;\n\ - gl_FragColor = vec4( pow( color.xyz, vec3(gamma) ), color.a );\n\ - }\n"; - - LiteGraph.registerNodeType("fx/generic", LGraphFXGeneric); - global.LGraphFXGeneric = LGraphFXGeneric; - - // Vigneting ************************************ - - function LGraphFXVigneting() { - this.addInput("Tex.", "Texture"); - this.addInput("intensity", "number"); - - this.addOutput("Texture", "Texture"); - this.properties = { - intensity: 1, - invert: false, - precision: LGraphTexture.DEFAULT - }; - - if (!LGraphFXVigneting._shader) { - LGraphFXVigneting._shader = new GL.Shader( - Shader.SCREEN_VERTEX_SHADER, - LGraphFXVigneting.pixel_shader - ); - } - } - - LGraphFXVigneting.title = "Vigneting"; - LGraphFXVigneting.desc = "Vigneting"; - - LGraphFXVigneting.widgets_info = { - precision: { widget: "combo", values: LGraphTexture.MODE_VALUES } - }; - - LGraphFXVigneting.prototype.onExecute = function() { - var tex = this.getInputData(0); - - if (this.properties.precision === LGraphTexture.PASS_THROUGH) { - this.setOutputData(0, tex); - return; - } - - if (!tex) { - return; - } - - this._tex = LGraphTexture.getTargetTexture( - tex, - this._tex, - this.properties.precision - ); - - var intensity = this.properties.intensity; - if (this.isInputConnected(1)) { - intensity = this.getInputData(1); - this.properties.intensity = intensity; - } - - gl.disable(gl.BLEND); - gl.disable(gl.DEPTH_TEST); - - var mesh = Mesh.getScreenQuad(); - var shader = LGraphFXVigneting._shader; - var invert = this.properties.invert; - - this._tex.drawTo(function() { - tex.bind(0); - shader - .uniforms({ - u_texture: 0, - u_intensity: intensity, - u_isize: [1 / tex.width, 1 / tex.height], - u_invert: invert ? 1 : 0 - }) - .draw(mesh); - }); - - this.setOutputData(0, this._tex); - }; - - LGraphFXVigneting.pixel_shader = - "precision highp float;\n\ - precision highp float;\n\ - varying vec2 v_coord;\n\ - uniform sampler2D u_texture;\n\ - uniform float u_intensity;\n\ - uniform int u_invert;\n\ - \n\ - void main() {\n\ - float luminance = 1.0 - length( v_coord - vec2(0.5) ) * 1.414;\n\ - vec4 color = texture2D(u_texture, v_coord);\n\ - if(u_invert == 1)\n\ - luminance = 1.0 - luminance;\n\ - luminance = mix(1.0, luminance, u_intensity);\n\ - gl_FragColor = vec4( luminance * color.xyz, color.a);\n\ - }\n\ - "; - - LiteGraph.registerNodeType("fx/vigneting", LGraphFXVigneting); - global.LGraphFXVigneting = LGraphFXVigneting; - } -})(this); - -(function(global) { - var LiteGraph = global.LiteGraph; - var MIDI_COLOR = "#243"; - - function MIDIEvent(data) { - this.channel = 0; - this.cmd = 0; - this.data = new Uint32Array(3); - - if (data) { - this.setup(data); - } - } - - LiteGraph.MIDIEvent = MIDIEvent; - - MIDIEvent.prototype.fromJSON = function(o) { - this.setup(o.data); - }; - - MIDIEvent.prototype.setup = function(data) { - var raw_data = data; - if (data.constructor === Object) { - raw_data = data.data; - } - - this.data.set(raw_data); - - var midiStatus = raw_data[0]; - this.status = midiStatus; - - var midiCommand = midiStatus & 0xf0; - - if (midiStatus >= 0xf0) { - this.cmd = midiStatus; - } else { - this.cmd = midiCommand; - } - - if (this.cmd == MIDIEvent.NOTEON && this.velocity == 0) { - this.cmd = MIDIEvent.NOTEOFF; - } - - this.cmd_str = MIDIEvent.commands[this.cmd] || ""; - - if ( - midiCommand >= MIDIEvent.NOTEON || - midiCommand <= MIDIEvent.NOTEOFF - ) { - this.channel = midiStatus & 0x0f; - } - }; - - Object.defineProperty(MIDIEvent.prototype, "velocity", { - get: function() { - if (this.cmd == MIDIEvent.NOTEON) { - return this.data[2]; - } - return -1; - }, - set: function(v) { - this.data[2] = v; // v / 127; - }, - enumerable: true - }); - - MIDIEvent.notes = [ - "A", - "A#", - "B", - "C", - "C#", - "D", - "D#", - "E", - "F", - "F#", - "G", - "G#" - ]; - MIDIEvent.note_to_index = { - A: 0, - "A#": 1, - B: 2, - C: 3, - "C#": 4, - D: 5, - "D#": 6, - E: 7, - F: 8, - "F#": 9, - G: 10, - "G#": 11 - }; - - Object.defineProperty(MIDIEvent.prototype, "note", { - get: function() { - if (this.cmd != MIDIEvent.NOTEON) { - return -1; - } - return MIDIEvent.toNoteString(this.data[1], true); - }, - set: function(v) { - throw "notes cannot be assigned this way, must modify the data[1]"; - }, - enumerable: true - }); - - Object.defineProperty(MIDIEvent.prototype, "octave", { - get: function() { - if (this.cmd != MIDIEvent.NOTEON) { - return -1; - } - var octave = this.data[1] - 24; - return Math.floor(octave / 12 + 1); - }, - set: function(v) { - throw "octave cannot be assigned this way, must modify the data[1]"; - }, - enumerable: true - }); - - //returns HZs - MIDIEvent.prototype.getPitch = function() { - return Math.pow(2, (this.data[1] - 69) / 12) * 440; - }; - - MIDIEvent.computePitch = function(note) { - return Math.pow(2, (note - 69) / 12) * 440; - }; - - MIDIEvent.prototype.getCC = function() { - return this.data[1]; - }; - - MIDIEvent.prototype.getCCValue = function() { - return this.data[2]; - }; - - //not tested, there is a formula missing here - MIDIEvent.prototype.getPitchBend = function() { - return this.data[1] + (this.data[2] << 7) - 8192; - }; - - MIDIEvent.computePitchBend = function(v1, v2) { - return v1 + (v2 << 7) - 8192; - }; - - MIDIEvent.prototype.setCommandFromString = function(str) { - this.cmd = MIDIEvent.computeCommandFromString(str); - }; - - MIDIEvent.computeCommandFromString = function(str) { - if (!str) { - return 0; - } - - if (str && str.constructor === Number) { - return str; - } - - str = str.toUpperCase(); - switch (str) { - case "NOTE ON": - case "NOTEON": - return MIDIEvent.NOTEON; - break; - case "NOTE OFF": - case "NOTEOFF": - return MIDIEvent.NOTEON; - break; - case "KEY PRESSURE": - case "KEYPRESSURE": - return MIDIEvent.KEYPRESSURE; - break; - case "CONTROLLER CHANGE": - case "CONTROLLERCHANGE": - case "CC": - return MIDIEvent.CONTROLLERCHANGE; - break; - case "PROGRAM CHANGE": - case "PROGRAMCHANGE": - case "PC": - return MIDIEvent.PROGRAMCHANGE; - break; - case "CHANNEL PRESSURE": - case "CHANNELPRESSURE": - return MIDIEvent.CHANNELPRESSURE; - break; - case "PITCH BEND": - case "PITCHBEND": - return MIDIEvent.PITCHBEND; - break; - case "TIME TICK": - case "TIMETICK": - return MIDIEvent.TIMETICK; - break; - default: - return Number(str); //assume its a hex code - } - }; - - //transform from a pitch number to string like "C4" - MIDIEvent.toNoteString = function(d, skip_octave) { - d = Math.round(d); //in case it has decimals - var note = d - 21; - var octave = Math.floor((d - 24) / 12 + 1); - note = note % 12; - if (note < 0) { - note = 12 + note; - } - return MIDIEvent.notes[note] + (skip_octave ? "" : octave); - }; - - MIDIEvent.NoteStringToPitch = function(str) { - str = str.toUpperCase(); - var note = str[0]; - var octave = 4; - - if (str[1] == "#") { - note += "#"; - if (str.length > 2) { - octave = Number(str[2]); - } - } else { - if (str.length > 1) { - octave = Number(str[1]); - } - } - var pitch = MIDIEvent.note_to_index[note]; - if (pitch == null) { - return null; - } - return (octave - 1) * 12 + pitch + 21; - }; - - MIDIEvent.prototype.toString = function() { - var str = "" + this.channel + ". "; - switch (this.cmd) { - case MIDIEvent.NOTEON: - str += "NOTEON " + MIDIEvent.toNoteString(this.data[1]); - break; - case MIDIEvent.NOTEOFF: - str += "NOTEOFF " + MIDIEvent.toNoteString(this.data[1]); - break; - case MIDIEvent.CONTROLLERCHANGE: - str += "CC " + this.data[1] + " " + this.data[2]; - break; - case MIDIEvent.PROGRAMCHANGE: - str += "PC " + this.data[1]; - break; - case MIDIEvent.PITCHBEND: - str += "PITCHBEND " + this.getPitchBend(); - break; - case MIDIEvent.KEYPRESSURE: - str += "KEYPRESS " + this.data[1]; - break; - } - - return str; - }; - - MIDIEvent.prototype.toHexString = function() { - var str = ""; - for (var i = 0; i < this.data.length; i++) { - str += this.data[i].toString(16) + " "; - } - }; - - MIDIEvent.prototype.toJSON = function() { - return { - data: [this.data[0], this.data[1], this.data[2]], - object_class: "MIDIEvent" - }; - }; - - MIDIEvent.NOTEOFF = 0x80; - MIDIEvent.NOTEON = 0x90; - MIDIEvent.KEYPRESSURE = 0xa0; - MIDIEvent.CONTROLLERCHANGE = 0xb0; - MIDIEvent.PROGRAMCHANGE = 0xc0; - MIDIEvent.CHANNELPRESSURE = 0xd0; - MIDIEvent.PITCHBEND = 0xe0; - MIDIEvent.TIMETICK = 0xf8; - - MIDIEvent.commands = { - 0x80: "note off", - 0x90: "note on", - 0xa0: "key pressure", - 0xb0: "controller change", - 0xc0: "program change", - 0xd0: "channel pressure", - 0xe0: "pitch bend", - 0xf0: "system", - 0xf2: "Song pos", - 0xf3: "Song select", - 0xf6: "Tune request", - 0xf8: "time tick", - 0xfa: "Start Song", - 0xfb: "Continue Song", - 0xfc: "Stop Song", - 0xfe: "Sensing", - 0xff: "Reset" - }; - - MIDIEvent.commands_short = { - 0x80: "NOTEOFF", - 0x90: "NOTEOFF", - 0xa0: "KEYP", - 0xb0: "CC", - 0xc0: "PC", - 0xd0: "CP", - 0xe0: "PB", - 0xf0: "SYS", - 0xf2: "POS", - 0xf3: "SELECT", - 0xf6: "TUNEREQ", - 0xf8: "TT", - 0xfa: "START", - 0xfb: "CONTINUE", - 0xfc: "STOP", - 0xfe: "SENS", - 0xff: "RESET" - }; - - MIDIEvent.commands_reversed = {}; - for (var i in MIDIEvent.commands) { - MIDIEvent.commands_reversed[MIDIEvent.commands[i]] = i; - } - - //MIDI wrapper, instantiate by MIDIIn and MIDIOut - function MIDIInterface(on_ready, on_error) { - if (!navigator.requestMIDIAccess) { - this.error = "not suppoorted"; - if (on_error) { - on_error("Not supported"); - } else { - console.error("MIDI NOT SUPPORTED, enable by chrome://flags"); - } - return; - } - - this.on_ready = on_ready; - - this.state = { - note: [], - cc: [] - }; - - this.input_ports = null; - this.input_ports_info = []; - this.output_ports = null; - this.output_ports_info = []; - - navigator.requestMIDIAccess().then(this.onMIDISuccess.bind(this), this.onMIDIFailure.bind(this)); - } - - MIDIInterface.input = null; - - MIDIInterface.MIDIEvent = MIDIEvent; - - MIDIInterface.prototype.onMIDISuccess = function(midiAccess) { - console.log("MIDI ready!"); - console.log(midiAccess); - this.midi = midiAccess; // store in the global (in real usage, would probably keep in an object instance) - this.updatePorts(); - - if (this.on_ready) { - this.on_ready(this); - } - }; - - MIDIInterface.prototype.updatePorts = function() { - var midi = this.midi; - this.input_ports = midi.inputs; - this.input_ports_info = []; - this.output_ports = midi.outputs; - this.output_ports_info = []; - - var num = 0; - - var it = this.input_ports.values(); - var it_value = it.next(); - while (it_value && it_value.done === false) { - var port_info = it_value.value; - this.input_ports_info.push(port_info); - console.log( "Input port [type:'" + port_info.type + "'] id:'" + port_info.id + "' manufacturer:'" + port_info.manufacturer + "' name:'" + port_info.name + "' version:'" + port_info.version + "'" ); - num++; - it_value = it.next(); - } - this.num_input_ports = num; - - num = 0; - var it = this.output_ports.values(); - var it_value = it.next(); - while (it_value && it_value.done === false) { - var port_info = it_value.value; - this.output_ports_info.push(port_info); - console.log( "Output port [type:'" + port_info.type + "'] id:'" + port_info.id + "' manufacturer:'" + port_info.manufacturer + "' name:'" + port_info.name + "' version:'" + port_info.version + "'" ); - num++; - it_value = it.next(); - } - this.num_output_ports = num; - }; - - MIDIInterface.prototype.onMIDIFailure = function(msg) { - console.error("Failed to get MIDI access - " + msg); - }; - - MIDIInterface.prototype.openInputPort = function(port, callback) { - var input_port = this.input_ports.get("input-" + port); - if (!input_port) { - return false; - } - MIDIInterface.input = this; - var that = this; - - input_port.onmidimessage = function(a) { - var midi_event = new MIDIEvent(a.data); - that.updateState(midi_event); - if (callback) { - callback(a.data, midi_event); - } - if (MIDIInterface.on_message) { - MIDIInterface.on_message(a.data, midi_event); - } - }; - console.log("port open: ", input_port); - return true; - }; - - MIDIInterface.parseMsg = function(data) {}; - - MIDIInterface.prototype.updateState = function(midi_event) { - switch (midi_event.cmd) { - case MIDIEvent.NOTEON: - this.state.note[midi_event.value1 | 0] = midi_event.value2; - break; - case MIDIEvent.NOTEOFF: - this.state.note[midi_event.value1 | 0] = 0; - break; - case MIDIEvent.CONTROLLERCHANGE: - this.state.cc[midi_event.getCC()] = midi_event.getCCValue(); - break; - } - }; - - MIDIInterface.prototype.sendMIDI = function(port, midi_data) { - if (!midi_data) { - return; - } - - var output_port = this.output_ports_info[port];//this.output_ports.get("output-" + port); - if (!output_port) { - return; - } - - MIDIInterface.output = this; - - if (midi_data.constructor === MIDIEvent) { - output_port.send(midi_data.data); - } else { - output_port.send(midi_data); - } - }; - - function LGMIDIIn() { - this.addOutput("on_midi", LiteGraph.EVENT); - this.addOutput("out", "midi"); - this.properties = { port: 0 }; - this._last_midi_event = null; - this._current_midi_event = null; - this.boxcolor = "#AAA"; - this._last_time = 0; - - var that = this; - new MIDIInterface(function(midi) { - //open - that._midi = midi; - if (that._waiting) { - that.onStart(); - } - that._waiting = false; - }); - } - - LGMIDIIn.MIDIInterface = MIDIInterface; - - LGMIDIIn.title = "MIDI Input"; - LGMIDIIn.desc = "Reads MIDI from a input port"; - LGMIDIIn.color = MIDI_COLOR; - - LGMIDIIn.prototype.getPropertyInfo = function(name) { - if (!this._midi) { - return; - } - - if (name == "port") { - var values = {}; - for (var i = 0; i < this._midi.input_ports_info.length; ++i) { - var input = this._midi.input_ports_info[i]; - values[i] = i + ".- " + input.name + " version:" + input.version; - } - return { type: "enum", values: values }; - } - }; - - LGMIDIIn.prototype.onStart = function() { - if (this._midi) { - this._midi.openInputPort( - this.properties.port, - this.onMIDIEvent.bind(this) - ); - } else { - this._waiting = true; - } - }; - - LGMIDIIn.prototype.onMIDIEvent = function(data, midi_event) { - this._last_midi_event = midi_event; - this.boxcolor = "#AFA"; - this._last_time = LiteGraph.getTime(); - this.trigger("on_midi", midi_event); - if (midi_event.cmd == MIDIEvent.NOTEON) { - this.trigger("on_noteon", midi_event); - } else if (midi_event.cmd == MIDIEvent.NOTEOFF) { - this.trigger("on_noteoff", midi_event); - } else if (midi_event.cmd == MIDIEvent.CONTROLLERCHANGE) { - this.trigger("on_cc", midi_event); - } else if (midi_event.cmd == MIDIEvent.PROGRAMCHANGE) { - this.trigger("on_pc", midi_event); - } else if (midi_event.cmd == MIDIEvent.PITCHBEND) { - this.trigger("on_pitchbend", midi_event); - } - }; - - LGMIDIIn.prototype.onDrawBackground = function(ctx) { - this.boxcolor = "#AAA"; - if (!this.flags.collapsed && this._last_midi_event) { - ctx.fillStyle = "white"; - var now = LiteGraph.getTime(); - var f = 1.0 - Math.max(0, (now - this._last_time) * 0.001); - if (f > 0) { - var t = ctx.globalAlpha; - ctx.globalAlpha *= f; - ctx.font = "12px Tahoma"; - ctx.fillText( - this._last_midi_event.toString(), - 2, - this.size[1] * 0.5 + 3 - ); - //ctx.fillRect(0,0,this.size[0],this.size[1]); - ctx.globalAlpha = t; - } - } - }; - - LGMIDIIn.prototype.onExecute = function() { - if (this.outputs) { - var last = this._last_midi_event; - for (var i = 0; i < this.outputs.length; ++i) { - var output = this.outputs[i]; - var v = null; - switch (output.name) { - case "midi": - v = this._midi; - break; - case "last_midi": - v = last; - break; - default: - continue; - } - this.setOutputData(i, v); - } - } - }; - - LGMIDIIn.prototype.onGetOutputs = function() { - return [ - ["last_midi", "midi"], - ["on_midi", LiteGraph.EVENT], - ["on_noteon", LiteGraph.EVENT], - ["on_noteoff", LiteGraph.EVENT], - ["on_cc", LiteGraph.EVENT], - ["on_pc", LiteGraph.EVENT], - ["on_pitchbend", LiteGraph.EVENT] - ]; - }; - - LiteGraph.registerNodeType("midi/input", LGMIDIIn); - - function LGMIDIOut() { - this.addInput("send", LiteGraph.EVENT); - this.properties = { port: 0 }; - - var that = this; - new MIDIInterface(function(midi) { - that._midi = midi; - that.widget.options.values = that.getMIDIOutputs(); - }); - this.widget = this.addWidget("combo","Device",this.properties.port,{ property: "port", values: this.getMIDIOutputs.bind(this) }); - this.size = [340,60]; - } - - LGMIDIOut.MIDIInterface = MIDIInterface; - - LGMIDIOut.title = "MIDI Output"; - LGMIDIOut.desc = "Sends MIDI to output channel"; - LGMIDIOut.color = MIDI_COLOR; - - LGMIDIOut.prototype.onGetPropertyInfo = function(name) { - if (!this._midi) { - return; - } - - if (name == "port") { - var values = this.getMIDIOutputs(); - return { type: "enum", values: values }; - } - }; - LGMIDIOut.default_ports = {0:"unknown"}; - - LGMIDIOut.prototype.getMIDIOutputs = function() - { - var values = {}; - if(!this._midi) - return LGMIDIOut.default_ports; - if(this._midi.output_ports_info) - for (var i = 0; i < this._midi.output_ports_info.length; ++i) { - var output = this._midi.output_ports_info[i]; - if(!output) - continue; - var name = i + ".- " + output.name + " version:" + output.version; - values[i] = name; - } - return values; - } - - LGMIDIOut.prototype.onAction = function(event, midi_event) { - //console.log(midi_event); - if (!this._midi) { - return; - } - if (event == "send") { - this._midi.sendMIDI(this.properties.port, midi_event); - } - this.trigger("midi", midi_event); - }; - - LGMIDIOut.prototype.onGetInputs = function() { - return [["send", LiteGraph.ACTION]]; - }; - - LGMIDIOut.prototype.onGetOutputs = function() { - return [["on_midi", LiteGraph.EVENT]]; - }; - - LiteGraph.registerNodeType("midi/output", LGMIDIOut); - - - function LGMIDIShow() { - this.addInput("on_midi", LiteGraph.EVENT); - this._str = ""; - this.size = [200, 40]; - } - - LGMIDIShow.title = "MIDI Show"; - LGMIDIShow.desc = "Shows MIDI in the graph"; - LGMIDIShow.color = MIDI_COLOR; - - LGMIDIShow.prototype.getTitle = function() { - if (this.flags.collapsed) { - return this._str; - } - return this.title; - }; - - LGMIDIShow.prototype.onAction = function(event, midi_event) { - if (!midi_event) { - return; - } - if (midi_event.constructor === MIDIEvent) { - this._str = midi_event.toString(); - } else { - this._str = "???"; - } - }; - - LGMIDIShow.prototype.onDrawForeground = function(ctx) { - if (!this._str || this.flags.collapsed) { - return; - } - - ctx.font = "30px Arial"; - ctx.fillText(this._str, 10, this.size[1] * 0.8); - }; - - LGMIDIShow.prototype.onGetInputs = function() { - return [["in", LiteGraph.ACTION]]; - }; - - LGMIDIShow.prototype.onGetOutputs = function() { - return [["on_midi", LiteGraph.EVENT]]; - }; - - LiteGraph.registerNodeType("midi/show", LGMIDIShow); - - function LGMIDIFilter() { - this.properties = { - channel: -1, - cmd: -1, - min_value: -1, - max_value: -1 - }; - - var that = this; - this._learning = false; - this.addWidget("button", "Learn", "", function() { - that._learning = true; - that.boxcolor = "#FA3"; - }); - - this.addInput("in", LiteGraph.EVENT); - this.addOutput("on_midi", LiteGraph.EVENT); - this.boxcolor = "#AAA"; - } - - LGMIDIFilter.title = "MIDI Filter"; - LGMIDIFilter.desc = "Filters MIDI messages"; - LGMIDIFilter.color = MIDI_COLOR; - - LGMIDIFilter["@cmd"] = { - type: "enum", - title: "Command", - values: MIDIEvent.commands_reversed - }; - - LGMIDIFilter.prototype.getTitle = function() { - var str = null; - if (this.properties.cmd == -1) { - str = "Nothing"; - } else { - str = MIDIEvent.commands_short[this.properties.cmd] || "Unknown"; - } - - if ( - this.properties.min_value != -1 && - this.properties.max_value != -1 - ) { - str += - " " + - (this.properties.min_value == this.properties.max_value - ? this.properties.max_value - : this.properties.min_value + - ".." + - this.properties.max_value); - } - - return "Filter: " + str; - }; - - LGMIDIFilter.prototype.onPropertyChanged = function(name, value) { - if (name == "cmd") { - var num = Number(value); - if (isNaN(num)) { - num = MIDIEvent.commands[value] || 0; - } - this.properties.cmd = num; - } - }; - - LGMIDIFilter.prototype.onAction = function(event, midi_event) { - if (!midi_event || midi_event.constructor !== MIDIEvent) { - return; - } - - if (this._learning) { - this._learning = false; - this.boxcolor = "#AAA"; - this.properties.channel = midi_event.channel; - this.properties.cmd = midi_event.cmd; - this.properties.min_value = this.properties.max_value = - midi_event.data[1]; - } else { - if ( - this.properties.channel != -1 && - midi_event.channel != this.properties.channel - ) { - return; - } - if ( - this.properties.cmd != -1 && - midi_event.cmd != this.properties.cmd - ) { - return; - } - if ( - this.properties.min_value != -1 && - midi_event.data[1] < this.properties.min_value - ) { - return; - } - if ( - this.properties.max_value != -1 && - midi_event.data[1] > this.properties.max_value - ) { - return; - } - } - - this.trigger("on_midi", midi_event); - }; - - LiteGraph.registerNodeType("midi/filter", LGMIDIFilter); - - function LGMIDIEvent() { - this.properties = { - channel: 0, - cmd: 144, //0x90 - value1: 1, - value2: 1 - }; - - this.addInput("send", LiteGraph.EVENT); - this.addInput("assign", LiteGraph.EVENT); - this.addOutput("on_midi", LiteGraph.EVENT); - - this.midi_event = new MIDIEvent(); - this.gate = false; - } - - LGMIDIEvent.title = "MIDIEvent"; - LGMIDIEvent.desc = "Create a MIDI Event"; - LGMIDIEvent.color = MIDI_COLOR; - - LGMIDIEvent.prototype.onAction = function(event, midi_event) { - if (event == "assign") { - this.properties.channel = midi_event.channel; - this.properties.cmd = midi_event.cmd; - this.properties.value1 = midi_event.data[1]; - this.properties.value2 = midi_event.data[2]; - if (midi_event.cmd == MIDIEvent.NOTEON) { - this.gate = true; - } else if (midi_event.cmd == MIDIEvent.NOTEOFF) { - this.gate = false; - } - return; - } - - //send - var midi_event = this.midi_event; - midi_event.channel = this.properties.channel; - if (this.properties.cmd && this.properties.cmd.constructor === String) { - midi_event.setCommandFromString(this.properties.cmd); - } else { - midi_event.cmd = this.properties.cmd; - } - midi_event.data[0] = midi_event.cmd | midi_event.channel; - midi_event.data[1] = Number(this.properties.value1); - midi_event.data[2] = Number(this.properties.value2); - - this.trigger("on_midi", midi_event); - }; - - LGMIDIEvent.prototype.onExecute = function() { - var props = this.properties; - - if (this.inputs) { - for (var i = 0; i < this.inputs.length; ++i) { - var input = this.inputs[i]; - if (input.link == -1) { - continue; - } - switch (input.name) { - case "note": - var v = this.getInputData(i); - if (v != null) { - if (v.constructor === String) { - v = MIDIEvent.NoteStringToPitch(v); - } - this.properties.value1 = (v | 0) % 255; - } - break; - case "cmd": - var v = this.getInputData(i); - if (v != null) { - this.properties.cmd = v; - } - break; - case "value1": - var v = this.getInputData(i); - if (v != null) { - this.properties.value1 = clamp(v|0,0,127); - } - break; - case "value2": - var v = this.getInputData(i); - if (v != null) { - this.properties.value2 = clamp(v|0,0,127); - } - break; - } - } - } - - if (this.outputs) { - for (var i = 0; i < this.outputs.length; ++i) { - var output = this.outputs[i]; - var v = null; - switch (output.name) { - case "midi": - v = new MIDIEvent(); - v.setup([props.cmd, props.value1, props.value2]); - v.channel = props.channel; - break; - case "command": - v = props.cmd; - break; - case "cc": - v = props.value1; - break; - case "cc_value": - v = props.value2; - break; - case "note": - v = - props.cmd == MIDIEvent.NOTEON || - props.cmd == MIDIEvent.NOTEOFF - ? props.value1 - : null; - break; - case "velocity": - v = props.cmd == MIDIEvent.NOTEON ? props.value2 : null; - break; - case "pitch": - v = - props.cmd == MIDIEvent.NOTEON - ? MIDIEvent.computePitch(props.value1) - : null; - break; - case "pitchbend": - v = - props.cmd == MIDIEvent.PITCHBEND - ? MIDIEvent.computePitchBend( - props.value1, - props.value2 - ) - : null; - break; - case "gate": - v = this.gate; - break; - default: - continue; - } - if (v !== null) { - this.setOutputData(i, v); - } - } - } - }; - - LGMIDIEvent.prototype.onPropertyChanged = function(name, value) { - if (name == "cmd") { - this.properties.cmd = MIDIEvent.computeCommandFromString(value); - } - }; - - LGMIDIEvent.prototype.onGetInputs = function() { - return [["cmd", "number"],["note", "number"],["value1", "number"],["value2", "number"]]; - }; - - LGMIDIEvent.prototype.onGetOutputs = function() { - return [ - ["midi", "midi"], - ["on_midi", LiteGraph.EVENT], - ["command", "number"], - ["note", "number"], - ["velocity", "number"], - ["cc", "number"], - ["cc_value", "number"], - ["pitch", "number"], - ["gate", "bool"], - ["pitchbend", "number"] - ]; - }; - - LiteGraph.registerNodeType("midi/event", LGMIDIEvent); - - function LGMIDICC() { - this.properties = { - // channel: 0, - cc: 1, - value: 0 - }; - - this.addOutput("value", "number"); - } - - LGMIDICC.title = "MIDICC"; - LGMIDICC.desc = "gets a Controller Change"; - LGMIDICC.color = MIDI_COLOR; - - LGMIDICC.prototype.onExecute = function() { - var props = this.properties; - if (MIDIInterface.input) { - this.properties.value = - MIDIInterface.input.state.cc[this.properties.cc]; - } - this.setOutputData(0, this.properties.value); - }; - - LiteGraph.registerNodeType("midi/cc", LGMIDICC); - - function LGMIDIGenerator() { - this.addInput("generate", LiteGraph.ACTION); - this.addInput("scale", "string"); - this.addInput("octave", "number"); - this.addOutput("note", LiteGraph.EVENT); - this.properties = { - notes: "A,A#,B,C,C#,D,D#,E,F,F#,G,G#", - octave: 2, - duration: 0.5, - mode: "sequence" - }; - - this.notes_pitches = LGMIDIGenerator.processScale( - this.properties.notes - ); - this.sequence_index = 0; - } - - LGMIDIGenerator.title = "MIDI Generator"; - LGMIDIGenerator.desc = "Generates a random MIDI note"; - LGMIDIGenerator.color = MIDI_COLOR; - - LGMIDIGenerator.processScale = function(scale) { - var notes = scale.split(","); - for (var i = 0; i < notes.length; ++i) { - var n = notes[i]; - if ((n.length == 2 && n[1] != "#") || n.length > 2) { - notes[i] = -LiteGraph.MIDIEvent.NoteStringToPitch(n); - } else { - notes[i] = MIDIEvent.note_to_index[n] || 0; - } - } - return notes; - }; - - LGMIDIGenerator.prototype.onPropertyChanged = function(name, value) { - if (name == "notes") { - this.notes_pitches = LGMIDIGenerator.processScale(value); - } - }; - - LGMIDIGenerator.prototype.onExecute = function() { - var octave = this.getInputData(2); - if (octave != null) { - this.properties.octave = octave; - } - - var scale = this.getInputData(1); - if (scale) { - this.notes_pitches = LGMIDIGenerator.processScale(scale); - } - }; - - LGMIDIGenerator.prototype.onAction = function(event, midi_event) { - //var range = this.properties.max - this.properties.min; - //var pitch = this.properties.min + ((Math.random() * range)|0); - var pitch = 0; - var range = this.notes_pitches.length; - var index = 0; - - if (this.properties.mode == "sequence") { - index = this.sequence_index = (this.sequence_index + 1) % range; - } else if (this.properties.mode == "random") { - index = Math.floor(Math.random() * range); - } - - var note = this.notes_pitches[index]; - if (note >= 0) { - pitch = note + (this.properties.octave - 1) * 12 + 33; - } else { - pitch = -note; - } - - var midi_event = new MIDIEvent(); - midi_event.setup([MIDIEvent.NOTEON, pitch, 10]); - var duration = this.properties.duration || 1; - this.trigger("note", midi_event); - - //noteoff - setTimeout( - function() { - var midi_event = new MIDIEvent(); - midi_event.setup([MIDIEvent.NOTEOFF, pitch, 0]); - this.trigger("note", midi_event); - }.bind(this), - duration * 1000 - ); - }; - - LiteGraph.registerNodeType("midi/generator", LGMIDIGenerator); - - function LGMIDITranspose() { - this.properties = { - amount: 0 - }; - this.addInput("in", LiteGraph.ACTION); - this.addInput("amount", "number"); - this.addOutput("out", LiteGraph.EVENT); - - this.midi_event = new MIDIEvent(); - } - - LGMIDITranspose.title = "MIDI Transpose"; - LGMIDITranspose.desc = "Transpose a MIDI note"; - LGMIDITranspose.color = MIDI_COLOR; - - LGMIDITranspose.prototype.onAction = function(event, midi_event) { - if (!midi_event || midi_event.constructor !== MIDIEvent) { - return; - } - - if ( - midi_event.data[0] == MIDIEvent.NOTEON || - midi_event.data[0] == MIDIEvent.NOTEOFF - ) { - this.midi_event = new MIDIEvent(); - this.midi_event.setup(midi_event.data); - this.midi_event.data[1] = Math.round( - this.midi_event.data[1] + this.properties.amount - ); - this.trigger("out", this.midi_event); - } else { - this.trigger("out", midi_event); - } - }; - - LGMIDITranspose.prototype.onExecute = function() { - var amount = this.getInputData(1); - if (amount != null) { - this.properties.amount = amount; - } - }; - - LiteGraph.registerNodeType("midi/transpose", LGMIDITranspose); - - function LGMIDIQuantize() { - this.properties = { - scale: "A,A#,B,C,C#,D,D#,E,F,F#,G,G#" - }; - this.addInput("note", LiteGraph.ACTION); - this.addInput("scale", "string"); - this.addOutput("out", LiteGraph.EVENT); - - this.valid_notes = new Array(12); - this.offset_notes = new Array(12); - this.processScale(this.properties.scale); - } - - LGMIDIQuantize.title = "MIDI Quantize Pitch"; - LGMIDIQuantize.desc = "Transpose a MIDI note tp fit an scale"; - LGMIDIQuantize.color = MIDI_COLOR; - - LGMIDIQuantize.prototype.onPropertyChanged = function(name, value) { - if (name == "scale") { - this.processScale(value); - } - }; - - LGMIDIQuantize.prototype.processScale = function(scale) { - this._current_scale = scale; - this.notes_pitches = LGMIDIGenerator.processScale(scale); - for (var i = 0; i < 12; ++i) { - this.valid_notes[i] = this.notes_pitches.indexOf(i) != -1; - } - for (var i = 0; i < 12; ++i) { - if (this.valid_notes[i]) { - this.offset_notes[i] = 0; - continue; - } - for (var j = 1; j < 12; ++j) { - if (this.valid_notes[(i - j) % 12]) { - this.offset_notes[i] = -j; - break; - } - if (this.valid_notes[(i + j) % 12]) { - this.offset_notes[i] = j; - break; - } - } - } - }; - - LGMIDIQuantize.prototype.onAction = function(event, midi_event) { - if (!midi_event || midi_event.constructor !== MIDIEvent) { - return; - } - - if ( - midi_event.data[0] == MIDIEvent.NOTEON || - midi_event.data[0] == MIDIEvent.NOTEOFF - ) { - this.midi_event = new MIDIEvent(); - this.midi_event.setup(midi_event.data); - var note = midi_event.note; - var index = MIDIEvent.note_to_index[note]; - var offset = this.offset_notes[index]; - this.midi_event.data[1] += offset; - this.trigger("out", this.midi_event); - } else { - this.trigger("out", midi_event); - } - }; - - LGMIDIQuantize.prototype.onExecute = function() { - var scale = this.getInputData(1); - if (scale != null && scale != this._current_scale) { - this.processScale(scale); - } - }; - - LiteGraph.registerNodeType("midi/quantize", LGMIDIQuantize); - - function LGMIDIFromFile() { - this.properties = { - url: "", - autoplay: true - }; - - this.addInput("play", LiteGraph.ACTION); - this.addInput("pause", LiteGraph.ACTION); - this.addOutput("note", LiteGraph.EVENT); - this._midi = null; - this._current_time = 0; - this._playing = false; - - if (typeof MidiParser == "undefined") { - console.error( - "midi-parser.js not included, LGMidiPlay requires that library: https://raw.githubusercontent.com/colxi/midi-parser-js/master/src/main.js" - ); - this.boxcolor = "red"; - } - - } - - LGMIDIFromFile.title = "MIDI fromFile"; - LGMIDIFromFile.desc = "Plays a MIDI file"; - LGMIDIFromFile.color = MIDI_COLOR; - - LGMIDIFromFile.prototype.onAction = function( name ) - { - if(name == "play") - this.play(); - else if(name == "pause") - this._playing = !this._playing; - } - - LGMIDIFromFile.prototype.onPropertyChanged = function(name,value) - { - if(name == "url") - this.loadMIDIFile(value); - } - - LGMIDIFromFile.prototype.onExecute = function() { - if(!this._midi) - return; - - if(!this._playing) - return; - - this._current_time += this.graph.elapsed_time; - var current_time = this._current_time * 100; - - for(var i = 0; i < this._midi.tracks; ++i) - { - var track = this._midi.track[i]; - if(!track._last_pos) - { - track._last_pos = 0; - track._time = 0; - } - - var elem = track.event[ track._last_pos ]; - if(elem && (track._time + elem.deltaTime) <= current_time ) - { - track._last_pos++; - track._time += elem.deltaTime; - - if(elem.data) - { - var midi_cmd = elem.type << 4 + elem.channel; - var midi_event = new MIDIEvent(); - midi_event.setup([midi_cmd, elem.data[0], elem.data[1]]); - this.trigger("note", midi_event); - } - } - - } - }; - - LGMIDIFromFile.prototype.play = function() - { - this._playing = true; - this._current_time = 0; - if(!this._midi) - return; - - for(var i = 0; i < this._midi.tracks; ++i) - { - var track = this._midi.track[i]; - track._last_pos = 0; - track._time = 0; - } - } - - LGMIDIFromFile.prototype.loadMIDIFile = function(url) - { - var that = this; - LiteGraph.fetchFile( url, "arraybuffer", function(data) - { - that.boxcolor = "#AFA"; - that._midi = MidiParser.parse( new Uint8Array(data) ); - if(that.properties.autoplay) - that.play(); - }, function(err){ - that.boxcolor = "#FAA"; - that._midi = null; - }); - } - - LGMIDIFromFile.prototype.onDropFile = function(file) - { - this.properties.url = ""; - this.loadMIDIFile( file ); - } - - LiteGraph.registerNodeType("midi/fromFile", LGMIDIFromFile); - - - function LGMIDIPlay() { - this.properties = { - volume: 0.5, - duration: 1 - }; - this.addInput("note", LiteGraph.ACTION); - this.addInput("volume", "number"); - this.addInput("duration", "number"); - this.addOutput("note", LiteGraph.EVENT); - - if (typeof AudioSynth == "undefined") { - console.error( - "Audiosynth.js not included, LGMidiPlay requires that library" - ); - this.boxcolor = "red"; - } else { - var Synth = (this.synth = new AudioSynth()); - this.instrument = Synth.createInstrument("piano"); - } - } - - LGMIDIPlay.title = "MIDI Play"; - LGMIDIPlay.desc = "Plays a MIDI note"; - LGMIDIPlay.color = MIDI_COLOR; - - LGMIDIPlay.prototype.onAction = function(event, midi_event) { - if (!midi_event || midi_event.constructor !== MIDIEvent) { - return; - } - - if (this.instrument && midi_event.data[0] == MIDIEvent.NOTEON) { - var note = midi_event.note; //C# - if (!note || note == "undefined" || note.constructor !== String) { - return; - } - this.instrument.play( - note, - midi_event.octave, - this.properties.duration, - this.properties.volume - ); - } - this.trigger("note", midi_event); - }; - - LGMIDIPlay.prototype.onExecute = function() { - var volume = this.getInputData(1); - if (volume != null) { - this.properties.volume = volume; - } - - var duration = this.getInputData(2); - if (duration != null) { - this.properties.duration = duration; - } - }; - - LiteGraph.registerNodeType("midi/play", LGMIDIPlay); - - function LGMIDIKeys() { - this.properties = { - num_octaves: 2, - start_octave: 2 - }; - this.addInput("note", LiteGraph.ACTION); - this.addInput("reset", LiteGraph.ACTION); - this.addOutput("note", LiteGraph.EVENT); - this.size = [400, 100]; - this.keys = []; - this._last_key = -1; - } - - LGMIDIKeys.title = "MIDI Keys"; - LGMIDIKeys.desc = "Keyboard to play notes"; - LGMIDIKeys.color = MIDI_COLOR; - - LGMIDIKeys.keys = [ - { x: 0, w: 1, h: 1, t: 0 }, - { x: 0.75, w: 0.5, h: 0.6, t: 1 }, - { x: 1, w: 1, h: 1, t: 0 }, - { x: 1.75, w: 0.5, h: 0.6, t: 1 }, - { x: 2, w: 1, h: 1, t: 0 }, - { x: 2.75, w: 0.5, h: 0.6, t: 1 }, - { x: 3, w: 1, h: 1, t: 0 }, - { x: 4, w: 1, h: 1, t: 0 }, - { x: 4.75, w: 0.5, h: 0.6, t: 1 }, - { x: 5, w: 1, h: 1, t: 0 }, - { x: 5.75, w: 0.5, h: 0.6, t: 1 }, - { x: 6, w: 1, h: 1, t: 0 } - ]; - - LGMIDIKeys.prototype.onDrawForeground = function(ctx) { - if (this.flags.collapsed) { - return; - } - - var num_keys = this.properties.num_octaves * 12; - this.keys.length = num_keys; - var key_width = this.size[0] / (this.properties.num_octaves * 7); - var key_height = this.size[1]; - - ctx.globalAlpha = 1; - - for ( - var k = 0; - k < 2; - k++ //draw first whites (0) then blacks (1) - ) { - for (var i = 0; i < num_keys; ++i) { - var key_info = LGMIDIKeys.keys[i % 12]; - if (key_info.t != k) { - continue; - } - var octave = Math.floor(i / 12); - var x = octave * 7 * key_width + key_info.x * key_width; - if (k == 0) { - ctx.fillStyle = this.keys[i] ? "#CCC" : "white"; - } else { - ctx.fillStyle = this.keys[i] ? "#333" : "black"; - } - ctx.fillRect( - x + 1, - 0, - key_width * key_info.w - 2, - key_height * key_info.h - ); - } - } - }; - - LGMIDIKeys.prototype.getKeyIndex = function(pos) { - var num_keys = this.properties.num_octaves * 12; - var key_width = this.size[0] / (this.properties.num_octaves * 7); - var key_height = this.size[1]; - - for ( - var k = 1; - k >= 0; - k-- //test blacks first (1) then whites (0) - ) { - for (var i = 0; i < this.keys.length; ++i) { - var key_info = LGMIDIKeys.keys[i % 12]; - if (key_info.t != k) { - continue; - } - var octave = Math.floor(i / 12); - var x = octave * 7 * key_width + key_info.x * key_width; - var w = key_width * key_info.w; - var h = key_height * key_info.h; - if (pos[0] < x || pos[0] > x + w || pos[1] > h) { - continue; - } - return i; - } - } - return -1; - }; - - LGMIDIKeys.prototype.onAction = function(event, params) { - if (event == "reset") { - for (var i = 0; i < this.keys.length; ++i) { - this.keys[i] = false; - } - return; - } - - if (!params || params.constructor !== MIDIEvent) { - return; - } - var midi_event = params; - var start_note = (this.properties.start_octave - 1) * 12 + 29; - var index = midi_event.data[1] - start_note; - if (index >= 0 && index < this.keys.length) { - if (midi_event.data[0] == MIDIEvent.NOTEON) { - this.keys[index] = true; - } else if (midi_event.data[0] == MIDIEvent.NOTEOFF) { - this.keys[index] = false; - } - } - - this.trigger("note", midi_event); - }; - - LGMIDIKeys.prototype.onMouseDown = function(e, pos) { - if (pos[1] < 0) { - return; - } - var index = this.getKeyIndex(pos); - this.keys[index] = true; - this._last_key = index; - var pitch = (this.properties.start_octave - 1) * 12 + 29 + index; - var midi_event = new MIDIEvent(); - midi_event.setup([MIDIEvent.NOTEON, pitch, 100]); - this.trigger("note", midi_event); - return true; - }; - - LGMIDIKeys.prototype.onMouseMove = function(e, pos) { - if (pos[1] < 0 || this._last_key == -1) { - return; - } - this.setDirtyCanvas(true); - var index = this.getKeyIndex(pos); - if (this._last_key == index) { - return true; - } - this.keys[this._last_key] = false; - var pitch = - (this.properties.start_octave - 1) * 12 + 29 + this._last_key; - var midi_event = new MIDIEvent(); - midi_event.setup([MIDIEvent.NOTEOFF, pitch, 100]); - this.trigger("note", midi_event); - - this.keys[index] = true; - var pitch = (this.properties.start_octave - 1) * 12 + 29 + index; - var midi_event = new MIDIEvent(); - midi_event.setup([MIDIEvent.NOTEON, pitch, 100]); - this.trigger("note", midi_event); - - this._last_key = index; - return true; - }; - - LGMIDIKeys.prototype.onMouseUp = function(e, pos) { - if (pos[1] < 0) { - return; - } - var index = this.getKeyIndex(pos); - this.keys[index] = false; - this._last_key = -1; - var pitch = (this.properties.start_octave - 1) * 12 + 29 + index; - var midi_event = new MIDIEvent(); - midi_event.setup([MIDIEvent.NOTEOFF, pitch, 100]); - this.trigger("note", midi_event); - return true; - }; - - LiteGraph.registerNodeType("midi/keys", LGMIDIKeys); - - function now() { - return window.performance.now(); - } -})(this); - -(function(global) { - var LiteGraph = global.LiteGraph; - - var LGAudio = {}; - global.LGAudio = LGAudio; - - LGAudio.getAudioContext = function() { - if (!this._audio_context) { - window.AudioContext = - window.AudioContext || window.webkitAudioContext; - if (!window.AudioContext) { - console.error("AudioContext not supported by browser"); - return null; - } - this._audio_context = new AudioContext(); - this._audio_context.onmessage = function(msg) { - console.log("msg", msg); - }; - this._audio_context.onended = function(msg) { - console.log("ended", msg); - }; - this._audio_context.oncomplete = function(msg) { - console.log("complete", msg); - }; - } - - //in case it crashes - //if(this._audio_context.state == "suspended") - // this._audio_context.resume(); - return this._audio_context; - }; - - LGAudio.connect = function(audionodeA, audionodeB) { - try { - audionodeA.connect(audionodeB); - } catch (err) { - console.warn("LGraphAudio:", err); - } - }; - - LGAudio.disconnect = function(audionodeA, audionodeB) { - try { - audionodeA.disconnect(audionodeB); - } catch (err) { - console.warn("LGraphAudio:", err); - } - }; - - LGAudio.changeAllAudiosConnections = function(node, connect) { - if (node.inputs) { - for (var i = 0; i < node.inputs.length; ++i) { - var input = node.inputs[i]; - var link_info = node.graph.links[input.link]; - if (!link_info) { - continue; - } - - var origin_node = node.graph.getNodeById(link_info.origin_id); - var origin_audionode = null; - if (origin_node.getAudioNodeInOutputSlot) { - origin_audionode = origin_node.getAudioNodeInOutputSlot( - link_info.origin_slot - ); - } else { - origin_audionode = origin_node.audionode; - } - - var target_audionode = null; - if (node.getAudioNodeInInputSlot) { - target_audionode = node.getAudioNodeInInputSlot(i); - } else { - target_audionode = node.audionode; - } - - if (connect) { - LGAudio.connect(origin_audionode, target_audionode); - } else { - LGAudio.disconnect(origin_audionode, target_audionode); - } - } - } - - if (node.outputs) { - for (var i = 0; i < node.outputs.length; ++i) { - var output = node.outputs[i]; - for (var j = 0; j < output.links.length; ++j) { - var link_info = node.graph.links[output.links[j]]; - if (!link_info) { - continue; - } - - var origin_audionode = null; - if (node.getAudioNodeInOutputSlot) { - origin_audionode = node.getAudioNodeInOutputSlot(i); - } else { - origin_audionode = node.audionode; - } - - var target_node = node.graph.getNodeById( - link_info.target_id - ); - var target_audionode = null; - if (target_node.getAudioNodeInInputSlot) { - target_audionode = target_node.getAudioNodeInInputSlot( - link_info.target_slot - ); - } else { - target_audionode = target_node.audionode; - } - - if (connect) { - LGAudio.connect(origin_audionode, target_audionode); - } else { - LGAudio.disconnect(origin_audionode, target_audionode); - } - } - } - } - }; - - //used by many nodes - LGAudio.onConnectionsChange = function( - connection, - slot, - connected, - link_info - ) { - //only process the outputs events - if (connection != LiteGraph.OUTPUT) { - return; - } - - var target_node = null; - if (link_info) { - target_node = this.graph.getNodeById(link_info.target_id); - } - - if (!target_node) { - return; - } - - //get origin audionode - var local_audionode = null; - if (this.getAudioNodeInOutputSlot) { - local_audionode = this.getAudioNodeInOutputSlot(slot); - } else { - local_audionode = this.audionode; - } - - //get target audionode - var target_audionode = null; - if (target_node.getAudioNodeInInputSlot) { - target_audionode = target_node.getAudioNodeInInputSlot( - link_info.target_slot - ); - } else { - target_audionode = target_node.audionode; - } - - //do the connection/disconnection - if (connected) { - LGAudio.connect(local_audionode, target_audionode); - } else { - LGAudio.disconnect(local_audionode, target_audionode); - } - }; - - //this function helps creating wrappers to existing classes - LGAudio.createAudioNodeWrapper = function(class_object) { - var old_func = class_object.prototype.onPropertyChanged; - - class_object.prototype.onPropertyChanged = function(name, value) { - if (old_func) { - old_func.call(this, name, value); - } - - if (!this.audionode) { - return; - } - - if (this.audionode[name] === undefined) { - return; - } - - if (this.audionode[name].value !== undefined) { - this.audionode[name].value = value; - } else { - this.audionode[name] = value; - } - }; - - class_object.prototype.onConnectionsChange = - LGAudio.onConnectionsChange; - }; - - //contains the samples decoded of the loaded audios in AudioBuffer format - LGAudio.cached_audios = {}; - - LGAudio.loadSound = function(url, on_complete, on_error) { - if (LGAudio.cached_audios[url] && url.indexOf("blob:") == -1) { - if (on_complete) { - on_complete(LGAudio.cached_audios[url]); - } - return; - } - - if (LGAudio.onProcessAudioURL) { - url = LGAudio.onProcessAudioURL(url); - } - - //load new sample - var request = new XMLHttpRequest(); - request.open("GET", url, true); - request.responseType = "arraybuffer"; - - var context = LGAudio.getAudioContext(); - - // Decode asynchronously - request.onload = function() { - console.log("AudioSource loaded"); - context.decodeAudioData( - request.response, - function(buffer) { - console.log("AudioSource decoded"); - LGAudio.cached_audios[url] = buffer; - if (on_complete) { - on_complete(buffer); - } - }, - onError - ); - }; - request.send(); - - function onError(err) { - console.log("Audio loading sample error:", err); - if (on_error) { - on_error(err); - } - } - - return request; - }; - - //**************************************************** - - function LGAudioSource() { - this.properties = { - src: "", - gain: 0.5, - loop: true, - autoplay: true, - playbackRate: 1 - }; - - this._loading_audio = false; - this._audiobuffer = null; //points to AudioBuffer with the audio samples decoded - this._audionodes = []; - this._last_sourcenode = null; //the last AudioBufferSourceNode (there could be more if there are several sounds playing) - - this.addOutput("out", "audio"); - this.addInput("gain", "number"); - - //init context - var context = LGAudio.getAudioContext(); - - //create gain node to control volume - this.audionode = context.createGain(); - this.audionode.graphnode = this; - this.audionode.gain.value = this.properties.gain; - - //debug - if (this.properties.src) { - this.loadSound(this.properties.src); - } - } - - LGAudioSource.desc = "Plays an audio file"; - LGAudioSource["@src"] = { widget: "resource" }; - LGAudioSource.supported_extensions = ["wav", "ogg", "mp3"]; - - LGAudioSource.prototype.onAdded = function(graph) { - if (graph.status === LGraph.STATUS_RUNNING) { - this.onStart(); - } - }; - - LGAudioSource.prototype.onStart = function() { - if (!this._audiobuffer) { - return; - } - - if (this.properties.autoplay) { - this.playBuffer(this._audiobuffer); - } - }; - - LGAudioSource.prototype.onStop = function() { - this.stopAllSounds(); - }; - - LGAudioSource.prototype.onPause = function() { - this.pauseAllSounds(); - }; - - LGAudioSource.prototype.onUnpause = function() { - this.unpauseAllSounds(); - //this.onStart(); - }; - - LGAudioSource.prototype.onRemoved = function() { - this.stopAllSounds(); - if (this._dropped_url) { - URL.revokeObjectURL(this._url); - } - }; - - LGAudioSource.prototype.stopAllSounds = function() { - //iterate and stop - for (var i = 0; i < this._audionodes.length; ++i) { - if (this._audionodes[i].started) { - this._audionodes[i].started = false; - this._audionodes[i].stop(); - } - //this._audionodes[i].disconnect( this.audionode ); - } - this._audionodes.length = 0; - }; - - LGAudioSource.prototype.pauseAllSounds = function() { - LGAudio.getAudioContext().suspend(); - }; - - LGAudioSource.prototype.unpauseAllSounds = function() { - LGAudio.getAudioContext().resume(); - }; - - LGAudioSource.prototype.onExecute = function() { - if (this.inputs) { - for (var i = 0; i < this.inputs.length; ++i) { - var input = this.inputs[i]; - if (input.link == null) { - continue; - } - var v = this.getInputData(i); - if (v === undefined) { - continue; - } - if (input.name == "gain") - this.audionode.gain.value = v; - else if (input.name == "src") { - this.setProperty("src",v); - } else if (input.name == "playbackRate") { - this.properties.playbackRate = v; - for (var j = 0; j < this._audionodes.length; ++j) { - this._audionodes[j].playbackRate.value = v; - } - } - } - } - - if (this.outputs) { - for (var i = 0; i < this.outputs.length; ++i) { - var output = this.outputs[i]; - if (output.name == "buffer" && this._audiobuffer) { - this.setOutputData(i, this._audiobuffer); - } - } - } - }; - - LGAudioSource.prototype.onAction = function(event) { - if (this._audiobuffer) { - if (event == "Play") { - this.playBuffer(this._audiobuffer); - } else if (event == "Stop") { - this.stopAllSounds(); - } - } - }; - - LGAudioSource.prototype.onPropertyChanged = function(name, value) { - if (name == "src") { - this.loadSound(value); - } else if (name == "gain") { - this.audionode.gain.value = value; - } else if (name == "playbackRate") { - for (var j = 0; j < this._audionodes.length; ++j) { - this._audionodes[j].playbackRate.value = value; - } - } - }; - - LGAudioSource.prototype.playBuffer = function(buffer) { - var that = this; - var context = LGAudio.getAudioContext(); - - //create a new audionode (this is mandatory, AudioAPI doesnt like to reuse old ones) - var audionode = context.createBufferSource(); //create a AudioBufferSourceNode - this._last_sourcenode = audionode; - audionode.graphnode = this; - audionode.buffer = buffer; - audionode.loop = this.properties.loop; - audionode.playbackRate.value = this.properties.playbackRate; - this._audionodes.push(audionode); - audionode.connect(this.audionode); //connect to gain - - this._audionodes.push(audionode); - - this.trigger("start"); - - audionode.onended = function() { - //console.log("ended!"); - that.trigger("ended"); - //remove - var index = that._audionodes.indexOf(audionode); - if (index != -1) { - that._audionodes.splice(index, 1); - } - }; - - if (!audionode.started) { - audionode.started = true; - audionode.start(); - } - return audionode; - }; - - LGAudioSource.prototype.loadSound = function(url) { - var that = this; - - //kill previous load - if (this._request) { - this._request.abort(); - this._request = null; - } - - this._audiobuffer = null; //points to the audiobuffer once the audio is loaded - this._loading_audio = false; - - if (!url) { - return; - } - - this._request = LGAudio.loadSound(url, inner); - - this._loading_audio = true; - this.boxcolor = "#AA4"; - - function inner(buffer) { - this.boxcolor = LiteGraph.NODE_DEFAULT_BOXCOLOR; - that._audiobuffer = buffer; - that._loading_audio = false; - //if is playing, then play it - if (that.graph && that.graph.status === LGraph.STATUS_RUNNING) { - that.onStart(); - } //this controls the autoplay already - } - }; - - //Helps connect/disconnect AudioNodes when new connections are made in the node - LGAudioSource.prototype.onConnectionsChange = LGAudio.onConnectionsChange; - - LGAudioSource.prototype.onGetInputs = function() { - return [ - ["playbackRate", "number"], - ["src","string"], - ["Play", LiteGraph.ACTION], - ["Stop", LiteGraph.ACTION] - ]; - }; - - LGAudioSource.prototype.onGetOutputs = function() { - return [["buffer", "audiobuffer"], ["start", LiteGraph.EVENT], ["ended", LiteGraph.EVENT]]; - }; - - LGAudioSource.prototype.onDropFile = function(file) { - if (this._dropped_url) { - URL.revokeObjectURL(this._dropped_url); - } - var url = URL.createObjectURL(file); - this.properties.src = url; - this.loadSound(url); - this._dropped_url = url; - }; - - LGAudioSource.title = "Source"; - LGAudioSource.desc = "Plays audio"; - LiteGraph.registerNodeType("audio/source", LGAudioSource); - - //**************************************************** - - function LGAudioMediaSource() { - this.properties = { - gain: 0.5 - }; - - this._audionodes = []; - this._media_stream = null; - - this.addOutput("out", "audio"); - this.addInput("gain", "number"); - - //create gain node to control volume - var context = LGAudio.getAudioContext(); - this.audionode = context.createGain(); - this.audionode.graphnode = this; - this.audionode.gain.value = this.properties.gain; - } - - LGAudioMediaSource.prototype.onAdded = function(graph) { - if (graph.status === LGraph.STATUS_RUNNING) { - this.onStart(); - } - }; - - LGAudioMediaSource.prototype.onStart = function() { - if (this._media_stream == null && !this._waiting_confirmation) { - this.openStream(); - } - }; - - LGAudioMediaSource.prototype.onStop = function() { - this.audionode.gain.value = 0; - }; - - LGAudioMediaSource.prototype.onPause = function() { - this.audionode.gain.value = 0; - }; - - LGAudioMediaSource.prototype.onUnpause = function() { - this.audionode.gain.value = this.properties.gain; - }; - - LGAudioMediaSource.prototype.onRemoved = function() { - this.audionode.gain.value = 0; - if (this.audiosource_node) { - this.audiosource_node.disconnect(this.audionode); - this.audiosource_node = null; - } - if (this._media_stream) { - var tracks = this._media_stream.getTracks(); - if (tracks.length) { - tracks[0].stop(); - } - } - }; - - LGAudioMediaSource.prototype.openStream = function() { - if (!navigator.mediaDevices) { - 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.mediaDevices - .getUserMedia({ audio: true, video: false }) - .then(this.streamReady.bind(this)) - .catch(onFailSoHard); - - var that = this; - function onFailSoHard(err) { - console.log("Media rejected", err); - that._media_stream = false; - that.boxcolor = "red"; - } - }; - - LGAudioMediaSource.prototype.streamReady = function(localMediaStream) { - this._media_stream = localMediaStream; - //this._waiting_confirmation = false; - - //init context - if (this.audiosource_node) { - this.audiosource_node.disconnect(this.audionode); - } - var context = LGAudio.getAudioContext(); - this.audiosource_node = context.createMediaStreamSource( - localMediaStream - ); - this.audiosource_node.graphnode = this; - this.audiosource_node.connect(this.audionode); - this.boxcolor = "white"; - }; - - LGAudioMediaSource.prototype.onExecute = function() { - if (this._media_stream == null && !this._waiting_confirmation) { - this.openStream(); - } - - if (this.inputs) { - for (var i = 0; i < this.inputs.length; ++i) { - var input = this.inputs[i]; - if (input.link == null) { - continue; - } - var v = this.getInputData(i); - if (v === undefined) { - continue; - } - if (input.name == "gain") { - this.audionode.gain.value = this.properties.gain = v; - } - } - } - }; - - LGAudioMediaSource.prototype.onAction = function(event) { - if (event == "Play") { - this.audionode.gain.value = this.properties.gain; - } else if (event == "Stop") { - this.audionode.gain.value = 0; - } - }; - - LGAudioMediaSource.prototype.onPropertyChanged = function(name, value) { - if (name == "gain") { - this.audionode.gain.value = value; - } - }; - - //Helps connect/disconnect AudioNodes when new connections are made in the node - LGAudioMediaSource.prototype.onConnectionsChange = - LGAudio.onConnectionsChange; - - LGAudioMediaSource.prototype.onGetInputs = function() { - return [ - ["playbackRate", "number"], - ["Play", LiteGraph.ACTION], - ["Stop", LiteGraph.ACTION] - ]; - }; - - LGAudioMediaSource.title = "MediaSource"; - LGAudioMediaSource.desc = "Plays microphone"; - LiteGraph.registerNodeType("audio/media_source", LGAudioMediaSource); - - //***************************************************** - - function LGAudioAnalyser() { - this.properties = { - fftSize: 2048, - minDecibels: -100, - maxDecibels: -10, - smoothingTimeConstant: 0.5 - }; - - var context = LGAudio.getAudioContext(); - - this.audionode = context.createAnalyser(); - this.audionode.graphnode = this; - this.audionode.fftSize = this.properties.fftSize; - this.audionode.minDecibels = this.properties.minDecibels; - this.audionode.maxDecibels = this.properties.maxDecibels; - this.audionode.smoothingTimeConstant = this.properties.smoothingTimeConstant; - - this.addInput("in", "audio"); - this.addOutput("freqs", "array"); - this.addOutput("samples", "array"); - - this._freq_bin = null; - this._time_bin = null; - } - - LGAudioAnalyser.prototype.onPropertyChanged = function(name, value) { - this.audionode[name] = value; - }; - - LGAudioAnalyser.prototype.onExecute = function() { - if (this.isOutputConnected(0)) { - //send FFT - var bufferLength = this.audionode.frequencyBinCount; - if (!this._freq_bin || this._freq_bin.length != bufferLength) { - this._freq_bin = new Uint8Array(bufferLength); - } - this.audionode.getByteFrequencyData(this._freq_bin); - this.setOutputData(0, this._freq_bin); - } - - //send analyzer - if (this.isOutputConnected(1)) { - //send Samples - var bufferLength = this.audionode.frequencyBinCount; - if (!this._time_bin || this._time_bin.length != bufferLength) { - this._time_bin = new Uint8Array(bufferLength); - } - this.audionode.getByteTimeDomainData(this._time_bin); - this.setOutputData(1, this._time_bin); - } - - //properties - for (var i = 1; i < this.inputs.length; ++i) { - var input = this.inputs[i]; - if (input.link == null) { - continue; - } - var v = this.getInputData(i); - if (v !== undefined) { - this.audionode[input.name].value = v; - } - } - - //time domain - //this.audionode.getFloatTimeDomainData( dataArray ); - }; - - LGAudioAnalyser.prototype.onGetInputs = function() { - return [ - ["minDecibels", "number"], - ["maxDecibels", "number"], - ["smoothingTimeConstant", "number"] - ]; - }; - - LGAudioAnalyser.prototype.onGetOutputs = function() { - return [["freqs", "array"], ["samples", "array"]]; - }; - - LGAudioAnalyser.title = "Analyser"; - LGAudioAnalyser.desc = "Audio Analyser"; - LiteGraph.registerNodeType("audio/analyser", LGAudioAnalyser); - - //***************************************************** - - function LGAudioGain() { - //default - this.properties = { - gain: 1 - }; - - this.audionode = LGAudio.getAudioContext().createGain(); - this.addInput("in", "audio"); - this.addInput("gain", "number"); - this.addOutput("out", "audio"); - } - - LGAudioGain.prototype.onExecute = function() { - if (!this.inputs || !this.inputs.length) { - return; - } - - for (var i = 1; i < this.inputs.length; ++i) { - var input = this.inputs[i]; - var v = this.getInputData(i); - if (v !== undefined) { - this.audionode[input.name].value = v; - } - } - }; - - LGAudio.createAudioNodeWrapper(LGAudioGain); - - LGAudioGain.title = "Gain"; - LGAudioGain.desc = "Audio gain"; - LiteGraph.registerNodeType("audio/gain", LGAudioGain); - - function LGAudioConvolver() { - //default - this.properties = { - impulse_src: "", - normalize: true - }; - - this.audionode = LGAudio.getAudioContext().createConvolver(); - this.addInput("in", "audio"); - this.addOutput("out", "audio"); - } - - LGAudio.createAudioNodeWrapper(LGAudioConvolver); - - LGAudioConvolver.prototype.onRemove = function() { - if (this._dropped_url) { - URL.revokeObjectURL(this._dropped_url); - } - }; - - LGAudioConvolver.prototype.onPropertyChanged = function(name, value) { - if (name == "impulse_src") { - this.loadImpulse(value); - } else if (name == "normalize") { - this.audionode.normalize = value; - } - }; - - LGAudioConvolver.prototype.onDropFile = function(file) { - if (this._dropped_url) { - URL.revokeObjectURL(this._dropped_url); - } - this._dropped_url = URL.createObjectURL(file); - this.properties.impulse_src = this._dropped_url; - this.loadImpulse(this._dropped_url); - }; - - LGAudioConvolver.prototype.loadImpulse = function(url) { - var that = this; - - //kill previous load - if (this._request) { - this._request.abort(); - this._request = null; - } - - this._impulse_buffer = null; - this._loading_impulse = false; - - if (!url) { - return; - } - - //load new sample - this._request = LGAudio.loadSound(url, inner); - this._loading_impulse = true; - - // Decode asynchronously - function inner(buffer) { - that._impulse_buffer = buffer; - that.audionode.buffer = buffer; - console.log("Impulse signal set"); - that._loading_impulse = false; - } - }; - - LGAudioConvolver.title = "Convolver"; - LGAudioConvolver.desc = "Convolves the signal (used for reverb)"; - LiteGraph.registerNodeType("audio/convolver", LGAudioConvolver); - - function LGAudioDynamicsCompressor() { - //default - this.properties = { - threshold: -50, - knee: 40, - ratio: 12, - reduction: -20, - attack: 0, - release: 0.25 - }; - - this.audionode = LGAudio.getAudioContext().createDynamicsCompressor(); - this.addInput("in", "audio"); - this.addOutput("out", "audio"); - } - - LGAudio.createAudioNodeWrapper(LGAudioDynamicsCompressor); - - LGAudioDynamicsCompressor.prototype.onExecute = function() { - if (!this.inputs || !this.inputs.length) { - return; - } - for (var i = 1; i < this.inputs.length; ++i) { - var input = this.inputs[i]; - if (input.link == null) { - continue; - } - var v = this.getInputData(i); - if (v !== undefined) { - this.audionode[input.name].value = v; - } - } - }; - - LGAudioDynamicsCompressor.prototype.onGetInputs = function() { - return [ - ["threshold", "number"], - ["knee", "number"], - ["ratio", "number"], - ["reduction", "number"], - ["attack", "number"], - ["release", "number"] - ]; - }; - - LGAudioDynamicsCompressor.title = "DynamicsCompressor"; - LGAudioDynamicsCompressor.desc = "Dynamics Compressor"; - LiteGraph.registerNodeType( - "audio/dynamicsCompressor", - LGAudioDynamicsCompressor - ); - - function LGAudioWaveShaper() { - //default - this.properties = {}; - - this.audionode = LGAudio.getAudioContext().createWaveShaper(); - this.addInput("in", "audio"); - this.addInput("shape", "waveshape"); - this.addOutput("out", "audio"); - } - - LGAudioWaveShaper.prototype.onExecute = function() { - if (!this.inputs || !this.inputs.length) { - return; - } - var v = this.getInputData(1); - if (v === undefined) { - return; - } - this.audionode.curve = v; - }; - - LGAudioWaveShaper.prototype.setWaveShape = function(shape) { - this.audionode.curve = shape; - }; - - LGAudio.createAudioNodeWrapper(LGAudioWaveShaper); - - /* disabled till I dont find a way to do a wave shape -LGAudioWaveShaper.title = "WaveShaper"; -LGAudioWaveShaper.desc = "Distortion using wave shape"; -LiteGraph.registerNodeType("audio/waveShaper", LGAudioWaveShaper); -*/ - - function LGAudioMixer() { - //default - this.properties = { - gain1: 0.5, - gain2: 0.5 - }; - - this.audionode = LGAudio.getAudioContext().createGain(); - - this.audionode1 = LGAudio.getAudioContext().createGain(); - this.audionode1.gain.value = this.properties.gain1; - this.audionode2 = LGAudio.getAudioContext().createGain(); - this.audionode2.gain.value = this.properties.gain2; - - this.audionode1.connect(this.audionode); - this.audionode2.connect(this.audionode); - - this.addInput("in1", "audio"); - this.addInput("in1 gain", "number"); - this.addInput("in2", "audio"); - this.addInput("in2 gain", "number"); - - this.addOutput("out", "audio"); - } - - LGAudioMixer.prototype.getAudioNodeInInputSlot = function(slot) { - if (slot == 0) { - return this.audionode1; - } else if (slot == 2) { - return this.audionode2; - } - }; - - LGAudioMixer.prototype.onPropertyChanged = function(name, value) { - if (name == "gain1") { - this.audionode1.gain.value = value; - } else if (name == "gain2") { - this.audionode2.gain.value = value; - } - }; - - LGAudioMixer.prototype.onExecute = function() { - if (!this.inputs || !this.inputs.length) { - return; - } - - for (var i = 1; i < this.inputs.length; ++i) { - var input = this.inputs[i]; - - if (input.link == null || input.type == "audio") { - continue; - } - - var v = this.getInputData(i); - if (v === undefined) { - continue; - } - - if (i == 1) { - this.audionode1.gain.value = v; - } else if (i == 3) { - this.audionode2.gain.value = v; - } - } - }; - - LGAudio.createAudioNodeWrapper(LGAudioMixer); - - LGAudioMixer.title = "Mixer"; - LGAudioMixer.desc = "Audio mixer"; - LiteGraph.registerNodeType("audio/mixer", LGAudioMixer); - - function LGAudioADSR() { - //default - this.properties = { - A: 0.1, - D: 0.1, - S: 0.1, - R: 0.1 - }; - - this.audionode = LGAudio.getAudioContext().createGain(); - this.audionode.gain.value = 0; - this.addInput("in", "audio"); - this.addInput("gate", "boolean"); - this.addOutput("out", "audio"); - this.gate = false; - } - - LGAudioADSR.prototype.onExecute = function() { - var audioContext = LGAudio.getAudioContext(); - var now = audioContext.currentTime; - var node = this.audionode; - var gain = node.gain; - var current_gate = this.getInputData(1); - - var A = this.getInputOrProperty("A"); - var D = this.getInputOrProperty("D"); - var S = this.getInputOrProperty("S"); - var R = this.getInputOrProperty("R"); - - if (!this.gate && current_gate) { - gain.cancelScheduledValues(0); - gain.setValueAtTime(0, now); - gain.linearRampToValueAtTime(1, now + A); - gain.linearRampToValueAtTime(S, now + A + D); - } else if (this.gate && !current_gate) { - gain.cancelScheduledValues(0); - gain.setValueAtTime(gain.value, now); - gain.linearRampToValueAtTime(0, now + R); - } - - this.gate = current_gate; - }; - - LGAudioADSR.prototype.onGetInputs = function() { - return [ - ["A", "number"], - ["D", "number"], - ["S", "number"], - ["R", "number"] - ]; - }; - - LGAudio.createAudioNodeWrapper(LGAudioADSR); - - LGAudioADSR.title = "ADSR"; - LGAudioADSR.desc = "Audio envelope"; - LiteGraph.registerNodeType("audio/adsr", LGAudioADSR); - - function LGAudioDelay() { - //default - this.properties = { - delayTime: 0.5 - }; - - this.audionode = LGAudio.getAudioContext().createDelay(10); - this.audionode.delayTime.value = this.properties.delayTime; - this.addInput("in", "audio"); - this.addInput("time", "number"); - this.addOutput("out", "audio"); - } - - LGAudio.createAudioNodeWrapper(LGAudioDelay); - - LGAudioDelay.prototype.onExecute = function() { - var v = this.getInputData(1); - if (v !== undefined) { - this.audionode.delayTime.value = v; - } - }; - - LGAudioDelay.title = "Delay"; - LGAudioDelay.desc = "Audio delay"; - LiteGraph.registerNodeType("audio/delay", LGAudioDelay); - - function LGAudioBiquadFilter() { - //default - this.properties = { - frequency: 350, - detune: 0, - Q: 1 - }; - this.addProperty("type", "lowpass", "enum", { - values: [ - "lowpass", - "highpass", - "bandpass", - "lowshelf", - "highshelf", - "peaking", - "notch", - "allpass" - ] - }); - - //create node - this.audionode = LGAudio.getAudioContext().createBiquadFilter(); - - //slots - this.addInput("in", "audio"); - this.addOutput("out", "audio"); - } - - LGAudioBiquadFilter.prototype.onExecute = function() { - if (!this.inputs || !this.inputs.length) { - return; - } - - for (var i = 1; i < this.inputs.length; ++i) { - var input = this.inputs[i]; - if (input.link == null) { - continue; - } - var v = this.getInputData(i); - if (v !== undefined) { - this.audionode[input.name].value = v; - } - } - }; - - LGAudioBiquadFilter.prototype.onGetInputs = function() { - return [["frequency", "number"], ["detune", "number"], ["Q", "number"]]; - }; - - LGAudio.createAudioNodeWrapper(LGAudioBiquadFilter); - - LGAudioBiquadFilter.title = "BiquadFilter"; - LGAudioBiquadFilter.desc = "Audio filter"; - LiteGraph.registerNodeType("audio/biquadfilter", LGAudioBiquadFilter); - - function LGAudioOscillatorNode() { - //default - this.properties = { - frequency: 440, - detune: 0, - type: "sine" - }; - this.addProperty("type", "sine", "enum", { - values: ["sine", "square", "sawtooth", "triangle", "custom"] - }); - - //create node - this.audionode = LGAudio.getAudioContext().createOscillator(); - - //slots - this.addOutput("out", "audio"); - } - - LGAudioOscillatorNode.prototype.onStart = function() { - if (!this.audionode.started) { - this.audionode.started = true; - try { - this.audionode.start(); - } catch (err) {} - } - }; - - LGAudioOscillatorNode.prototype.onStop = function() { - if (this.audionode.started) { - this.audionode.started = false; - this.audionode.stop(); - } - }; - - LGAudioOscillatorNode.prototype.onPause = function() { - this.onStop(); - }; - - LGAudioOscillatorNode.prototype.onUnpause = function() { - this.onStart(); - }; - - LGAudioOscillatorNode.prototype.onExecute = function() { - if (!this.inputs || !this.inputs.length) { - return; - } - - for (var i = 0; i < this.inputs.length; ++i) { - var input = this.inputs[i]; - if (input.link == null) { - continue; - } - var v = this.getInputData(i); - if (v !== undefined) { - this.audionode[input.name].value = v; - } - } - }; - - LGAudioOscillatorNode.prototype.onGetInputs = function() { - return [ - ["frequency", "number"], - ["detune", "number"], - ["type", "string"] - ]; - }; - - LGAudio.createAudioNodeWrapper(LGAudioOscillatorNode); - - LGAudioOscillatorNode.title = "Oscillator"; - LGAudioOscillatorNode.desc = "Oscillator"; - LiteGraph.registerNodeType("audio/oscillator", LGAudioOscillatorNode); - - //***************************************************** - - //EXTRA - - function LGAudioVisualization() { - this.properties = { - continuous: true, - mark: -1 - }; - - this.addInput("data", "array"); - this.addInput("mark", "number"); - this.size = [300, 200]; - this._last_buffer = null; - } - - LGAudioVisualization.prototype.onExecute = function() { - this._last_buffer = this.getInputData(0); - var v = this.getInputData(1); - if (v !== undefined) { - this.properties.mark = v; - } - this.setDirtyCanvas(true, false); - }; - - LGAudioVisualization.prototype.onDrawForeground = function(ctx) { - if (!this._last_buffer) { - return; - } - - var buffer = this._last_buffer; - - //delta represents how many samples we advance per pixel - var delta = buffer.length / this.size[0]; - var h = this.size[1]; - - ctx.fillStyle = "black"; - ctx.fillRect(0, 0, this.size[0], this.size[1]); - ctx.strokeStyle = "white"; - ctx.beginPath(); - var x = 0; - - if (this.properties.continuous) { - ctx.moveTo(x, h); - for (var i = 0; i < buffer.length; i += delta) { - ctx.lineTo(x, h - (buffer[i | 0] / 255) * h); - x++; - } - } else { - for (var i = 0; i < buffer.length; i += delta) { - ctx.moveTo(x + 0.5, h); - ctx.lineTo(x + 0.5, h - (buffer[i | 0] / 255) * h); - x++; - } - } - ctx.stroke(); - - if (this.properties.mark >= 0) { - var samplerate = LGAudio.getAudioContext().sampleRate; - var binfreq = samplerate / buffer.length; - var x = (2 * (this.properties.mark / binfreq)) / delta; - if (x >= this.size[0]) { - x = this.size[0] - 1; - } - ctx.strokeStyle = "red"; - ctx.beginPath(); - ctx.moveTo(x, h); - ctx.lineTo(x, 0); - ctx.stroke(); - } - }; - - LGAudioVisualization.title = "Visualization"; - LGAudioVisualization.desc = "Audio Visualization"; - LiteGraph.registerNodeType("audio/visualization", LGAudioVisualization); - - function LGAudioBandSignal() { - //default - this.properties = { - band: 440, - amplitude: 1 - }; - - this.addInput("freqs", "array"); - this.addOutput("signal", "number"); - } - - LGAudioBandSignal.prototype.onExecute = function() { - this._freqs = this.getInputData(0); - if (!this._freqs) { - return; - } - - var band = this.properties.band; - var v = this.getInputData(1); - if (v !== undefined) { - band = v; - } - - var samplerate = LGAudio.getAudioContext().sampleRate; - var binfreq = samplerate / this._freqs.length; - var index = 2 * (band / binfreq); - var v = 0; - if (index < 0) { - v = this._freqs[0]; - } - if (index >= this._freqs.length) { - v = this._freqs[this._freqs.length - 1]; - } else { - var pos = index | 0; - var v0 = this._freqs[pos]; - var v1 = this._freqs[pos + 1]; - var f = index - pos; - v = v0 * (1 - f) + v1 * f; - } - - this.setOutputData(0, (v / 255) * this.properties.amplitude); - }; - - LGAudioBandSignal.prototype.onGetInputs = function() { - return [["band", "number"]]; - }; - - LGAudioBandSignal.title = "Signal"; - LGAudioBandSignal.desc = "extract the signal of some frequency"; - LiteGraph.registerNodeType("audio/signal", LGAudioBandSignal); - - function LGAudioScript() { - if (!LGAudioScript.default_code) { - var code = LGAudioScript.default_function.toString(); - var index = code.indexOf("{") + 1; - var index2 = code.lastIndexOf("}"); - LGAudioScript.default_code = code.substr(index, index2 - index); - } - - //default - this.properties = { - code: LGAudioScript.default_code - }; - - //create node - var ctx = LGAudio.getAudioContext(); - if (ctx.createScriptProcessor) { - this.audionode = ctx.createScriptProcessor(4096, 1, 1); - } - //buffer size, input channels, output channels - else { - console.warn("ScriptProcessorNode deprecated"); - this.audionode = ctx.createGain(); //bypass audio - } - - this.processCode(); - if (!LGAudioScript._bypass_function) { - LGAudioScript._bypass_function = this.audionode.onaudioprocess; - } - - //slots - this.addInput("in", "audio"); - this.addOutput("out", "audio"); - } - - LGAudioScript.prototype.onAdded = function(graph) { - if (graph.status == LGraph.STATUS_RUNNING) { - this.audionode.onaudioprocess = this._callback; - } - }; - - LGAudioScript["@code"] = { widget: "code", type: "code" }; - - LGAudioScript.prototype.onStart = function() { - this.audionode.onaudioprocess = this._callback; - }; - - LGAudioScript.prototype.onStop = function() { - this.audionode.onaudioprocess = LGAudioScript._bypass_function; - }; - - LGAudioScript.prototype.onPause = function() { - this.audionode.onaudioprocess = LGAudioScript._bypass_function; - }; - - LGAudioScript.prototype.onUnpause = function() { - this.audionode.onaudioprocess = this._callback; - }; - - LGAudioScript.prototype.onExecute = function() { - //nothing! because we need an onExecute to receive onStart... fix that - }; - - LGAudioScript.prototype.onRemoved = function() { - this.audionode.onaudioprocess = LGAudioScript._bypass_function; - }; - - LGAudioScript.prototype.processCode = function() { - try { - var func = new Function("properties", this.properties.code); - this._script = new func(this.properties); - this._old_code = this.properties.code; - this._callback = this._script.onaudioprocess; - } catch (err) { - console.error("Error in onaudioprocess code", err); - this._callback = LGAudioScript._bypass_function; - this.audionode.onaudioprocess = this._callback; - } - }; - - LGAudioScript.prototype.onPropertyChanged = function(name, value) { - if (name == "code") { - this.properties.code = value; - this.processCode(); - if (this.graph && this.graph.status == LGraph.STATUS_RUNNING) { - this.audionode.onaudioprocess = this._callback; - } - } - }; - - LGAudioScript.default_function = function() { - this.onaudioprocess = function(audioProcessingEvent) { - // The input buffer is the song we loaded earlier - var inputBuffer = audioProcessingEvent.inputBuffer; - - // The output buffer contains the samples that will be modified and played - var outputBuffer = audioProcessingEvent.outputBuffer; - - // Loop through the output channels (in this case there is only one) - for ( - var channel = 0; - channel < outputBuffer.numberOfChannels; - channel++ - ) { - var inputData = inputBuffer.getChannelData(channel); - var outputData = outputBuffer.getChannelData(channel); - - // Loop through the 4096 samples - for (var sample = 0; sample < inputBuffer.length; sample++) { - // make output equal to the same as the input - outputData[sample] = inputData[sample]; - } - } - }; - }; - - LGAudio.createAudioNodeWrapper(LGAudioScript); - - LGAudioScript.title = "Script"; - LGAudioScript.desc = "apply script to signal"; - LiteGraph.registerNodeType("audio/script", LGAudioScript); - - function LGAudioDestination() { - this.audionode = LGAudio.getAudioContext().destination; - this.addInput("in", "audio"); - } - - LGAudioDestination.title = "Destination"; - LGAudioDestination.desc = "Audio output"; - LiteGraph.registerNodeType("audio/destination", LGAudioDestination); -})(this); - -//event related nodes -(function(global) { - var LiteGraph = global.LiteGraph; - - function LGWebSocket() { - this.size = [60, 20]; - this.addInput("send", LiteGraph.ACTION); - this.addOutput("received", LiteGraph.EVENT); - this.addInput("in", 0); - this.addOutput("out", 0); - this.properties = { - url: "", - room: "lgraph", //allows to filter messages, - only_send_changes: true - }; - this._ws = null; - this._last_sent_data = []; - this._last_received_data = []; - } - - LGWebSocket.title = "WebSocket"; - LGWebSocket.desc = "Send data through a websocket"; - - LGWebSocket.prototype.onPropertyChanged = function(name, value) { - if (name == "url") { - this.connectSocket(); - } - }; - - LGWebSocket.prototype.onExecute = function() { - if (!this._ws && this.properties.url) { - this.connectSocket(); - } - - if (!this._ws || this._ws.readyState != WebSocket.OPEN) { - return; - } - - var room = this.properties.room; - var only_changes = this.properties.only_send_changes; - - for (var i = 1; i < this.inputs.length; ++i) { - var data = this.getInputData(i); - if (data == null) { - continue; - } - var json; - try { - json = JSON.stringify({ - type: 0, - room: room, - channel: i, - data: data - }); - } catch (err) { - continue; - } - if (only_changes && this._last_sent_data[i] == json) { - continue; - } - - this._last_sent_data[i] = json; - this._ws.send(json); - } - - for (var i = 1; i < this.outputs.length; ++i) { - this.setOutputData(i, this._last_received_data[i]); - } - - if (this.boxcolor == "#AFA") { - this.boxcolor = "#6C6"; - } - }; - - LGWebSocket.prototype.connectSocket = function() { - var that = this; - var url = this.properties.url; - if (url.substr(0, 2) != "ws") { - url = "ws://" + url; - } - this._ws = new WebSocket(url); - this._ws.onopen = function() { - console.log("ready"); - that.boxcolor = "#6C6"; - }; - this._ws.onmessage = function(e) { - that.boxcolor = "#AFA"; - var data = JSON.parse(e.data); - if (data.room && data.room != that.properties.room) { - return; - } - if (data.type == 1) { - if ( - data.data.object_class && - LiteGraph[data.data.object_class] - ) { - var obj = null; - try { - obj = new LiteGraph[data.data.object_class](data.data); - that.triggerSlot(0, obj); - } catch (err) { - return; - } - } else { - that.triggerSlot(0, data.data); - } - } else { - that._last_received_data[data.channel || 0] = data.data; - } - }; - this._ws.onerror = function(e) { - console.log("couldnt connect to websocket"); - that.boxcolor = "#E88"; - }; - this._ws.onclose = function(e) { - console.log("connection closed"); - that.boxcolor = "#000"; - }; - }; - - LGWebSocket.prototype.send = function(data) { - if (!this._ws || this._ws.readyState != WebSocket.OPEN) { - return; - } - this._ws.send(JSON.stringify({ type: 1, msg: data })); - }; - - LGWebSocket.prototype.onAction = function(action, param) { - if (!this._ws || this._ws.readyState != WebSocket.OPEN) { - return; - } - this._ws.send({ - type: 1, - room: this.properties.room, - action: action, - data: param - }); - }; - - LGWebSocket.prototype.onGetInputs = function() { - return [["in", 0]]; - }; - - LGWebSocket.prototype.onGetOutputs = function() { - return [["out", 0]]; - }; - - LiteGraph.registerNodeType("network/websocket", LGWebSocket); - - //It is like a websocket but using the SillyServer.js server that bounces packets back to all clients connected: - //For more information: https://github.com/jagenjo/SillyServer.js - - function LGSillyClient() { - //this.size = [60,20]; - this.room_widget = this.addWidget( - "text", - "Room", - "lgraph", - this.setRoom.bind(this) - ); - this.addWidget( - "button", - "Reconnect", - null, - this.connectSocket.bind(this) - ); - - this.addInput("send", LiteGraph.ACTION); - this.addOutput("received", LiteGraph.EVENT); - this.addInput("in", 0); - this.addOutput("out", 0); - this.properties = { - url: "tamats.com:55000", - room: "lgraph", - only_send_changes: true - }; - - this._server = null; - this.connectSocket(); - this._last_sent_data = []; - this._last_received_data = []; - - if(typeof(SillyClient) == "undefined") - console.warn("remember to add SillyClient.js to your project: https://tamats.com/projects/sillyserver/src/sillyclient.js"); - } - - LGSillyClient.title = "SillyClient"; - LGSillyClient.desc = "Connects to SillyServer to broadcast messages"; - - LGSillyClient.prototype.onPropertyChanged = function(name, value) { - if (name == "room") { - this.room_widget.value = value; - } - this.connectSocket(); - }; - - LGSillyClient.prototype.setRoom = function(room_name) { - this.properties.room = room_name; - this.room_widget.value = room_name; - this.connectSocket(); - }; - - //force label names - LGSillyClient.prototype.onDrawForeground = function() { - for (var i = 1; i < this.inputs.length; ++i) { - var slot = this.inputs[i]; - slot.label = "in_" + i; - } - for (var i = 1; i < this.outputs.length; ++i) { - var slot = this.outputs[i]; - slot.label = "out_" + i; - } - }; - - LGSillyClient.prototype.onExecute = function() { - if (!this._server || !this._server.is_connected) { - return; - } - - var only_send_changes = this.properties.only_send_changes; - - for (var i = 1; i < this.inputs.length; ++i) { - var data = this.getInputData(i); - var prev_data = this._last_sent_data[i]; - if (data != null) { - if (only_send_changes) - { - var is_equal = true; - if( data && data.length && prev_data && prev_data.length == data.length && data.constructor !== String) - { - for(var j = 0; j < data.length; ++j) - if( prev_data[j] != data[j] ) - { - is_equal = false; - break; - } - } - else if(this._last_sent_data[i] != data) - is_equal = false; - if(is_equal) - continue; - } - this._server.sendMessage({ type: 0, channel: i, data: data }); - if( data.length && data.constructor !== String ) - { - if( this._last_sent_data[i] ) - { - this._last_sent_data[i].length = data.length; - for(var j = 0; j < data.length; ++j) - this._last_sent_data[i][j] = data[j]; - } - else //create - { - if(data.constructor === Array) - this._last_sent_data[i] = data.concat(); - else - this._last_sent_data[i] = new data.constructor( data ); - } - } - else - this._last_sent_data[i] = data; //should be cloned - } - } - - for (var i = 1; i < this.outputs.length; ++i) { - this.setOutputData(i, this._last_received_data[i]); - } - - if (this.boxcolor == "#AFA") { - this.boxcolor = "#6C6"; - } - }; - - LGSillyClient.prototype.connectSocket = function() { - var that = this; - if (typeof SillyClient == "undefined") { - if (!this._error) { - console.error( - "SillyClient node cannot be used, you must include SillyServer.js" - ); - } - this._error = true; - return; - } - - this._server = new SillyClient(); - this._server.on_ready = function() { - console.log("ready"); - that.boxcolor = "#6C6"; - }; - this._server.on_message = function(id, msg) { - var data = null; - try { - data = JSON.parse(msg); - } catch (err) { - return; - } - - if (data.type == 1) { - //EVENT slot - if ( - data.data.object_class && - LiteGraph[data.data.object_class] - ) { - var obj = null; - try { - obj = new LiteGraph[data.data.object_class](data.data); - that.triggerSlot(0, obj); - } catch (err) { - return; - } - } else { - that.triggerSlot(0, data.data); - } - } //for FLOW slots - else { - that._last_received_data[data.channel || 0] = data.data; - } - that.boxcolor = "#AFA"; - }; - this._server.on_error = function(e) { - console.log("couldnt connect to websocket"); - that.boxcolor = "#E88"; - }; - this._server.on_close = function(e) { - console.log("connection closed"); - that.boxcolor = "#000"; - }; - - if (this.properties.url && this.properties.room) { - try { - this._server.connect(this.properties.url, this.properties.room); - } catch (err) { - console.error("SillyServer error: " + err); - this._server = null; - return; - } - this._final_url = this.properties.url + "/" + this.properties.room; - } - }; - - LGSillyClient.prototype.send = function(data) { - if (!this._server || !this._server.is_connected) { - return; - } - this._server.sendMessage({ type: 1, data: data }); - }; - - LGSillyClient.prototype.onAction = function(action, param) { - if (!this._server || !this._server.is_connected) { - return; - } - this._server.sendMessage({ type: 1, action: action, data: param }); - }; - - LGSillyClient.prototype.onGetInputs = function() { - return [["in", 0]]; - }; - - LGSillyClient.prototype.onGetOutputs = function() { - return [["out", 0]]; - }; - - LiteGraph.registerNodeType("network/sillyclient", LGSillyClient); - -//HTTP Request -function HTTPRequestNode() { - var that = this; - this.addInput("request", LiteGraph.ACTION); - this.addInput("url", "string"); - this.addProperty("url", ""); - this.addOutput("ready", LiteGraph.EVENT); - this.addOutput("data", "string"); - this.addWidget("button", "Fetch", null, this.fetch.bind(this)); - this._data = null; - this._fetching = null; -} - -HTTPRequestNode.title = "HTTP Request"; -HTTPRequestNode.desc = "Fetch data through HTTP"; - -HTTPRequestNode.prototype.fetch = function() -{ - var url = this.properties.url; - if(!url) - return; - - this.boxcolor = "#FF0"; - var that = this; - this._fetching = fetch(url) - .then(resp=>{ - if(!resp.ok) - { - this.boxcolor = "#F00"; - that.trigger("error"); - } - else - { - this.boxcolor = "#0F0"; - return resp.text(); - } - }) - .then(data=>{ - that._data = data; - that._fetching = null; - that.trigger("ready"); - }); -} - -HTTPRequestNode.prototype.onAction = function(evt) -{ - if(evt == "request") - this.fetch(); -} - -HTTPRequestNode.prototype.onExecute = function() { - this.setOutputData(1, this._data); -}; - -HTTPRequestNode.prototype.onGetOutputs = function() { - return [["error",LiteGraph.EVENT]]; -} - -LiteGraph.registerNodeType("network/httprequest", HTTPRequestNode); - - - -})(this); diff --git a/build/litegraph.full.pack.js b/build/litegraph.full.pack.js new file mode 100644 index 000000000..2c7082e84 --- /dev/null +++ b/build/litegraph.full.pack.js @@ -0,0 +1,34 @@ +/*packed*/!function(global){var LiteGraph=global.LiteGraph={VERSION:.4,CANVAS_GRID_SIZE:10,NODE_TITLE_HEIGHT:30,NODE_TITLE_TEXT_Y:20,NODE_SLOT_HEIGHT:20,NODE_WIDGET_HEIGHT:20,NODE_WIDTH:140,NODE_MIN_WIDTH:50,NODE_COLLAPSED_RADIUS:10,NODE_COLLAPSED_WIDTH:80,NODE_TITLE_COLOR:"#999",NODE_SELECTED_TITLE_COLOR:"#FFF",NODE_TEXT_SIZE:14,NODE_TEXT_COLOR:"#AAA",NODE_SUBTEXT_SIZE:12,NODE_DEFAULT_COLOR:"#333",NODE_DEFAULT_BGCOLOR:"#353535",NODE_DEFAULT_BOXCOLOR:"#666",NODE_DEFAULT_SHAPE:"box",NODE_BOX_OUTLINE_COLOR:"#FFF",DEFAULT_SHADOW_COLOR:"rgba(0,0,0,0.5)",DEFAULT_GROUP_FONT:24,WIDGET_BGCOLOR:"#222",WIDGET_OUTLINE_COLOR:"#666",WIDGET_TEXT_COLOR:"#DDD",WIDGET_SECONDARY_TEXT_COLOR:"#999",LINK_COLOR:"#9A9",EVENT_LINK_COLOR:"#A86",CONNECTING_LINK_COLOR:"#AFA",MAX_NUMBER_OF_NODES:1e3,DEFAULT_POSITION:[100,100],VALID_SHAPES:["default","box","round","card"],BOX_SHAPE:1,ROUND_SHAPE:2,CIRCLE_SHAPE:3,CARD_SHAPE:4,ARROW_SHAPE:5,GRID_SHAPE:6,INPUT:1,OUTPUT:2,EVENT:-1,ACTION:-1,NODE_MODES:["Always","On Event","Never","On Trigger"],NODE_MODES_COLORS:["#666","#422","#333","#224","#626"],ALWAYS:0,ON_EVENT:1,NEVER:2,ON_TRIGGER:3,UP:1,DOWN:2,LEFT:3,RIGHT:4,CENTER:5,LINK_RENDER_MODES:["Straight","Linear","Spline"],STRAIGHT_LINK:0,LINEAR_LINK:1,SPLINE_LINK:2,NORMAL_TITLE:0,NO_TITLE:1,TRANSPARENT_TITLE:2,AUTOHIDE_TITLE:3,VERTICAL_LAYOUT:"vertical",proxy:null,node_images_path:"",debug:!1,catch_exceptions:!0,throw_errors:!0,allow_scripts:!1,use_deferred_actions:!0,registered_node_types:{},node_types_by_file_extension:{},Nodes:{},Globals:{},searchbox_extras:{},auto_sort_node_types:!1,node_box_coloured_when_on:!1,node_box_coloured_by_mode:!1,dialog_close_on_mouse_leave:!0,dialog_close_on_mouse_leave_delay:500,shift_click_do_break_link_from:!1,click_do_break_link_to:!1,search_hide_on_mouse_leave:!0,search_filter_enabled:!1,search_show_all_on_open:!0,auto_load_slot_types:!1,registered_slot_in_types:{},registered_slot_out_types:{},slot_types_in:[],slot_types_out:[],slot_types_default_in:[],slot_types_default_out:[],alt_drag_do_clone_nodes:!1,do_add_triggers_slots:!1,allow_multi_output_for_events:!0,middle_click_slot_add_default_node:!1,release_link_on_empty_shows_menu:!1,pointerevents_method:"mouse",ctrl_shift_v_paste_connect_unselected_outputs:!1,use_uuids:!1,registerNodeType:function(type,base_class){if(!base_class.prototype)throw"Cannot register a simple object, it must be a class with a prototype";base_class.type=type,LiteGraph.debug&&console.log("Node registered: "+type);var i,classname=base_class.name,pos=type.lastIndexOf("/");for(i in base_class.category=type.substring(0,pos),base_class.title||(base_class.title=classname),LGraphNode.prototype)base_class.prototype[i]||(base_class.prototype[i]=LGraphNode.prototype[i]);pos=this.registered_node_types[type];if(pos&&console.log("replacing node type: "+type),!Object.prototype.hasOwnProperty.call(base_class.prototype,"shape")&&(Object.defineProperty(base_class.prototype,"shape",{set:function(v){switch(v){case"default":delete this._shape;break;case"box":this._shape=LiteGraph.BOX_SHAPE;break;case"round":this._shape=LiteGraph.ROUND_SHAPE;break;case"circle":this._shape=LiteGraph.CIRCLE_SHAPE;break;case"card":this._shape=LiteGraph.CARD_SHAPE;break;default:this._shape=v}},get:function(){return this._shape},enumerable:!0,configurable:!0}),base_class.supported_extensions))for(let i in base_class.supported_extensions){var ext=base_class.supported_extensions[i];ext&&ext.constructor===String&&(this.node_types_by_file_extension[ext.toLowerCase()]=base_class)}(this.registered_node_types[type]=base_class).constructor.name&&(this.Nodes[classname]=base_class),LiteGraph.onNodeTypeRegistered&&LiteGraph.onNodeTypeRegistered(type,base_class),pos&&LiteGraph.onNodeTypeReplaced&&LiteGraph.onNodeTypeReplaced(type,base_class,pos),base_class.prototype.onPropertyChange&&console.warn("LiteGraph node class "+type+" has onPropertyChange method, it must be called onPropertyChanged with d at the end"),this.auto_load_slot_types&&new base_class(base_class.title||"tmpnode")},unregisterNodeType:function(type){var base_class=type.constructor===String?this.registered_node_types[type]:type;if(!base_class)throw"node type not found: "+type;delete this.registered_node_types[base_class.type],base_class.constructor.name&&delete this.Nodes[base_class.constructor.name]},registerNodeAndSlotType:function(type,slot_type,out){out=out||!1;var class_type=(type.constructor===String&&"anonymous"!==this.registered_node_types[type]?this.registered_node_types[type]:type).constructor.type;let allTypes=[];allTypes="string"==typeof slot_type?slot_type.split(","):slot_type==this.EVENT||slot_type==this.ACTION?["_event_"]:["*"];for(let i=0;i(a^16*Math.random()>>a/4).toString(16))},isValidConnection:function(type_a,type_b){if(""!=type_b&&"*"!==type_b||(type_b=0),!(type_a=""!=type_a&&"*"!==type_a?type_a:0)||!type_b||type_a==type_b||type_a==LiteGraph.EVENT&&type_b==LiteGraph.ACTION)return!0;if(type_a=String(type_a),type_b=String(type_b),type_a=type_a.toLowerCase(),type_b=type_b.toLowerCase(),-1==type_a.indexOf(",")&&-1==type_b.indexOf(","))return type_a==type_b;for(var supported_types_a=type_a.split(","),supported_types_b=type_b.split(","),i=0;imax_size&&(max_size=node.size[max_size_index]),layout==LiteGraph.VERTICAL_LAYOUT?0:1);y+=node.size[max_size_index]+margin+LiteGraph.NODE_TITLE_HEIGHT}x+=max_size+margin}}this.setDirtyCanvas(!0,!0)},LGraph.prototype.getTime=function(){return this.globaltime},LGraph.prototype.getFixedTime=function(){return this.fixedtime},LGraph.prototype.getElapsedTime=function(){return this.elapsed_time},LGraph.prototype.sendEventToAllNodes=function(eventname,params,mode){mode=mode||LiteGraph.ALWAYS;var nodes=this._nodes_in_order||this._nodes;if(nodes)for(var j=0,l=nodes.length;j=LiteGraph.MAX_NUMBER_OF_NODES)throw"LiteGraph: max number of nodes in a graph reached";return LiteGraph.use_uuids?null!=node.id&&-1!=node.id||(node.id=LiteGraph.uuidv4()):null==node.id||-1==node.id?node.id=++this.last_node_id:this.last_node_id=this.outputs.length)){var output_info=this.outputs[slot];if(output_info&&(output_info._data=data,this.outputs[slot].links))for(var i=0;i=this.outputs.length)){var output_info=this.outputs[slot];if(output_info&&(output_info.type=type,this.outputs[slot].links))for(var i=0;i=this.inputs.length||null==this.inputs[slot].link))return slot=this.inputs[slot].link,(slot=this.graph.links[slot])?(force_update&&(force_update=this.graph.getNodeById(slot.origin_id))&&(force_update.updateOutputData?force_update.updateOutputData(slot.origin_slot):force_update.onExecute&&force_update.onExecute()),slot.data):null},LGraphNode.prototype.getInputDataType=function(slot){var node;return this.inputs&&!(slot>=this.inputs.length||null==this.inputs[slot].link)&&(slot=this.inputs[slot].link,slot=this.graph.links[slot])?(node=this.graph.getNodeById(slot.origin_id))?(node=node.outputs[slot.origin_slot])?node.type:null:slot.type:null},LGraphNode.prototype.getInputDataByName=function(slot_name,force_update){slot_name=this.findInputSlot(slot_name);return-1==slot_name?null:this.getInputData(slot_name,force_update)},LGraphNode.prototype.isInputConnected=function(slot){return!!this.inputs&&slot=this.inputs.length)&&(slot=this.inputs[slot])&&null!==slot.link&&(slot=this.graph.links[slot.link])?this.graph.getNodeById(slot.origin_id):null},LGraphNode.prototype.getInputOrProperty=function(name){if(!this.inputs||!this.inputs.length)return this.properties?this.properties[name]:null;for(var i=0,l=this.inputs.length;i=this.outputs.length?null:this.outputs[slot]._data},LGraphNode.prototype.getOutputInfo=function(slot){return this.outputs&&slot=this.outputs.length)return null;var output=this.outputs[slot];if(!output.links||0==output.links.length)return null;for(var r=[],i=0;ix&&this.pos[1]-margin_top-marginy)return!0;return!1},LGraphNode.prototype.getSlotInPosition=function(x,y){var link_pos=new Float32Array(2);if(this.inputs)for(var i=0,l=this.inputs.length;i=this.outputs.length)return LiteGraph.debug&&console.log("Connect: Error, slot number not found"),null;if(!(target_node=target_node&&target_node.constructor===Number?this.graph.getNodeById(target_node):target_node))throw"target node is null";if(target_node==this)return null;if(target_slot.constructor===String){if(-1==(target_slot=target_node.findInputSlot(target_slot)))return LiteGraph.debug&&console.log("Connect: Error, no slot of name "+target_slot),null}else if(target_slot===LiteGraph.EVENT){if(!LiteGraph.do_add_triggers_slots)return null;target_node.changeMode(LiteGraph.ON_TRIGGER),target_slot=target_node.findInputSlot("onTrigger")}else if(!target_node.inputs||target_slot>=target_node.inputs.length)return LiteGraph.debug&&console.log("Connect: Error, slot number not found"),null;var link_info,changed=!1,input=target_node.inputs[target_slot],output=this.outputs[slot];return this.outputs[slot]?!1!==(target_slot=target_node.onBeforeConnectInput?target_node.onBeforeConnectInput(target_slot):target_slot)&&null!==target_slot&&LiteGraph.isValidConnection(output.type,input.type)?target_node.onConnectInput&&!1===target_node.onConnectInput(target_slot,output.type,output,this,slot)||this.onConnectOutput&&!1===this.onConnectOutput(slot,input.type,input,target_node,target_slot)?null:(target_node.inputs[target_slot]&&null!=target_node.inputs[target_slot].link&&(this.graph.beforeChange(),target_node.disconnectInput(target_slot,{doProcessChange:!1}),changed=!0),null!==output.links&&output.links.length&&output.type===LiteGraph.EVENT&&!LiteGraph.allow_multi_output_for_events&&(this.graph.beforeChange(),this.disconnectOutput(slot,!1,{doProcessChange:!1}),changed=!0),link_info=new LLink(LiteGraph.use_uuids?LiteGraph.uuidv4():++this.graph.last_link_id,input.type||output.type,this.id,slot,target_node.id,target_slot),this.graph.links[link_info.id]=link_info,null==output.links&&(output.links=[]),output.links.push(link_info.id),target_node.inputs[target_slot].link=link_info.id,this.graph&&this.graph._version++,this.onConnectionsChange&&this.onConnectionsChange(LiteGraph.OUTPUT,slot,!0,link_info,output),target_node.onConnectionsChange&&target_node.onConnectionsChange(LiteGraph.INPUT,target_slot,!0,link_info,input),this.graph&&this.graph.onNodeConnectionChange&&(this.graph.onNodeConnectionChange(LiteGraph.INPUT,target_node,target_slot,this,slot),this.graph.onNodeConnectionChange(LiteGraph.OUTPUT,this,slot,target_node,target_slot)),this.setDirtyCanvas(!1,!0),this.graph.afterChange(),this.graph.connectionChange(this,link_info),link_info):(this.setDirtyCanvas(!1,!0),changed&&this.graph.connectionChange(this,null),null):null},LGraphNode.prototype.disconnectOutput=function(slot,target_node){if(slot.constructor===String){if(-1==(slot=this.findOutputSlot(slot)))return LiteGraph.debug&&console.log("Connect: Error, no slot of name "+slot),!1}else if(!this.outputs||slot>=this.outputs.length)return LiteGraph.debug&&console.log("Connect: Error, slot number not found"),!1;var output=this.outputs[slot];if(!output||!output.links||0==output.links.length)return!1;if(target_node){if(!(target_node=target_node.constructor===Number?this.graph.getNodeById(target_node):target_node))throw"Target Node not found";for(var i=0,l=output.links.length;i=this.inputs.length)return LiteGraph.debug&&console.log("Connect: Error, slot number not found"),!1;var input=this.inputs[slot];if(!input)return!1;var link_id=this.inputs[slot].link;if(null!=link_id){this.inputs[slot].link=null;var link_info=this.graph.links[link_id];if(link_info){var target_node=this.graph.getNodeById(link_info.origin_id);if(!target_node)return!1;var output=target_node.outputs[link_info.origin_slot];if(!output||!output.links||0==output.links.length)return!1;for(var i=0,l=output.links.length;iLGraphNode.MAX_CONSOLE&&this.console.shift(),this.graph.onNodeTrace&&this.graph.onNodeTrace(this,msg)},LGraphNode.prototype.setDirtyCanvas=function(dirty_foreground,dirty_background){this.graph&&this.graph.sendActionToCanvas("setDirty",[dirty_foreground,dirty_background])},LGraphNode.prototype.loadImage=function(url){var img=new Image,that=(img.src=LiteGraph.node_images_path+url,img.ready=!1,this);return img.onload=function(){this.ready=!0,that.setDirtyCanvas(!0)},img},LGraphNode.prototype.captureInput=function(v){if(this.graph&&this.graph.list_of_graphcanvas)for(var list=this.graph.list_of_graphcanvas,i=0;i=this.viewport[0]&&x=this.viewport[1]&&rectthis.max_scale&&(value=this.max_scale),value!=this.scale&&this.element&&(rect=this.element.getBoundingClientRect())&&(zooming_center=zooming_center||[.5*rect.width,.5*rect.height],rect=this.convertCanvasToOffset(zooming_center),this.scale=value,Math.abs(this.scale-1)<.01&&(this.scale=1),zooming_center=[(value=this.convertCanvasToOffset(zooming_center))[0]-rect[0],value[1]-rect[1]],this.offset[0]+=zooming_center[0],this.offset[1]+=zooming_center[1],this.onredraw)&&this.onredraw(this)},DragAndScale.prototype.changeDeltaScale=function(value,zooming_center){this.changeScale(this.scale*value,zooming_center)},DragAndScale.prototype.reset=function(){this.scale=1,this.offset[0]=0,this.offset[1]=0},global.LGraphCanvas=LiteGraph.LGraphCanvas=LGraphCanvas,LGraphCanvas.DEFAULT_BACKGROUND_IMAGE="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAIAAAD/gAIDAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAQBJREFUeNrs1rEKwjAUhlETUkj3vP9rdmr1Ysammk2w5wdxuLgcMHyptfawuZX4pJSWZTnfnu/lnIe/jNNxHHGNn//HNbbv+4dr6V+11uF527arU7+u63qfa/bnmh8sWLBgwYJlqRf8MEptXPBXJXa37BSl3ixYsGDBMliwFLyCV/DeLIMFCxYsWLBMwSt4Be/NggXLYMGCBUvBK3iNruC9WbBgwYJlsGApeAWv4L1ZBgsWLFiwYJmCV/AK3psFC5bBggULloJX8BpdwXuzYMGCBctgwVLwCl7Be7MMFixYsGDBsu8FH1FaSmExVfAxBa/gvVmwYMGCZbBg/W4vAQYA5tRF9QYlv/QAAAAASUVORK5CYII=",LGraphCanvas.link_type_colors={"-1":LiteGraph.EVENT_LINK_COLOR,number:"#AAA",node:"#DCA"},LGraphCanvas.gradients={},LGraphCanvas.prototype.clear=function(){this.frame=0,this.last_draw_time=0,this.render_time=0,this.fps=0,this.dragging_rectangle=null,this.selected_nodes={},this.selected_group=null,this.visible_nodes=[],this.node_dragged=null,this.node_over=null,this.node_capturing_input=null,this.connecting_node=null,this.highlighted_links={},this.dragging_canvas=!1,this.dirty_canvas=!0,this.dirty_bgcanvas=!0,this.dirty_area=null,this.node_in_panel=null,this.node_widget=null,this.last_mouse=[0,0],this.last_mouseclick=0,this.pointer_is_down=!1,this.pointer_is_double=!1,this.visible_area.set([0,0,0,0]),this.onClear&&this.onClear()},LGraphCanvas.prototype.setGraph=function(graph,skip_clear){this.graph!=graph&&(skip_clear||this.clear(),!graph&&this.graph?this.graph.detachCanvas(this):(graph.attachCanvas(this),this._graph_stack&&(this._graph_stack=null),this.setDirty(!0,!0)))},LGraphCanvas.prototype.getTopGraph=function(){return this._graph_stack.length?this._graph_stack[0]:this.graph},LGraphCanvas.prototype.openSubgraph=function(graph){if(!graph)throw"graph cannot be null";if(this.graph==graph)throw"graph cannot be the same";this.clear(),this.graph&&(this._graph_stack||(this._graph_stack=[]),this._graph_stack.push(this.graph)),graph.attachCanvas(this),this.checkPanels(),this.setDirty(!0,!0)},LGraphCanvas.prototype.closeSubgraph=function(){var subgraph_node,graph;this._graph_stack&&0!=this._graph_stack.length&&(subgraph_node=this.graph._subgraph_node,graph=this._graph_stack.pop(),this.selected_nodes={},this.highlighted_links={},graph.attachCanvas(this),this.setDirty(!0,!0),subgraph_node&&(this.centerOnNode(subgraph_node),this.selectNodes([subgraph_node])),this.ds.offset=[0,0],this.ds.scale=1)},LGraphCanvas.prototype.getCurrentGraph=function(){return this.graph},LGraphCanvas.prototype.setCanvas=function(canvas,skip_events){if(canvas&&canvas.constructor===String&&!(canvas=document.getElementById(canvas)))throw"Error creating LiteGraph canvas: Canvas not found";if(canvas!==this.canvas&&(canvas||!this.canvas||skip_events||this.unbindEvents(),this.canvas=canvas,this.ds.element=canvas)){if(canvas.className+=" lgraphcanvas",canvas.data=this,canvas.tabindex="1",this.bgcanvas=null,this.bgcanvas||(this.bgcanvas=document.createElement("canvas"),this.bgcanvas.width=this.canvas.width,this.bgcanvas.height=this.canvas.height),null==canvas.getContext){if("canvas"!=canvas.localName)throw"Element supplied for LGraphCanvas must be a element, you passed a "+canvas.localName;throw"This browser doesn't support Canvas"}null==(this.ctx=canvas.getContext("2d"))&&(canvas.webgl_enabled||console.warn("This canvas seems to be WebGL, enabling WebGL renderer"),this.enableWebGL()),skip_events||this.bindEvents()}},LGraphCanvas.prototype._doNothing=function(e){return e.preventDefault(),!1},LGraphCanvas.prototype._doReturnTrue=function(e){return e.preventDefault(),!0},LGraphCanvas.prototype.bindEvents=function(){var canvas,document;this._events_binded?console.warn("LGraphCanvas: events already binded"):(canvas=this.canvas,document=this.getCanvasWindow().document,this._mousedown_callback=this.processMouseDown.bind(this),this._mousewheel_callback=this.processMouseWheel.bind(this),this._mousemove_callback=this.processMouseMove.bind(this),this._mouseup_callback=this.processMouseUp.bind(this),LiteGraph.pointerListenerAdd(canvas,"down",this._mousedown_callback,!0),canvas.addEventListener("mousewheel",this._mousewheel_callback,!1),LiteGraph.pointerListenerAdd(canvas,"up",this._mouseup_callback,!0),LiteGraph.pointerListenerAdd(canvas,"move",this._mousemove_callback),canvas.addEventListener("contextmenu",this._doNothing),canvas.addEventListener("DOMMouseScroll",this._mousewheel_callback,!1),this._key_callback=this.processKey.bind(this),canvas.setAttribute("tabindex",1),canvas.addEventListener("keydown",this._key_callback,!0),document.addEventListener("keyup",this._key_callback,!0),this._ondrop_callback=this.processDrop.bind(this),canvas.addEventListener("dragover",this._doNothing,!1),canvas.addEventListener("dragend",this._doNothing,!1),canvas.addEventListener("drop",this._ondrop_callback,!1),canvas.addEventListener("dragenter",this._doReturnTrue,!1),this._events_binded=!0)},LGraphCanvas.prototype.unbindEvents=function(){var document;this._events_binded?(document=this.getCanvasWindow().document,LiteGraph.pointerListenerRemove(this.canvas,"move",this._mousedown_callback),LiteGraph.pointerListenerRemove(this.canvas,"up",this._mousedown_callback),LiteGraph.pointerListenerRemove(this.canvas,"down",this._mousedown_callback),this.canvas.removeEventListener("mousewheel",this._mousewheel_callback),this.canvas.removeEventListener("DOMMouseScroll",this._mousewheel_callback),this.canvas.removeEventListener("keydown",this._key_callback),document.removeEventListener("keyup",this._key_callback),this.canvas.removeEventListener("contextmenu",this._doNothing),this.canvas.removeEventListener("drop",this._ondrop_callback),this.canvas.removeEventListener("dragenter",this._doReturnTrue),this._mousedown_callback=null,this._mousewheel_callback=null,this._key_callback=null,this._ondrop_callback=null,this._events_binded=!1):console.warn("LGraphCanvas: no events binded")},LGraphCanvas.getFileExtension=function(url){var question=url.indexOf("?"),question=(url=-1!=question?url.substr(0,question):url).lastIndexOf(".");return-1==question?"":url.substr(question+1).toLowerCase()},LGraphCanvas.prototype.enableWebGL=function(){if("undefined"==typeof GL)throw"litegl.js must be included to use a WebGL canvas";if("undefined"==typeof enableWebGLCanvas)throw"webglCanvas.js must be included to use this feature";this.gl=this.ctx=enableWebGLCanvas(this.canvas),this.ctx.webgl=!0,this.bgcanvas=this.canvas,this.bgctx=this.gl,this.canvas.webgl_enabled=!0},LGraphCanvas.prototype.setDirty=function(fgcanvas,bgcanvas){fgcanvas&&(this.dirty_canvas=!0),bgcanvas&&(this.dirty_bgcanvas=!0)},LGraphCanvas.prototype.getCanvasWindow=function(){var doc;return this.canvas?(doc=this.canvas.ownerDocument).defaultView||doc.parentWindow:window},LGraphCanvas.prototype.startRendering=function(){function renderFrame(){this.pause_rendering||this.draw();var window=this.getCanvasWindow();this.is_rendering&&window.requestAnimationFrame(renderFrame.bind(this))}this.is_rendering||(this.is_rendering=!0,renderFrame.call(this))},LGraphCanvas.prototype.stopRendering=function(){this.is_rendering=!1},LGraphCanvas.prototype.blockClick=function(){this.block_click=!0,this.last_mouseclick=0},LGraphCanvas.prototype.processMouseDown=function(e){if(this.set_canvas_dirty_on_mouse_event&&(this.dirty_canvas=!0),this.graph){this.adjustMouseEvent(e);var ref_window=this.getCanvasWindow(),that=(ref_window.document,LGraphCanvas.active_canvas=this),x=e.clientX,y=e.clientY,x=(this.ds.viewport=this.viewport,!this.viewport||this.viewport&&x>=this.viewport[0]&&x=this.viewport[1]&&ycenter[0]+4||e.canvasYcenter[1]+4)){this.showLinkMenu(link,e),this.over_link_center=null;break}}this.selected_group=this.graph.getGroupOnPos(e.canvasX,e.canvasY),this.selected_group_resizing=!1,this.selected_group&&!this.read_only&&(e.ctrlKey&&(this.dragging_rectangle=null),distance([e.canvasX,e.canvasY],[this.selected_group.pos[0]+this.selected_group.size[0],this.selected_group.pos[1]+this.selected_group.size[1]])*this.ds.scale<10?this.selected_group_resizing=!0:this.selected_group.recomputeInsideNodes()),is_double_click&&!this.read_only&&this.allow_searchbox&&(this.showSearchBox(e),e.preventDefault(),e.stopPropagation()),x=!0}}else{if(this.live_mode||node.flags.pinned||this.bringToFront(node),this.allow_interaction&&!this.connecting_node&&!node.flags.collapsed&&!this.live_mode)if(!skip_action&&!1!==node.resizable&&isInsideRectangle(e.canvasX,e.canvasY,node.pos[0]+node.size[0]-5,node.pos[1]+node.size[1]-5,10,10))this.graph.beforeChange(),this.resizing_node=node,this.canvas.style.cursor="se-resize",skip_action=!0;else{if(node.outputs)for(var i=0,l=node.outputs.length;inode.size[0]-LiteGraph.NODE_TITLE_HEIGHT&&y[1]<0&&(that=this,setTimeout(function(){that.openSubgraph(node.subgraph)},10)),this.live_mode&&(block_drag_node=x=!0)),block_drag_node?node.is_selected||this.processNodeSelected(node,e):(this.allow_dragnodes&&(this.graph.beforeChange(),this.node_dragged=node),this.processNodeSelected(node,e)),this.dirty_canvas=!0)}!skip_action&&x&&this.allow_dragcanvas&&(this.dragging_canvas=!0)}return this.last_mouse[0]=e.clientX,this.last_mouse[1]=e.clientY,this.last_mouseclick=LiteGraph.getTime(),this.last_mouse_dragging=!0,this.graph.change(),ref_window.document.activeElement&&("input"==ref_window.document.activeElement.nodeName.toLowerCase()||"textarea"==ref_window.document.activeElement.nodeName.toLowerCase())||e.preventDefault(),e.stopPropagation(),this.onMouseDown&&this.onMouseDown(e),!1}}}},LGraphCanvas.prototype.processMouseMove=function(e){if(this.autoresize&&this.resize(),this.set_canvas_dirty_on_mouse_event&&(this.dirty_canvas=!0),this.graph){(LGraphCanvas.active_canvas=this).adjustMouseEvent(e);var mouse=[e.clientX,e.clientY],delta=(this.mouse[0]=mouse[0],this.mouse[1]=mouse[1],[mouse[0]-this.last_mouse[0],mouse[1]-this.last_mouse[1]]);if(this.last_mouse=mouse,this.graph_mouse[0]=e.canvasX,this.graph_mouse[1]=e.canvasY,!this.block_click){e.dragging=this.last_mouse_dragging,this.node_widget&&(this.processNodeWidgets(this.node_widget[0],this.graph_mouse,e,this.node_widget[1]),this.dirty_canvas=!0);var node=this.graph.getNodeOnPos(e.canvasX,e.canvasY,this.visible_nodes);if(this.dragging_rectangle)this.dragging_rectangle[2]=e.canvasX-this.dragging_rectangle[0],this.dragging_rectangle[3]=e.canvasY-this.dragging_rectangle[1],this.dirty_canvas=!0;else if(this.selected_group&&!this.read_only)this.selected_group_resizing?this.selected_group.size=[e.canvasX-this.selected_group.pos[0],e.canvasY-this.selected_group.pos[1]]:(mouse=delta[0]/this.ds.scale,deltay=delta[1]/this.ds.scale,this.selected_group.move(mouse,deltay,e.ctrlKey),this.selected_group._nodes.length&&(this.dirty_canvas=!0)),this.dirty_bgcanvas=!0;else if(this.dragging_canvas)this.ds.offset[0]+=delta[0]/this.ds.scale,this.ds.offset[1]+=delta[1]/this.ds.scale,this.dirty_canvas=!0,this.dirty_bgcanvas=!0;else if((this.allow_interaction||node&&node.flags.allow_interaction)&&!this.read_only){this.connecting_node&&(this.dirty_canvas=!0);for(var pos,slot,slot_type,deltay,i=0,l=this.graph._nodes.length;icenter[0]+4||e.canvasYcenter[1]+4)){over_link=link;break}}over_link!=this.over_link_center&&(this.over_link_center=over_link,this.dirty_canvas=!0),this.canvas&&(this.canvas.style.cursor="")}if(this.node_capturing_input&&this.node_capturing_input!=node&&this.node_capturing_input.onMouseMove&&this.node_capturing_input.onMouseMove(e,[e.canvasX-this.node_capturing_input.pos[0],e.canvasY-this.node_capturing_input.pos[1]],this),this.node_dragged&&!this.live_mode){for(var i in this.selected_nodes){var n=this.selected_nodes[i];n.pos[0]+=delta[0]/this.ds.scale,n.pos[1]+=delta[1]/this.ds.scale,n.is_selected||this.processNodeSelected(n,e)}this.dirty_canvas=!0,this.dirty_bgcanvas=!0}this.resizing_node&&!this.live_mode&&(mouse=[e.canvasX-this.resizing_node.pos[0],e.canvasY-this.resizing_node.pos[1]],deltay=this.resizing_node.computeSize(),mouse[0]=Math.max(deltay[0],mouse[0]),mouse[1]=Math.max(deltay[1],mouse[1]),this.resizing_node.setSize(mouse),this.canvas.style.cursor="se-resize",this.dirty_canvas=!0,this.dirty_bgcanvas=!0)}}return e.preventDefault(),!1}},LGraphCanvas.prototype.processMouseUp=function(e){var is_primary=void 0===e.isPrimary||e.isPrimary;if(!is_primary)return!1;if(this.set_canvas_dirty_on_mouse_event&&(this.dirty_canvas=!0),this.graph){var document=this.getCanvasWindow().document,document=((LGraphCanvas.active_canvas=this).options.skip_events||(LiteGraph.pointerListenerRemove(document,"move",this._mousemove_callback,!0),LiteGraph.pointerListenerAdd(this.canvas,"move",this._mousemove_callback,!0),LiteGraph.pointerListenerRemove(document,"up",this._mouseup_callback,!0)),this.adjustMouseEvent(e),LiteGraph.getTime());if(e.click_time=document-this.last_mouseclick,this.last_mouse_dragging=!1,this.last_click_position=null,this.block_click&&(this.block_click=!1),1==e.which){this.node_widget&&this.processNodeWidgets(this.node_widget[0],this.graph_mouse,e),this.node_widget=null,this.selected_group&&(document=this.selected_group.pos[0]-Math.round(this.selected_group.pos[0]),diffy=this.selected_group.pos[1]-Math.round(this.selected_group.pos[1]),this.selected_group.move(document,diffy,e.ctrlKey),this.selected_group.pos[0]=Math.round(this.selected_group.pos[0]),this.selected_group.pos[1]=Math.round(this.selected_group.pos[1]),this.selected_group._nodes.length&&(this.dirty_canvas=!0),this.selected_group=null),this.selected_group_resizing=!1;var slot,document=this.graph.getNodeOnPos(e.canvasX,e.canvasY,this.visible_nodes);if(this.dragging_rectangle){if(this.graph){var nodes=this.graph._nodes,node_bounding=new Float32Array(4),diffy=Math.abs(this.dragging_rectangle[2]),h=Math.abs(this.dragging_rectangle[3]),startx=this.dragging_rectangle[2]<0?this.dragging_rectangle[0]-diffy:this.dragging_rectangle[0],starty=this.dragging_rectangle[3]<0?this.dragging_rectangle[1]-h:this.dragging_rectangle[1];if(this.dragging_rectangle[0]=startx,this.dragging_rectangle[1]=starty,this.dragging_rectangle[2]=diffy,this.dragging_rectangle[3]=h,!document||10=this.viewport[0]&&x=this.viewport[1]&&yclipboard_info.nodes[i].pos[0]&&(posMin[0]=clipboard_info.nodes[i].pos[0],posMinIndexes[0]=i),posMin[1]>clipboard_info.nodes[i].pos[1]&&(posMin[1]=clipboard_info.nodes[i].pos[1],posMinIndexes[1]=i)):(posMin=[clipboard_info.nodes[i].pos[0],clipboard_info.nodes[i].pos[1]],posMinIndexes=[i,i]);for(var nodes=[],i=0;i=this.viewport[0]&&x=this.viewport[1]&&y> ";ctx.fillText(title+viewport.getTitle(),.5*canvas.width,40),ctx.restore()}var that,viewport=!1;this.onRenderBackground&&(viewport=this.onRenderBackground(canvas,ctx)),this.viewport||(ctx.restore(),ctx.setTransform(1,0,0,1,0,0)),this.visible_links.length=0,this.graph&&(ctx.save(),this.ds.toCanvasContext(ctx),this.ds.scale<1.5&&!viewport&&this.clear_background_color&&(ctx.fillStyle=this.clear_background_color,ctx.fillRect(this.visible_area[0],this.visible_area[1],this.visible_area[2],this.visible_area[3])),this.background_image&&.5bounding[2]&&(bounding[2]=x),ybounding[3]&&(bounding[3]=y)}function isInsideBounding(p,bb){return!(p[0]bb[1][0]||p[1]>bb[1][1])}function overlapBounding(a,b){var A_end_x=a[0]+a[2],A_end_y=a[1]+a[3],B_end_x=b[0]+b[2],B_end_y=b[1]+b[3];return!(a[0]>B_end_x||a[1]>B_end_y||A_end_xrect.width-root_rect.width-10&&(eventClass=rect.width-root_rect.width-10),rect.height)&&top>rect.height-root_rect.height-10&&(top=rect.height-root_rect.height-10),root.style.left=eventClass+"px",root.style.top=top+"px",options.scale&&(root.style.transform="scale("+options.scale+")")}function CurveEditor(points){this.points=points,this.selected=-1,this.nearest=-1,this.size=null,this.must_update=!0,this.margin=5}function clamp(v,a,b){return va[1]?0:Math.PI,ctx.save(),ctx.translate(skip_border[0],skip_border[1]),ctx.rotate(angleA),ctx.beginPath(),ctx.moveTo(-5,-3),ctx.lineTo(0,7),ctx.lineTo(5,-3),ctx.fill(),ctx.restore(),ctx.save(),ctx.translate(posC[0],posC[1]),ctx.rotate(angleB),ctx.beginPath(),ctx.moveTo(-5,-3),ctx.lineTo(0,7),ctx.lineTo(5,-3),ctx.fill(),ctx.restore()),ctx.beginPath(),ctx.arc(pos[0],pos[1],5,0,2*Math.PI),ctx.fill()),flow){ctx.fillStyle=color;for(i=0;i<5;++i){var f=(.001*LiteGraph.getTime()+.2*i)%1,pos=this.computeConnectionPoint(a,b,f,start_dir,end_dir);ctx.beginPath(),ctx.arc(pos[0],pos[1],5,0,2*Math.PI),ctx.fill()}}},LGraphCanvas.prototype.computeConnectionPoint=function(a,b,t,start_dir,end_dir){start_dir=start_dir||LiteGraph.RIGHT,end_dir=end_dir||LiteGraph.LEFT;var dist=distance(a,b),p0=a,p1=[a[0],a[1]],p2=[b[0],b[1]],a=b;switch(start_dir){case LiteGraph.LEFT:p1[0]+=-.25*dist;break;case LiteGraph.RIGHT:p1[0]+=.25*dist;break;case LiteGraph.UP:p1[1]+=-.25*dist;break;case LiteGraph.DOWN:p1[1]+=.25*dist}switch(end_dir){case LiteGraph.LEFT:p2[0]+=-.25*dist;break;case LiteGraph.RIGHT:p2[0]+=.25*dist;break;case LiteGraph.UP:p2[1]+=-.25*dist;break;case LiteGraph.DOWN:p2[1]+=.25*dist}b=(1-t)*(1-t)*(1-t),start_dir=(1-t)*(1-t)*3*t,end_dir=3*(1-t)*(t*t),t*=t*t;return[b*p0[0]+start_dir*p1[0]+end_dir*p2[0]+t*a[0],b*p0[1]+start_dir*p1[1]+end_dir*p2[1]+t*a[1]]},LGraphCanvas.prototype.drawExecutionOrder=function(ctx){ctx.shadowColor="transparent",ctx.globalAlpha=.25,ctx.textAlign="center",ctx.strokeStyle="white",ctx.globalAlpha=.75;for(var visible_nodes=this.visible_nodes,i=0;iw.last_y+widget_height||void 0===w.last_y)){var old_value=w.value;switch(w.type){case"button":event.type===LiteGraph.pointerevents_method+"down"&&(w.callback&&setTimeout(function(){w.callback(w,that,node,pos,event)},20),w.clicked=!0,this.dirty_canvas=!0);break;case"slider":var old_value=w.value,nvalue=clamp((x-15)/(widget_width-30),0,1);w.options.read_only||(w.value=w.options.min+(w.options.max-w.options.min)*nvalue,old_value!=w.value&&setTimeout(function(){inner_value_change(w,w.value)},20),this.dirty_canvas=!0);break;case"number":case"combo":var old_value=w.value,values,values_list,delta,index,text_values,menu,delta;function inner_clicked(v,option,event){return values!=values_list&&(v=text_values.indexOf(v)),this.value=v,inner_value_change(this,v),!(that.dirty_canvas=!0)}event.type==LiteGraph.pointerevents_method+"move"&&"number"==w.type?(deltaX&&(w.value+=.1*deltaX*(w.options.step||1)),null!=w.options.min&&w.valuew.options.max&&(w.value=w.options.max)):event.type==LiteGraph.pointerevents_method+"down"?(values=w.options.values,values&&values.constructor===Function&&(values=w.options.values(w,node)),values_list=null,"number"!=w.type&&(values_list=values.constructor===Array?values:Object.keys(values)),delta=x<40?-1:widget_width-40w.options.max&&(w.value=w.options.max)):delta?(index=-1,this.last_mouseclick=0,index=values.constructor===Object?values_list.indexOf(String(w.value))+delta:values_list.indexOf(w.value)+delta,index>=values_list.length&&(index=values_list.length-1),index<0&&(index=0),values.constructor===Array?w.value=values[index]:w.value=index):(text_values=values!=values_list?Object.values(values):values,menu=new LiteGraph.ContextMenu(text_values,{scale:Math.max(1,this.ds.scale),event:event,className:"dark",callback:inner_clicked.bind(w)},ref_window))):event.type==LiteGraph.pointerevents_method+"up"&&"number"==w.type&&(delta=x<40?-1:widget_width-40right.pos[0]+right.size[0])&&(right=node),(null===bottom||y+height>bottom.pos[1]+bottom.size[1])&&(bottom=node),(null===left||x"+(info.label||i)+""+value+"",value:i})}if(entries.length)return new LiteGraph.ContextMenu(entries,{event:e,callback:function(v,options,e,prev){var rect;node&&(rect=this.getBoundingClientRect(),canvas.showEditPropertyValue(node,v.value,{position:[rect.left,rect.top]}))},parentMenu:prev_menu,allow_html:!0,node:node},ref_window),!1}},LGraphCanvas.decodeHTML=function(str){var e=document.createElement("div");return e.innerText=str,e.innerHTML},LGraphCanvas.onMenuResizeNode=function(value,options,e,menu,node){if(node){function fApplyMultiNode(node){node.size=node.computeSize(),node.onResize&&node.onResize(node.size)}var graphcanvas=LGraphCanvas.active_canvas;if(!graphcanvas.selected_nodes||Object.keys(graphcanvas.selected_nodes).length<=1)fApplyMultiNode(node);else for(var i in graphcanvas.selected_nodes)fApplyMultiNode(graphcanvas.selected_nodes[i]);node.setDirtyCanvas(!0,!0)}},LGraphCanvas.prototype.showLinkMenu=function(link,e){var that=this,node_left=that.graph.getNodeById(link.origin_id),node_right=that.graph.getNodeById(link.target_id),fromType=!1,destType=(node_left&&node_left.outputs&&node_left.outputs[link.origin_slot]&&(fromType=node_left.outputs[link.origin_slot].type),!1),menu=(node_right&&node_right.outputs&&node_right.outputs[link.target_slot]&&(destType=node_right.inputs[link.target_slot].type),new LiteGraph.ContextMenu(["Add Node",null,"Delete",null],{event:e,title:null!=link.data?link.data.constructor.name:null,callback:function(v,options,e){switch(v){case"Add Node":LGraphCanvas.onMenuAdd(null,null,e,menu,function(node){node.inputs&&node.inputs.length&&node.outputs&&node.outputs.length&&node_left.connectByType(link.origin_slot,node,fromType)&&(node.connectByType(link.target_slot,node_right,destType),node.pos[0]-=.5*node.size[0])});break;case"Delete":that.graph.removeLink(link.id)}}}));return!1},LGraphCanvas.prototype.createDefaultNodeForSlot=function(optPass){var optPass=optPass||{},opts=Object.assign({nodeFrom:null,slotFrom:null,nodeTo:null,slotTo:null,position:[],nodeType:null,posAdd:[0,0],posSizeFix:[0,0]},optPass),isFrom=opts.nodeFrom&&null!==opts.slotFrom,optPass=!isFrom&&opts.nodeTo&&null!==opts.slotTo;if(isFrom||optPass)if(opts.nodeType){var nodeX=isFrom?opts.nodeFrom:opts.nodeTo,slotX=isFrom?opts.slotFrom:opts.slotTo,iSlotConn=!1;switch(typeof slotX){case"string":iSlotConn=isFrom?nodeX.findOutputSlot(slotX,!1):nodeX.findInputSlot(slotX,!1),slotX=(isFrom?nodeX.outputs:nodeX.inputs)[slotX];break;case"object":iSlotConn=isFrom?nodeX.findOutputSlot(slotX.name):nodeX.findInputSlot(slotX.name);break;case"number":iSlotConn=slotX,slotX=(isFrom?nodeX.outputs:nodeX.inputs)[slotX];break;default:return console.warn("Cant get slot information "+slotX),!1}!1!==slotX&&!1!==iSlotConn||console.warn("createDefaultNodeForSlot bad slotX "+slotX+" "+iSlotConn);var fromSlotType=slotX.type==LiteGraph.EVENT?"_event_":slotX.type,slotTypesDefault=isFrom?LiteGraph.slot_types_default_out:LiteGraph.slot_types_default_in;if(slotTypesDefault&&slotTypesDefault[fromSlotType]){if(slotX.link,nodeNewType=!1,Array.isArray(slotTypesDefault[fromSlotType])||"object"==typeof slotTypesDefault[fromSlotType]){for(var typeX in slotTypesDefault[fromSlotType])if(opts.nodeType==slotTypesDefault[fromSlotType][typeX]||"AUTO"==opts.nodeType){nodeNewType=slotTypesDefault[fromSlotType][typeX];break}}else opts.nodeType!=slotTypesDefault[fromSlotType]&&"AUTO"!=opts.nodeType||(nodeNewType=slotTypesDefault[fromSlotType]);if(nodeNewType){var nodeNewOpts=!1,newNode=("object"==typeof nodeNewType&&nodeNewType.node&&(nodeNewType=(nodeNewOpts=nodeNewType).node),LiteGraph.createNode(nodeNewType));if(newNode){if(nodeNewOpts){if(nodeNewOpts.properties)for(var i in nodeNewOpts.properties)newNode.addProperty(i,nodeNewOpts.properties[i]);if(nodeNewOpts.inputs)for(var i in newNode.inputs=[],nodeNewOpts.inputs)newNode.addOutput(nodeNewOpts.inputs[i][0],nodeNewOpts.inputs[i][1]);if(nodeNewOpts.outputs)for(var i in newNode.outputs=[],nodeNewOpts.outputs)newNode.addOutput(nodeNewOpts.outputs[i][0],nodeNewOpts.outputs[i][1]);nodeNewOpts.title&&(newNode.title=nodeNewOpts.title),nodeNewOpts.json&&newNode.configure(nodeNewOpts.json)}return this.graph.add(newNode),newNode.pos=[opts.position[0]+opts.posAdd[0]+(opts.posSizeFix[0]?opts.posSizeFix[0]*newNode.size[0]:0),opts.position[1]+opts.posAdd[1]+(opts.posSizeFix[1]?opts.posSizeFix[1]*newNode.size[1]:0)],isFrom?opts.nodeFrom.connectByType(iSlotConn,newNode,fromSlotType):opts.nodeTo.connectByTypeOutput(iSlotConn,newNode,fromSlotType),!0}console.log("failed creating "+nodeNewType)}}}else console.warn("No type to createDefaultNodeForSlot");else console.warn("No data passed to createDefaultNodeForSlot "+opts.nodeFrom+" "+opts.slotFrom+" "+opts.nodeTo+" "+opts.slotTo);return!1},LGraphCanvas.prototype.showConnectionMenu=function(optPass){var optPass=optPass||{},opts=Object.assign({nodeFrom:null,slotFrom:null,nodeTo:null,slotTo:null,e:null},optPass),that=this,isFrom=opts.nodeFrom&&opts.slotFrom,optPass=!isFrom&&opts.nodeTo&&opts.slotTo;if(isFrom||optPass){var nodeX=isFrom?opts.nodeFrom:opts.nodeTo,slotX=isFrom?opts.slotFrom:opts.slotTo,iSlotConn=!1;switch(typeof slotX){case"string":iSlotConn=isFrom?nodeX.findOutputSlot(slotX,!1):nodeX.findInputSlot(slotX,!1),slotX=(isFrom?nodeX.outputs:nodeX.inputs)[slotX];break;case"object":iSlotConn=isFrom?nodeX.findOutputSlot(slotX.name):nodeX.findInputSlot(slotX.name);break;case"number":iSlotConn=slotX,slotX=(isFrom?nodeX.outputs:nodeX.inputs)[slotX];break;default:return console.warn("Cant get slot information "+slotX),!1}var options=["Add Node",null],fromSlotType=(that.allow_searchbox&&(options.push("Search"),options.push(null)),slotX.type==LiteGraph.EVENT?"_event_":slotX.type),slotTypesDefault=isFrom?LiteGraph.slot_types_default_out:LiteGraph.slot_types_default_in;if(slotTypesDefault&&slotTypesDefault[fromSlotType])if(Array.isArray(slotTypesDefault[fromSlotType])||"object"==typeof slotTypesDefault[fromSlotType])for(var typeX in slotTypesDefault[fromSlotType])options.push(slotTypesDefault[fromSlotType][typeX]);else options.push(slotTypesDefault[fromSlotType]);var menu=new LiteGraph.ContextMenu(options,{event:opts.e,title:(slotX&&""!=slotX.name?slotX.name+(fromSlotType?" | ":""):"")+(slotX&&fromSlotType?fromSlotType:""),callback:function(v,options,e){switch(v){case"Add Node":LGraphCanvas.onMenuAdd(null,null,e,menu,function(node){isFrom?opts.nodeFrom.connectByType(iSlotConn,node,fromSlotType):opts.nodeTo.connectByTypeOutput(iSlotConn,node,fromSlotType)});break;case"Search":isFrom?that.showSearchBox(e,{node_from:opts.nodeFrom,slot_from:slotX,type_filter_in:fromSlotType}):that.showSearchBox(e,{node_to:opts.nodeTo,slot_from:slotX,type_filter_out:fromSlotType});break;default:that.createDefaultNodeForSlot(Object.assign(opts,{position:[opts.e.canvasX,opts.e.canvasY],nodeType:v}))}}})}else console.warn("No data passed to showConnectionMenu");return!1},LGraphCanvas.onShowPropertyEditor=function(item,options,e,menu,node){var property=item.property||"title",value=node[property],dialog=document.createElement("div");dialog.is_modified=!1,dialog.className="graphdialog",dialog.innerHTML="",dialog.close=function(){dialog.parentNode&&dialog.parentNode.removeChild(dialog)};dialog.querySelector(".name").innerText=property;var input=dialog.querySelector(".value");input&&(input.value=value,input.addEventListener("blur",function(e){this.focus()}),input.addEventListener("keydown",function(e){if(dialog.is_modified=!0,27==e.keyCode)dialog.close();else if(13==e.keyCode)inner();else if(13!=e.keyCode&&"textarea"!=e.target.localName)return;e.preventDefault(),e.stopPropagation()}));var value=LGraphCanvas.active_canvas.canvas,rect=value.getBoundingClientRect(),offsetx=-20,offsety=-20;rect&&(offsetx-=rect.left,offsety-=rect.top),event?(dialog.style.left=event.clientX+offsetx+"px",dialog.style.top=event.clientY+offsety+"px"):(dialog.style.left=.5*value.width+offsetx+"px",dialog.style.top=.5*value.height+offsety+"px");dialog.querySelector("button").addEventListener("click",inner),value.parentNode.appendChild(dialog),input&&input.focus();var dialogCloseTimer=null;function inner(){var value;input&&(value=input.value,"Number"==item.type?value=Number(value):"Boolean"==item.type&&(value=Boolean(value)),node[property]=value,dialog.parentNode&&dialog.parentNode.removeChild(dialog),node.setDirtyCanvas(!0,!0))}dialog.addEventListener("mouseleave",function(e){LiteGraph.dialog_close_on_mouse_leave&&!dialog.is_modified&&LiteGraph.dialog_close_on_mouse_leave&&(dialogCloseTimer=setTimeout(dialog.close,LiteGraph.dialog_close_on_mouse_leave_delay))}),dialog.addEventListener("mouseenter",function(e){LiteGraph.dialog_close_on_mouse_leave&&dialogCloseTimer&&clearTimeout(dialogCloseTimer)})},LGraphCanvas.prototype.prompt=function(title,value,callback,event,multiline){var that=this,dialog=(title=title||"",document.createElement("div"));dialog.is_modified=!1,dialog.className="graphdialog rounded",dialog.innerHTML=multiline?" ":" ",dialog.close=function(){that.prompt_box=null,dialog.parentNode&&dialog.parentNode.removeChild(dialog)};var multiline=LGraphCanvas.active_canvas.canvas,dialogCloseTimer=(multiline.parentNode.appendChild(dialog),1LGraphCanvas.search_limit)break}}var filtered=null;if(Array.prototype.filter)filtered=Object.keys(LiteGraph.registered_node_types).filter(inner_test_filter);else for(var i in filtered=[],LiteGraph.registered_node_types)inner_test_filter(i)&&filtered.push(i);for(i=0;iLGraphCanvas.search_limit));i++);if(options.show_general_after_typefiltered&&(sIn.value||sOut.value)){for(var i in filtered_extra=[],LiteGraph.registered_node_types)inner_test_filter(i,{inTypeOverride:!(!sIn||!sIn.value)&&"*",outTypeOverride:!(!sOut||!sOut.value)&&"*"})&&filtered_extra.push(i);for(i=0;iLGraphCanvas.search_limit));i++);}if((sIn.value||sOut.value)&&0==helper.childNodes.length&&options.show_general_if_none_on_typefilter){for(var i in filtered_extra=[],LiteGraph.registered_node_types)inner_test_filter(i,{skipFilter:!0})&&filtered_extra.push(i);for(i=0;iLGraphCanvas.search_limit));i++);}function inner_test_filter(type,optsIn){var optsIn=optsIn||{},optsIn=Object.assign({skipFilter:!1,inTypeOverride:!1,outTypeOverride:!1},optsIn),ctor=LiteGraph.registered_node_types[type];if(filter&&ctor.filter!=filter)return!1;if((!options.show_all_if_empty||str)&&-1===type.toLowerCase().indexOf(str))return!1;if(options.do_type_filter&&!optsIn.skipFilter){ctor=type,type=sIn.value;if(!1!==optsIn.inTypeOverride&&(type=optsIn.inTypeOverride),sIn&&type&&LiteGraph.registered_slot_in_types[type]&&LiteGraph.registered_slot_in_types[type].nodes){var doesInc=LiteGraph.registered_slot_in_types[type].nodes.includes(ctor);if(!1===doesInc)return!1}type=sOut.value;if(!1!==optsIn.outTypeOverride&&(type=optsIn.outTypeOverride),sOut&&type&&LiteGraph.registered_slot_out_types[type]&&LiteGraph.registered_slot_out_types[type].nodes){doesInc=LiteGraph.registered_slot_out_types[type].nodes.includes(ctor);if(!1===doesInc)return!1}}return!0}}function addResult(type,className){var help=document.createElement("div");first=first||type,help.innerText=type,help.dataset.type=escape(type),help.className="litegraph lite-search-item",className&&(help.className+=" "+className),help.addEventListener("click",function(e){select(unescape(this.dataset.type))}),helper.appendChild(help)}}return dialog.style.left=left+"px",dialog.style.top=top+"px",event.layerY>def_options.height-200&&(helper.style.maxHeight=def_options.height-event.layerY-20+"px"),input.focus(),options.show_all_on_open&&refreshHelper(),dialog},LGraphCanvas.prototype.showEditPropertyValue=function(node,property,options){if(node&&void 0!==node.properties[property]){options=options||{};var info=node.getPropertyInfo(property),type=info.type,input_html="";if("string"==type||"number"==type||"array"==type||"object"==type)input_html="";else if("enum"!=type&&"combo"!=type||!info.values){if("boolean"!=type&&"toggle"!=type)return void console.warn("unknown type: "+type);input_html=""}else{for(var i in input_html=""}var dialog=this.createDialog(""+(info.label||property)+""+input_html+"",options),input=!1;return"enum"!=type&&"combo"!=type||!info.values?"boolean"==type||"toggle"==type?(input=dialog.querySelector("input"))&&input.addEventListener("click",function(e){dialog.modified(),setValue(!!input.checked)}):(input=dialog.querySelector("input"))&&(input.addEventListener("blur",function(e){this.focus()}),v=void 0!==node.properties[property]?node.properties[property]:"","string"!==type&&(v=JSON.stringify(v)),input.value=v,input.addEventListener("keydown",function(e){if(27==e.keyCode)dialog.close();else if(13==e.keyCode)inner();else if(13!=e.keyCode)return void dialog.modified();e.preventDefault(),e.stopPropagation()})):(input=dialog.querySelector("select")).addEventListener("change",function(e){dialog.modified(),setValue(e.target.value)}),input&&input.focus(),dialog.querySelector("button").addEventListener("click",inner),dialog}function inner(){setValue(input.value)}function setValue(value){info&&info.values&&info.values.constructor===Object&&null!=info.values[value]&&(value=info.values[value]),"number"==typeof node.properties[property]&&(value=Number(value)),"array"!=type&&"object"!=type||(value=JSON.parse(value)),node.properties[property]=value,node.graph&&node.graph._version++,node.onPropertyChanged&&node.onPropertyChanged(property,value),options.onclose&&options.onclose(),dialog.close(),node.setDirtyCanvas(!0,!0)}},LGraphCanvas.prototype.createDialog=function(html,options){options=Object.assign({checkForInput:!1,closeOnLeave:!0,closeOnLeave_checkModified:!0},options||{});var dialog=document.createElement("div"),html=(dialog.className="graphdialog",dialog.innerHTML=html,dialog.is_modified=!1,this.canvas.getBoundingClientRect()),offsetx=-20,offsety=-20,dialogCloseTimer=(html&&(offsetx-=html.left,offsety-=html.top),options.position?(offsetx+=options.position[0],offsety+=options.position[1]):options.event?(offsetx+=options.event.clientX,offsety+=options.event.clientY):(offsetx+=.5*this.canvas.width,offsety+=.5*this.canvas.height),dialog.style.left=offsetx+"px",dialog.style.top=offsety+"px",this.canvas.parentNode.appendChild(dialog),options.checkForInput&&(html=[],html=dialog.querySelectorAll("input"))&&html.forEach(function(iX){iX.addEventListener("keydown",function(e){if(dialog.modified(),27==e.keyCode)dialog.close();else if(13!=e.keyCode)return;e.preventDefault(),e.stopPropagation()}),iX.focus()}),dialog.modified=function(){dialog.is_modified=!0},dialog.close=function(){dialog.parentNode&&dialog.parentNode.removeChild(dialog)},null),prevent_timeout=!1,offsetx=(dialog.addEventListener("mouseleave",function(e){prevent_timeout||(options.closeOnLeave||LiteGraph.dialog_close_on_mouse_leave)&&!dialog.is_modified&&LiteGraph.dialog_close_on_mouse_leave&&(dialogCloseTimer=setTimeout(dialog.close,LiteGraph.dialog_close_on_mouse_leave_delay))}),dialog.addEventListener("mouseenter",function(e){(options.closeOnLeave||LiteGraph.dialog_close_on_mouse_leave)&&dialogCloseTimer&&clearTimeout(dialogCloseTimer)}),dialog.querySelectorAll("select"));return offsetx&&offsetx.forEach(function(selIn){selIn.addEventListener("click",function(e){prevent_timeout++}),selIn.addEventListener("blur",function(e){prevent_timeout=0}),selIn.addEventListener("change",function(e){prevent_timeout=-1})}),dialog},LGraphCanvas.prototype.createPanel=function(title,options){var ref_window=(options=options||{}).window||window,root=document.createElement("div");return root.className="litegraph dialog",root.innerHTML="
",root.header=root.querySelector(".dialog-header"),options.width&&(root.style.width=options.width+(options.width.constructor===Number?"px":"")),options.height&&(root.style.height=options.height+(options.height.constructor===Number?"px":"")),options.closable&&((options=document.createElement("span")).innerHTML="✕",options.classList.add("close"),options.addEventListener("click",function(){root.close()}),root.header.appendChild(options)),root.title_element=root.querySelector(".dialog-title"),root.title_element.innerText=title,root.content=root.querySelector(".dialog-content"),root.alt_content=root.querySelector(".dialog-alt-content"),root.footer=root.querySelector(".dialog-footer"),root.close=function(){root.onClose&&"function"==typeof root.onClose&&root.onClose(),root.parentNode&&root.parentNode.removeChild(root),this.parentNode&&this.parentNode.removeChild(this)},root.toggleAltContent=function(force){var vTo;force=void 0!==force?(vTo=force?"block":"none",force?"none":"block"):(vTo="block"!=root.alt_content.style.display?"block":"none","block"!=root.alt_content.style.display?"none":"block"),root.alt_content.style.display=vTo,root.content.style.display=force},root.toggleFooterVisibility=function(force){force=void 0!==force?force?"block":"none":"block"!=root.footer.style.display?"block":"none",root.footer.style.display=force},root.clear=function(){this.content.innerHTML=""},root.addHTML=function(code,classname,on_footer){var elem=document.createElement("div");return classname&&(elem.className=classname),elem.innerHTML=code,(on_footer?root.footer:root.content).appendChild(elem),elem},root.addButton=function(name,callback,options){var elem=document.createElement("button");return elem.innerText=name,elem.options=options,elem.classList.add("btn"),elem.addEventListener("click",callback),root.footer.appendChild(elem),elem},root.addSeparator=function(){var elem=document.createElement("div");elem.className="separator",root.content.appendChild(elem)},root.addWidget=function(type,name,value,options,callback){options=options||{};var str_value=String(value),elem=("number"==(type=type.toLowerCase())&&(str_value=value.toFixed(3)),document.createElement("div")),value_element=(elem.className="property",elem.innerHTML="",elem.querySelector(".property_name").innerText=options.label||name,elem.querySelector(".property_value"));function innerChange(name,value){options.callback&&options.callback(name,value,options),callback&&callback(name,value,options)}return value_element.innerText=str_value,elem.dataset.property=name,elem.dataset.type=options.type||type,elem.options=options,elem.value=value,"code"==type?elem.addEventListener("click",function(e){root.inner_showCodePad(this.dataset.property)}):"boolean"==type?(elem.classList.add("boolean"),value&&elem.classList.add("bool-on"),elem.addEventListener("click",function(){var propname=this.dataset.property;this.value=!this.value,this.classList.toggle("bool-on"),this.querySelector(".property_value").innerText=this.value?"true":"false",innerChange(propname,this.value)})):"string"==type||"number"==type?(value_element.setAttribute("contenteditable",!0),value_element.addEventListener("keydown",function(e){"Enter"!=e.code||"string"==type&&e.shiftKey||(e.preventDefault(),this.blur())}),value_element.addEventListener("blur",function(){var v=this.innerText;innerChange(this.parentNode.dataset.property,v="number"==this.parentNode.dataset.type?Number(v):v)})):"enum"!=type&&"combo"!=type||(str_value=LGraphCanvas.getPropertyPrintableValue(value,options.values),value_element.innerText=str_value,value_element.addEventListener("click",function(event){var values=options.values||[],propname=this.parentNode.dataset.property,elem_that=this;new LiteGraph.ContextMenu(values,{event:event,className:"dark",callback:function(v,option,event){return elem_that.innerText=v,innerChange(propname,v),!1}},ref_window)})),root.content.appendChild(elem),elem},root.onOpen&&"function"==typeof root.onOpen&&root.onOpen(),root},LGraphCanvas.getPropertyPrintableValue=function(value,values){if(!values)return String(value);if(values.constructor===Array)return String(value);if(values.constructor===Object){var k,desc_value="";for(k in values)if(values[k]==value){desc_value=k;break}return String(value)+" ("+desc_value+")"}},LGraphCanvas.prototype.closePanels=function(){var panel=document.querySelector("#node-panel");panel&&panel.close(),(panel=document.querySelector("#option-panel"))&&panel.close()},LGraphCanvas.prototype.showShowGraphOptionsPanel=function(refOpts,obEv,refMenu,refMenu2){if(this.constructor&&"HTMLDivElement"==this.constructor.name){if(!(obEv&&obEv.event&&obEv.event.target&&obEv.event.target.lgraphcanvas))return void console.warn("Canvas not found");var graphcanvas=obEv.event.target.lgraphcanvas}else graphcanvas=this;graphcanvas.closePanels();obEv=graphcanvas.getCanvasWindow();function fUpdate(name,value,options){options&&options.key&&(name=options.key),options.values&&(value=Object.values(options.values).indexOf(value)),graphcanvas[name]=value}panel=graphcanvas.createPanel("Options",{closable:!0,window:obEv,onOpen:function(){graphcanvas.OPTIONPANEL_IS_OPEN=!0},onClose:function(){graphcanvas.OPTIONPANEL_IS_OPEN=!1,graphcanvas.options_panel=null}}),(graphcanvas.options_panel=panel).id="option-panel",panel.classList.add("settings"),panel.content.innerHTML="";var pI,aProps=LiteGraph.availableCanvasOptions;for(pI in aProps.sort(),aProps){var pX=aProps[pI];panel.addWidget("boolean",pX,graphcanvas[pX],{key:pX,on:"True",off:"False"},fUpdate)}graphcanvas.links_render_mode,panel.addWidget("combo","Render mode",LiteGraph.LINK_RENDER_MODES[graphcanvas.links_render_mode],{key:"links_render_mode",values:LiteGraph.LINK_RENDER_MODES},fUpdate),panel.addSeparator(),panel.footer.innerHTML="",graphcanvas.canvas.parentNode.appendChild(panel)},LGraphCanvas.prototype.showShowNodePanel=function(node){this.SELECTED_NODE=node,this.closePanels();var ref_window=this.getCanvasWindow(),graphcanvas=this,panel=this.createPanel(node.title||"",{closable:!0,window:ref_window,onOpen:function(){graphcanvas.NODEPANEL_IS_OPEN=!0},onClose:function(){graphcanvas.NODEPANEL_IS_OPEN=!1,graphcanvas.node_panel=null}});function inner_refresh(){panel.content.innerHTML="",panel.addHTML(""+node.type+""+(node.constructor.desc||"")+""),panel.addHTML("

Properties

");function fUpdate(name,value){switch(graphcanvas.graph.beforeChange(node),name){case"Title":node.title=value;break;case"Mode":var kV=Object.values(LiteGraph.NODE_MODES).indexOf(value);0<=kV&&LiteGraph.NODE_MODES[kV]?node.changeMode(kV):console.warn("unexpected mode: "+value);break;case"Color":LGraphCanvas.node_colors[value]?(node.color=LGraphCanvas.node_colors[value].color,node.bgcolor=LGraphCanvas.node_colors[value].bgcolor):console.warn("unexpected color: "+value);break;default:node.setProperty(name,value)}graphcanvas.graph.afterChange(),graphcanvas.dirty_canvas=!0}panel.addWidget("string","Title",node.title,{},fUpdate),panel.addWidget("combo","Mode",LiteGraph.NODE_MODES[node.mode],{values:LiteGraph.NODE_MODES},fUpdate);var pName,nodeCol="";for(pName in void 0!==node.color&&(nodeCol=Object.keys(LGraphCanvas.node_colors).filter(function(nK){return LGraphCanvas.node_colors[nK].color==node.color})),panel.addWidget("combo","Color",nodeCol,{values:Object.keys(LGraphCanvas.node_colors)},fUpdate),node.properties){var value=node.properties[pName],info=node.getPropertyInfo(pName);info.type;node.onAddPropertyToPanel&&node.onAddPropertyToPanel(pName,panel)||panel.addWidget(info.widget||info.type,pName,value,info,fUpdate)}panel.addSeparator(),node.onShowCustomPanelInfo&&node.onShowCustomPanelInfo(panel),panel.footer.innerHTML="",panel.addButton("Delete",function(){node.block_delete||(node.graph.remove(node),panel.close())}).classList.add("delete")}(graphcanvas.node_panel=panel).id="node-panel",panel.node=node,panel.classList.add("settings"),panel.inner_showCodePad=function(propname){panel.classList.remove("settings"),panel.classList.add("centered"),panel.alt_content.innerHTML="";function fDoneWith(){panel.toggleAltContent(!1),panel.toggleFooterVisibility(!0),textarea.parentNode.removeChild(textarea),panel.classList.add("settings"),panel.classList.remove("centered"),inner_refresh()}var textarea=panel.alt_content.querySelector("textarea"),assign=(textarea.value=node.properties[propname],textarea.addEventListener("keydown",function(e){"Enter"==e.code&&e.ctrlKey&&(node.setProperty(propname,textarea.value),fDoneWith())}),panel.toggleAltContent(!0),panel.toggleFooterVisibility(!1),textarea.style.height="calc(100% - 40px)",panel.addButton("Assign",function(){node.setProperty(propname,textarea.value),fDoneWith()})),assign=(panel.alt_content.appendChild(assign),panel.addButton("Close",fDoneWith));assign.style.float="right",panel.alt_content.appendChild(assign)},inner_refresh(),this.canvas.parentNode.appendChild(panel)},LGraphCanvas.prototype.showSubgraphPropertiesDialog=function(node){console.log("showing subgraph properties dialog");var old_panel=this.canvas.parentNode.querySelector(".subgraph_dialog"),panel=(old_panel&&old_panel.close(),this.createPanel("Subgraph Inputs",{closable:!0,width:500}));function inner_refresh(){if(panel.clear(),node.inputs)for(var i=0;i","subgraph_property")).dataset.name=input.name,elem.dataset.slot=i,elem.querySelector(".name").innerText=input.name,elem.querySelector(".type").innerText=input.type,elem.querySelector("button").addEventListener("click",function(e){node.removeInput(Number(this.parentNode.dataset.slot)),inner_refresh()}))}}panel.node=node,panel.classList.add("subgraph_dialog");return panel.addHTML(" + NameType","subgraph_property extra",!0).querySelector("button").addEventListener("click",function(e){var elem=this.parentNode,name=elem.querySelector(".name").value,type=elem.querySelector(".type").value;name&&-1==node.findInputSlot(name)&&(node.addInput(name,type),elem.querySelector(".name").value="",elem.querySelector(".type").value="",inner_refresh())}),inner_refresh(),this.canvas.parentNode.appendChild(panel),panel},LGraphCanvas.prototype.showSubgraphPropertiesDialogRight=function(node){var old_panel=this.canvas.parentNode.querySelector(".subgraph_dialog"),panel=(old_panel&&old_panel.close(),this.createPanel("Subgraph Outputs",{closable:!0,width:500}));function inner_refresh(){if(panel.clear(),node.outputs)for(var i=0;i","subgraph_property")).dataset.name=input.name,elem.dataset.slot=i,elem.querySelector(".name").innerText=input.name,elem.querySelector(".type").innerText=input.type,elem.querySelector("button").addEventListener("click",function(e){node.removeOutput(Number(this.parentNode.dataset.slot)),inner_refresh()}))}}panel.node=node,panel.classList.add("subgraph_dialog");old_panel=panel.addHTML(" + NameType","subgraph_property extra",!0);function addOutput(){var elem=this.parentNode,name=elem.querySelector(".name").value,type=elem.querySelector(".type").value;name&&-1==node.findOutputSlot(name)&&(node.addOutput(name,type),elem.querySelector(".name").value="",elem.querySelector(".type").value="",inner_refresh())}return old_panel.querySelector(".name").addEventListener("keydown",function(e){13==e.keyCode&&addOutput.apply(this)}),old_panel.querySelector("button").addEventListener("click",function(e){addOutput.apply(this)}),inner_refresh(),this.canvas.parentNode.appendChild(panel),panel},LGraphCanvas.prototype.checkPanels=function(){if(this.canvas)for(var panels=this.canvas.parentNode.querySelectorAll(".litegraph.dialog"),i=0;iNo color"}),LGraphCanvas.node_colors){var color=LGraphCanvas.node_colors[i],value={value:i,content:""+i+""};values.push(value)}return new LiteGraph.ContextMenu(values,{event:e,callback:function(v){if(node){function fApplyColor(node){color?node.constructor===LiteGraph.LGraphGroup?node.color=color.groupcolor:(node.color=color.color,node.bgcolor=color.bgcolor):(delete node.color,delete node.bgcolor)}var color=v.value?LGraphCanvas.node_colors[v.value]:null,graphcanvas=LGraphCanvas.active_canvas;if(!graphcanvas.selected_nodes||Object.keys(graphcanvas.selected_nodes).length<=1)fApplyColor(node);else for(var i in graphcanvas.selected_nodes)fApplyColor(graphcanvas.selected_nodes[i]);node.setDirtyCanvas(!0,!0)}},parentMenu:menu,node:node}),!1},LGraphCanvas.onMenuNodeShapes=function(value,options,e,menu,node){if(node)return new LiteGraph.ContextMenu(LiteGraph.VALID_SHAPES,{event:e,callback:function(v){if(node){node.graph.beforeChange();function fApplyMultiNode(node){node.shape=v}var graphcanvas=LGraphCanvas.active_canvas;if(!graphcanvas.selected_nodes||Object.keys(graphcanvas.selected_nodes).length<=1)fApplyMultiNode(node);else for(var i in graphcanvas.selected_nodes)fApplyMultiNode(graphcanvas.selected_nodes[i]);node.graph.afterChange(),node.setDirtyCanvas(!0)}},parentMenu:menu,node:node}),!1;throw"no node passed"},LGraphCanvas.onMenuNodeRemove=function(value,options,e,menu,node){if(!node)throw"no node passed";function fApplyMultiNode(node){!1!==node.removable&&graph.remove(node)}var graph=node.graph,graphcanvas=(graph.beforeChange(),LGraphCanvas.active_canvas);if(!graphcanvas.selected_nodes||Object.keys(graphcanvas.selected_nodes).length<=1)fApplyMultiNode(node);else for(var i in graphcanvas.selected_nodes)fApplyMultiNode(graphcanvas.selected_nodes[i]);graph.afterChange(),node.setDirtyCanvas(!0,!0)},LGraphCanvas.onMenuNodeToSubgraph=function(value,options,e,menu,node){var nodes_list,subgraph_node,graph=node.graph,graphcanvas=LGraphCanvas.active_canvas;graphcanvas&&((nodes_list=Object.values(graphcanvas.selected_nodes||{})).length||(nodes_list=[node]),(subgraph_node=LiteGraph.createNode("graph/subgraph")).pos=node.pos.concat(),graph.add(subgraph_node),subgraph_node.buildFromNodes(nodes_list),graphcanvas.deselectAllNodes(),node.setDirtyCanvas(!0,!0))},LGraphCanvas.onMenuNodeClone=function(value,options,e,menu,node){node.graph.beforeChange();function fApplyMultiNode(node){var newnode;!1!==node.clonable&&(newnode=node.clone())&&(newnode.pos=[node.pos[0]+5,node.pos[1]+5],node.graph.add(newnode),newSelected[newnode.id]=newnode)}var newSelected={},graphcanvas=LGraphCanvas.active_canvas;if(!graphcanvas.selected_nodes||Object.keys(graphcanvas.selected_nodes).length<=1)fApplyMultiNode(node);else for(var i in graphcanvas.selected_nodes)fApplyMultiNode(graphcanvas.selected_nodes[i]);Object.keys(newSelected).length&&graphcanvas.selectNodes(newSelected),node.graph.afterChange(),node.setDirtyCanvas(!0,!0)},LGraphCanvas.node_colors={red:{color:"#322",bgcolor:"#533",groupcolor:"#A88"},brown:{color:"#332922",bgcolor:"#593930",groupcolor:"#b06634"},green:{color:"#232",bgcolor:"#353",groupcolor:"#8A8"},blue:{color:"#223",bgcolor:"#335",groupcolor:"#88A"},pale_blue:{color:"#2a363b",bgcolor:"#3f5159",groupcolor:"#3f789e"},cyan:{color:"#233",bgcolor:"#355",groupcolor:"#8AA"},purple:{color:"#323",bgcolor:"#535",groupcolor:"#a1309b"},yellow:{color:"#432",bgcolor:"#653",groupcolor:"#b58b2a"},black:{color:"#222",bgcolor:"#000",groupcolor:"#444"}},LGraphCanvas.prototype.getCanvasMenuOptions=function(){var extra,options=null;return this.getMenuOptions?options=this.getMenuOptions():(options=[{content:"Add Node",has_submenu:!0,callback:LGraphCanvas.onMenuAdd},{content:"Add Group",callback:LGraphCanvas.onGroupAdd}],1Name",options),(input=dialog.querySelector("input"))&&slot_info&&(input.value=slot_info.label||""),inner=function(){node.graph.beforeChange(),input.value&&(slot_info&&(slot_info.label=input.value),that.setDirty(!0)),dialog.close(),node.graph.afterChange()},dialog.querySelector("button").addEventListener("click",inner),input.addEventListener("keydown",function(e){if(dialog.is_modified=!0,27==e.keyCode)dialog.close();else if(13==e.keyCode)inner();else if(13!=e.keyCode&&"textarea"!=e.target.localName)return;e.preventDefault(),e.stopPropagation()}),input.focus()))},extra:node},slot=(node&&(options.title=node.type),null);node&&(slot=node.getSlotInPosition(event.canvasX,event.canvasY),LGraphCanvas.active_node=node),slot?(menu_info=[],node.getSlotMenuOptions?menu_info=node.getSlotMenuOptions(slot):(slot&&slot.output&&slot.output.links&&slot.output.links.length&&menu_info.push({content:"Disconnect Links",slot:slot}),(_slot=slot.input||slot.output).removable&&menu_info.push(_slot.locked?"Cannot remove":{content:"Remove Slot",slot:slot}),_slot.nameLocked||menu_info.push({content:"Rename Slot",slot:slot})),options.title=(slot.input||slot.output).type||"*",slot.input&&slot.input.type==LiteGraph.ACTION&&(options.title="Action"),slot.output&&slot.output.type==LiteGraph.EVENT&&(options.title="Event")):node?menu_info=this.getNodeMenuOptions(node):(menu_info=this.getCanvasMenuOptions(),(_slot=this.graph.getGroupOnPos(event.canvasX,event.canvasY))&&menu_info.push(null,{content:"Edit Group",has_submenu:!0,submenu:{title:"Group",extra:_slot,options:this.getGroupMenuOptions(_slot)}})),menu_info&&new LiteGraph.ContextMenu(menu_info,options,ref_window)},LiteGraph.compareObjects=compareObjects,LiteGraph.distance=distance,LiteGraph.colorToString=colorToString,LiteGraph.isInsideRectangle=isInsideRectangle,LiteGraph.growBounding=growBounding,LiteGraph.isInsideBounding=isInsideBounding,LiteGraph.overlapBounding=overlapBounding,LiteGraph.hex2num=hex2num,LiteGraph.num2hex=num2hex,ContextMenu.prototype.addItem=function(name,value,options){var that=this,element=(options=options||{},document.createElement("div")),disabled=!(element.className="litemenu-entry submenu");function inner_onclick(e){var value=this.value,close_parent=!0;if((that.current_submenu&&that.current_submenu.close(e),options.callback&&!0===options.callback.call(this,value,options,e,that,options.node)&&(close_parent=!1),value)&&(value.callback&&!options.ignore_item_callbacks&&!0!==value.disabled&&!0===value.callback.call(this,value,options,e,that,options.extra)&&(close_parent=!1),value.submenu)){if(!value.submenu.options)throw"ContextMenu submenu needs options";new that.constructor(value.submenu.options,{callback:value.submenu.callback,event:e,parentMenu:that,ignore_item_callbacks:value.submenu.ignore_item_callbacks,title:value.submenu.title,extra:value.submenu.extra,autoopen:options.autoopen});close_parent=!1}close_parent&&!that.lock&&that.close()}return null===value?element.classList.add("separator"):(element.innerHTML=value&&value.title?value.title:name,(element.value=value)&&(value.disabled&&(disabled=!0,element.classList.add("disabled")),value.submenu||value.has_submenu)&&element.classList.add("has_submenu"),"function"==typeof value?(element.dataset.value=name,element.onclick_callback=value):element.dataset.value=value,value.className&&(element.className+=" "+value.className)),this.root.appendChild(element),disabled||element.addEventListener("click",inner_onclick),!disabled&&options.autoopen&&LiteGraph.pointerListenerAdd(element,"enter",function(e){var value=this.value;value&&value.has_submenu&&inner_onclick.call(this,e)}),element},ContextMenu.prototype.close=function(e,ignore_parent_menu){this.root.parentNode&&this.root.parentNode.removeChild(this.root),this.parentMenu&&!ignore_parent_menu&&(this.parentMenu.lock=!1,this.parentMenu.current_submenu=null,void 0===e?this.parentMenu.close():e&&!ContextMenu.isCursorOverElement(e,this.parentMenu.root)&&ContextMenu.trigger(this.parentMenu.root,LiteGraph.pointerevents_method+"leave",e)),this.current_submenu&&this.current_submenu.close(e,!0),this.root.closing_timer&&clearTimeout(this.root.closing_timer)},ContextMenu.trigger=function(element,event_name,params,origin){var evt=document.createEvent("CustomEvent");return evt.initCustomEvent(event_name,!0,!0,params),evt.srcElement=origin,element.dispatchEvent?element.dispatchEvent(evt):element.__events&&element.__events.dispatchEvent(evt),evt},ContextMenu.prototype.getTopMenu=function(){return this.options.parentMenu?this.options.parentMenu.getTopMenu():this},ContextMenu.prototype.getFirstEvent=function(){return this.options.parentMenu?this.options.parentMenu.getFirstEvent():this.options.event},ContextMenu.isCursorOverElement=function(event,element){var left=event.clientX,event=event.clientY,element=element.getBoundingClientRect();return!!element&&event>element.top&&eventelement.left&&leftthis.size[0]+10||localpos[1]<-10||localpos[1]>this.size[1]+10)?(points.splice(s,1),this.selected=-1):(curvepos[0]=graphcanvas?0==s?0:1:clamp(x,0,1),curvepos[1]=1-clamp(y,0,1),points.sort(function(a,b){return a[0]-b[0]}),this.selected=points.indexOf(curvepos),this.must_update=!0)))},CurveEditor.prototype.onMouseUp=function(localpos,graphcanvas){return!(this.selected=-1)},CurveEditor.prototype.getCloserPoint=function(pos,max_dist){var points=this.points;if(!points)return-1;max_dist=max_dist||30;for(var w=this.size[0]-2*this.margin,h=this.size[1]-2*this.margin,num=points.length,p2=[0,0],min_dist=1e6,closest=-1,i=0;iy&&(localpos[0]idMap.linkIDs[l]))}for(const node of graph.nodes){var merge;"graph/subgraph"===node.type&&(merge=reassignGraphUUIDs(node.subgraph),idMap.nodeIDs.assign(merge.nodeIDs),idMap.linkIDs.assign(merge.linkIDs))}},Subgraph.prototype.clone=function(){var subgraph,node=LiteGraph.createNode(this.type),data=this.serialize();return LiteGraph.use_uuids&&(subgraph=LiteGraph.cloneObject(data.subgraph),this.reassignSubgraphUUIDs(subgraph),data.subgraph=subgraph),delete data.id,delete data.inputs,delete data.outputs,node.configure(data),node},Subgraph.prototype.buildFromNodes=function(nodes){for(var ids={},min_x=0,i=0;i.5*this.size[1]?-1:1,this.properties.value=clamp(this.properties.value+e*this.properties.step,this.properties.min,this.properties.max),this.graph._version++,this.setDirtyCanvas(!0)),this.mouse_captured&&(this.mouse_captured=!1,this.captureInput(!1))},LiteGraph.registerNodeType("widget/number",WidgetNumber),WidgetCombo.title="Combo",WidgetCombo.desc="Widget to select from a list",WidgetCombo.prototype.onExecute=function(){this.setOutputData(0,this.properties.value)},WidgetCombo.prototype.onPropertyChanged=function(name,value){"values"==name?(this._values=value.split(";"),this.widget.options.values=this._values):"value"==name&&(this.widget.value=value)},LiteGraph.registerNodeType("widget/combo",WidgetCombo),WidgetKnob.title="Knob",WidgetKnob.desc="Circular controller",WidgetKnob.size=[80,100],WidgetKnob.prototype.onDrawForeground=function(ctx){var center_x,center_y,radius,angle;this.flags.collapsed||(-1==this.value&&(this.value=(this.properties.value-this.properties.min)/(this.properties.max-this.properties.min)),center_x=.5*this.size[0],center_y=.5*this.size[1],radius=.5*Math.min(this.size[0],this.size[1])-5,Math.floor(.05*radius),ctx.globalAlpha=1,ctx.save(),ctx.translate(center_x,center_y),ctx.rotate(.75*Math.PI),ctx.fillStyle="rgba(0,0,0,0.5)",ctx.beginPath(),ctx.moveTo(0,0),ctx.arc(0,0,radius,0,1.5*Math.PI),ctx.fill(),ctx.strokeStyle="black",ctx.fillStyle=this.properties.color,ctx.lineWidth=2,ctx.beginPath(),ctx.moveTo(0,0),ctx.arc(0,0,radius-4,0,1.5*Math.PI*Math.max(.01,this.value)),ctx.closePath(),ctx.fill(),ctx.lineWidth=1,ctx.globalAlpha=1,ctx.restore(),ctx.fillStyle="black",ctx.beginPath(),ctx.arc(center_x,center_y,.75*radius,0,2*Math.PI,!0),ctx.fill(),ctx.fillStyle=this.mouseOver?"white":this.properties.color,ctx.beginPath(),angle=this.value*Math.PI*1.5+.75*Math.PI,ctx.arc(center_x+Math.cos(angle)*radius*.65,center_y+Math.sin(angle)*radius*.65,.05*radius,0,2*Math.PI,!0),ctx.fill(),ctx.fillStyle=this.mouseOver?"white":"#AAA",ctx.font=Math.floor(.5*radius)+"px Arial",ctx.textAlign="center",ctx.fillText(this.properties.value.toFixed(this.properties.precision),center_x,center_y+.15*radius))},WidgetKnob.prototype.onExecute=function(){this.setOutputData(0,this.properties.value),this.boxcolor=LiteGraph.colorToString([this.value,this.value,this.value])},WidgetKnob.prototype.onMouseDown=function(e){return this.center=[.5*this.size[0],.5*this.size[1]+20],this.radius=.5*this.size[0],!(e.canvasY-this.pos[1]<20||LiteGraph.distance([e.canvasX,e.canvasY],[this.pos[0]+this.center[0],this.pos[1]+this.center[1]])>this.radius||(this.oldmouse=[e.canvasX-this.pos[0],e.canvasY-this.pos[1]],this.captureInput(!0),0))},WidgetKnob.prototype.onMouseMove=function(e){var v;this.oldmouse&&(e=[e.canvasX-this.pos[0],e.canvasY-this.pos[1]],v=this.value,1<(v-=.01*(e[1]-this.oldmouse[1]))?v=1:v<0&&(v=0),this.value=v,this.properties.value=this.properties.min+(this.properties.max-this.properties.min)*this.value,this.oldmouse=e,this.setDirtyCanvas(!0))},WidgetKnob.prototype.onMouseUp=function(e){this.oldmouse&&(this.oldmouse=null,this.captureInput(!1))},WidgetKnob.prototype.onPropertyChanged=function(name,value){if("min"==name||"max"==name||"value"==name)return this.properties[name]=parseFloat(value),!0},LiteGraph.registerNodeType("widget/knob",WidgetKnob),WidgetSliderGUI.title="Inner Slider",WidgetSliderGUI.prototype.onPropertyChanged=function(name,value){"value"==name&&(this.slider.value=value)},WidgetSliderGUI.prototype.onExecute=function(){this.setOutputData(0,this.properties.value)},LiteGraph.registerNodeType("widget/internal_slider",WidgetSliderGUI),WidgetHSlider.title="H.Slider",WidgetHSlider.desc="Linear slider controller",WidgetHSlider.prototype.onDrawForeground=function(ctx){-1==this.value&&(this.value=(this.properties.value-this.properties.min)/(this.properties.max-this.properties.min)),ctx.globalAlpha=1,ctx.lineWidth=1,ctx.fillStyle="#000",ctx.fillRect(2,2,this.size[0]-4,this.size[1]-4),ctx.fillStyle=this.properties.color,ctx.beginPath(),ctx.rect(4,4,(this.size[0]-8)*this.value,this.size[1]-8),ctx.fill()},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=LiteGraph.colorToString([this.value,this.value,this.value])},WidgetHSlider.prototype.onMouseDown=function(e){return!(e.canvasY-this.pos[1]<0||(this.oldmouse=[e.canvasX-this.pos[0],e.canvasY-this.pos[1]],this.captureInput(!0),0))},WidgetHSlider.prototype.onMouseMove=function(e){var v;this.oldmouse&&(e=[e.canvasX-this.pos[0],e.canvasY-this.pos[1]],v=this.value,1<(v+=(e[0]-this.oldmouse[0])/this.size[0])?v=1:v<0&&(v=0),this.value=v,this.oldmouse=e,this.setDirtyCanvas(!0))},WidgetHSlider.prototype.onMouseUp=function(e){this.oldmouse=null,this.captureInput(!1)},WidgetHSlider.prototype.onMouseLeave=function(e){},LiteGraph.registerNodeType("widget/hslider",WidgetHSlider),WidgetProgress.title="Progress",WidgetProgress.desc="Shows data in linear progress",WidgetProgress.prototype.onExecute=function(){var v=this.getInputData(0);null!=v&&(this.properties.value=v)},WidgetProgress.prototype.onDrawForeground=function(ctx){ctx.lineWidth=1,ctx.fillStyle=this.properties.color;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),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=this.properties.color;var v=this.properties.value,fontsize=(this.properties.glowSize?(ctx.shadowColor=this.properties.color,ctx.shadowOffsetX=0,ctx.shadowOffsetY=0,ctx.shadowBlur=this.properties.glowSize):ctx.shadowColor="transparent",this.properties.fontsize);if(ctx.textAlign=this.properties.align,ctx.font=fontsize.toString()+"px "+this.properties.font,this.str="number"==typeof v?v.toFixed(this.properties.decimals):v,"string"==typeof this.str)for(var lines=this.str.replace(/[\r\n]/g,"\\n").split("\\n"),i=0;ithreshold?gamepad.xbox.axes.lx:0,this._left_axis[1]=Math.abs(gamepad.xbox.axes.ly)>threshold?gamepad.xbox.axes.ly:0,this._right_axis[0]=Math.abs(gamepad.xbox.axes.rx)>threshold?gamepad.xbox.axes.rx:0,this._right_axis[1]=Math.abs(gamepad.xbox.axes.ry)>threshold?gamepad.xbox.axes.ry:0,this._triggers[0]=Math.abs(gamepad.xbox.axes.ltrigger)>threshold?gamepad.xbox.axes.ltrigger:0,this._triggers[1]=Math.abs(gamepad.xbox.axes.rtrigger)>threshold?gamepad.xbox.axes.rtrigger:0),this.outputs)for(var i=0;i","enum",{values:MathCondition.values}),this.addWidget("combo","Cond.",this.properties.OP,{property:"OP",values:MathCondition.values}),this.size=[80,60]}function MathBranch(){this.addInput("in",0),this.addInput("cond","boolean"),this.addOutput("true",0),this.addOutput("false",0),this.size=[80,60]}function MathAccumulate(){this.addInput("inc","number"),this.addOutput("total","number"),this.addProperty("increment",1),this.addProperty("value",0)}function MathTrigonometry(){this.addInput("v","number"),this.addOutput("sin","number"),this.addProperty("amplitude",1),this.addProperty("offset",0),this.bgImageUrl="nodes/imgs/icon-sin.png"}function MathFormula(){this.addInput("x","number"),this.addInput("y","number"),this.addOutput("","number"),this.properties={x:1,y:1,formula:"x+y"},this.code_widget=this.addWidget("text","F(x,y)",this.properties.formula,function(v,canvas,node){node.properties.formula=v}),this.addWidget("toggle","allow",LiteGraph.allow_scripts,function(v){LiteGraph.allow_scripts=v}),this._func=null}function Math3DVec2ToXY(){this.addInput("vec2","vec2"),this.addOutput("x","number"),this.addOutput("y","number")}function Math3DXYToVec2(){this.addInputs([["x","number"],["y","number"]]),this.addOutput("vec2","vec2"),this.properties={x:0,y:0},this._data=new Float32Array(2)}function Math3DVec3ToXYZ(){this.addInput("vec3","vec3"),this.addOutput("x","number"),this.addOutput("y","number"),this.addOutput("z","number")}function Math3DXYZToVec3(){this.addInputs([["x","number"],["y","number"],["z","number"]]),this.addOutput("vec3","vec3"),this.properties={x:0,y:0,z:0},this._data=new Float32Array(3)}function Math3DVec4ToXYZW(){this.addInput("vec4","vec4"),this.addOutput("x","number"),this.addOutput("y","number"),this.addOutput("z","number"),this.addOutput("w","number")}function Math3DXYZWToVec4(){this.addInputs([["x","number"],["y","number"],["z","number"],["w","number"]]),this.addOutput("vec4","vec4"),this.properties={x:0,y:0,z:0,w:0},this._data=new Float32Array(4)}Converter.title="Converter",Converter.desc="type A to type B",Converter.prototype.onExecute=function(){var v=this.getInputData(0);if(null!=v&&this.outputs)for(var i=0;inum_samples&&(this._current=0),0),i=0;iB":value=B=B":value=B<=A}this.setOutputData(i,value)}}},MathCompare.prototype.onGetOutputs=function(){return[["A==B","boolean"],["A!=B","boolean"],["A>B","boolean"],["A=B","boolean"],["A<=B","boolean"]]},LiteGraph.registerNodeType("math/compare",MathCompare),LiteGraph.registerSearchboxExtra("math/compare","==",{outputs:[["A==B","boolean"]],title:"A==B"}),LiteGraph.registerSearchboxExtra("math/compare","!=",{outputs:[["A!=B","boolean"]],title:"A!=B"}),LiteGraph.registerSearchboxExtra("math/compare",">",{outputs:[["A>B","boolean"]],title:"A>B"}),LiteGraph.registerSearchboxExtra("math/compare","<",{outputs:[["A=",{outputs:[["A>=B","boolean"]],title:"A>=B"}),LiteGraph.registerSearchboxExtra("math/compare","<=",{outputs:[["A<=B","boolean"]],title:"A<=B"}),MathCondition["@OP"]={type:"enum",title:"operation",values:MathCondition.values=[">","<","==","!=","<=",">=","||","&&"]},MathCondition.title="Condition",MathCondition.desc="evaluates condition between A and B",MathCondition.prototype.getTitle=function(){return"A "+this.properties.OP+" B"},MathCondition.prototype.onExecute=function(){var A=this.getInputData(0),B=(void 0===A?A=this.properties.A:this.properties.A=A,this.getInputData(1)),result=(void 0===B?B=this.properties.B:this.properties.B=B,!0);switch(this.properties.OP){case">":result=B=":result=B<=A;break;case"||":result=A||B;break;case"&&":result=A&&B}this.setOutputData(0,result),this.setOutputData(1,!result)},LiteGraph.registerNodeType("math/condition",MathCondition),MathBranch.title="Branch",MathBranch.desc="If condition is true, outputs IN in true, otherwise in false",MathBranch.prototype.onExecute=function(){var V=this.getInputData(0);this.getInputData(1)?(this.setOutputData(0,V),this.setOutputData(1,null)):(this.setOutputData(0,null),this.setOutputData(1,V))},LiteGraph.registerNodeType("math/branch",MathBranch),MathAccumulate.title="Accumulate",MathAccumulate.desc="Increments a value every time",MathAccumulate.prototype.onExecute=function(){null===this.properties.value&&(this.properties.value=0);var inc=this.getInputData(0);this.properties.value+=null!==inc?inc:this.properties.increment,this.setOutputData(0,this.properties.value)},LiteGraph.registerNodeType("math/accumulate",MathAccumulate),MathTrigonometry.title="Trigonometry",MathTrigonometry.desc="Sin Cos Tan",MathTrigonometry.prototype.onExecute=function(){var v=this.getInputData(0),amplitude=(null==v&&(v=0),this.properties.amplitude),slot=this.findInputSlot("amplitude"),offset=(-1!=slot&&(amplitude=this.getInputData(slot)),this.properties.offset);-1!=(slot=this.findInputSlot("offset"))&&(offset=this.getInputData(slot));for(var value,i=0,l=this.outputs.length;iXY",Math3DVec2ToXY.desc="vector 2 to components",Math3DVec2ToXY.prototype.onExecute=function(){var v=this.getInputData(0);null!=v&&(this.setOutputData(0,v[0]),this.setOutputData(1,v[1]))},LiteGraph.registerNodeType("math3d/vec2-to-xy",Math3DVec2ToXY),Math3DXYToVec2.title="XY->Vec2",Math3DXYToVec2.desc="components to vector2",Math3DXYToVec2.prototype.onExecute=function(){var x=this.getInputData(0),y=(null==x&&(x=this.properties.x),this.getInputData(1)),data=(null==y&&(y=this.properties.y),this._data);data[0]=x,data[1]=y,this.setOutputData(0,data)},LiteGraph.registerNodeType("math3d/xy-to-vec2",Math3DXYToVec2),Math3DVec3ToXYZ.title="Vec3->XYZ",Math3DVec3ToXYZ.desc="vector 3 to components",Math3DVec3ToXYZ.prototype.onExecute=function(){var v=this.getInputData(0);null!=v&&(this.setOutputData(0,v[0]),this.setOutputData(1,v[1]),this.setOutputData(2,v[2]))},LiteGraph.registerNodeType("math3d/vec3-to-xyz",Math3DVec3ToXYZ),Math3DXYZToVec3.title="XYZ->Vec3",Math3DXYZToVec3.desc="components to vector3",Math3DXYZToVec3.prototype.onExecute=function(){var x=this.getInputData(0),y=(null==x&&(x=this.properties.x),this.getInputData(1)),z=(null==y&&(y=this.properties.y),this.getInputData(2)),data=(null==z&&(z=this.properties.z),this._data);data[0]=x,data[1]=y,data[2]=z,this.setOutputData(0,data)},LiteGraph.registerNodeType("math3d/xyz-to-vec3",Math3DXYZToVec3),Math3DVec4ToXYZW.title="Vec4->XYZW",Math3DVec4ToXYZW.desc="vector 4 to components",Math3DVec4ToXYZW.prototype.onExecute=function(){var v=this.getInputData(0);null!=v&&(this.setOutputData(0,v[0]),this.setOutputData(1,v[1]),this.setOutputData(2,v[2]),this.setOutputData(3,v[3]))},LiteGraph.registerNodeType("math3d/vec4-to-xyzw",Math3DVec4ToXYZW),Math3DXYZWToVec4.title="XYZW->Vec4",Math3DXYZWToVec4.desc="components to vector4",Math3DXYZWToVec4.prototype.onExecute=function(){var x=this.getInputData(0),y=(null==x&&(x=this.properties.x),this.getInputData(1)),z=(null==y&&(y=this.properties.y),this.getInputData(2)),w=(null==z&&(z=this.properties.z),this.getInputData(3)),data=(null==w&&(w=this.properties.w),this._data);data[0]=x,data[1]=y,data[2]=z,data[3]=w,this.setOutputData(0,data)},LiteGraph.registerNodeType("math3d/xyzw-to-vec4",Math3DXYZWToVec4)}(this); + +!function(global){var LiteGraph=global.LiteGraph;function Math3DMat4(){this.addInput("T","vec3"),this.addInput("R","vec3"),this.addInput("S","vec3"),this.addOutput("mat4","mat4"),this.properties={T:[0,0,0],R:[0,0,0],S:[1,1,1],R_in_degrees:!0},this._result=mat4.create(),this._must_update=!0}function Math3DOperation(){this.addInput("A","number,vec3"),this.addInput("B","number,vec3"),this.addOutput("=","number,vec3"),this.addProperty("OP","+","enum",{values:Math3DOperation.values}),this._result=vec3.create()}function Math3DVec3Scale(){this.addInput("in","vec3"),this.addInput("f","number"),this.addOutput("out","vec3"),this.properties={f:1},this._data=new Float32Array(3)}function Math3DVec3Length(){this.addInput("in","vec3"),this.addOutput("out","number")}function Math3DVec3Normalize(){this.addInput("in","vec3"),this.addOutput("out","vec3"),this._data=new Float32Array(3)}function Math3DVec3Lerp(){this.addInput("A","vec3"),this.addInput("B","vec3"),this.addInput("f","vec3"),this.addOutput("out","vec3"),this.properties={f:.5},this._data=new Float32Array(3)}function Math3DVec3Dot(){this.addInput("A","vec3"),this.addInput("B","vec3"),this.addOutput("out","number")}function Math3DQuaternion(){this.addOutput("quat","quat"),this.properties={x:0,y:0,z:0,w:1,normalize:!1},this._value=quat.create()}function Math3DRotation(){this.addInputs([["degrees","number"],["axis","vec3"]]),this.addOutput("quat","quat"),this.properties={angle:90,axis:vec3.fromValues(0,1,0)},this._value=quat.create()}function MathEulerToQuat(){this.addInput("euler","vec3"),this.addOutput("quat","quat"),this.properties={euler:[0,0,0],use_yaw_pitch_roll:!1},this._degs=vec3.create(),this._value=quat.create()}function MathQuatToEuler(){this.addInput(["quat","quat"]),this.addOutput("euler","vec3"),this._value=vec3.create()}function Math3DRotateVec3(){this.addInputs([["vec3","vec3"],["quat","quat"]]),this.addOutput("result","vec3"),this.properties={vec:[0,0,1]}}function Math3DMultQuat(){this.addInputs([["A","quat"],["B","quat"]]),this.addOutput("A*B","quat"),this._value=quat.create()}function Math3DQuatSlerp(){this.addInputs([["A","quat"],["B","quat"],["factor","number"]]),this.addOutput("slerp","quat"),this.addProperty("factor",.5),this._value=quat.create()}function Math3DRemapRange(){this.addInput("vec3","vec3"),this.addOutput("remap","vec3"),this.addOutput("clamped","vec3"),this.properties={clamp:!0,range_min:[-1,-1,0],range_max:[1,1,0],target_min:[-1,-1,0],target_max:[1,1,0]},this._value=vec3.create(),this._clamped=vec3.create()}Math3DMat4.title="mat4",Math3DMat4.temp_quat=new Float32Array([0,0,0,1]),Math3DMat4.temp_mat4=new Float32Array(16),Math3DMat4.temp_vec3=new Float32Array(3),Math3DMat4.prototype.onPropertyChanged=function(name,value){this._must_update=!0},Math3DMat4.prototype.onExecute=function(){var M=this._result,Q=Math3DMat4.temp_quat,temp_mat4=Math3DMat4.temp_mat4,temp_vec3=Math3DMat4.temp_vec3,T=this.getInputData(0),R=this.getInputData(1),S=this.getInputData(2);(this._must_update||T||R||S)&&(T=T||this.properties.T,R=R||this.properties.R,S=S||this.properties.S,mat4.identity(M),mat4.translate(M,M,T),this.properties.R_in_degrees?(temp_vec3.set(R),vec3.scale(temp_vec3,temp_vec3,DEG2RAD),quat.fromEuler(Q,temp_vec3)):quat.fromEuler(Q,R),mat4.fromQuat(temp_mat4,Q),mat4.multiply(M,M,temp_mat4),mat4.scale(M,M,S)),this.setOutputData(0,M)},LiteGraph.registerNodeType("math3d/mat4",Math3DMat4),Math3DOperation.values=["+","-","*","/","%","^","max","min","dot","cross"],LiteGraph.registerSearchboxExtra("math3d/operation","CROSS()",{properties:{OP:"cross"},title:"CROSS()"}),LiteGraph.registerSearchboxExtra("math3d/operation","DOT()",{properties:{OP:"dot"},title:"DOT()"}),Math3DOperation.title="Operation",Math3DOperation.desc="Easy math 3D operators",Math3DOperation["@OP"]={type:"enum",title:"operation",values:Math3DOperation.values},Math3DOperation.size=[100,60],Math3DOperation.prototype.getTitle=function(){return"max"==this.properties.OP||"min"==this.properties.OP?this.properties.OP+"(A,B)":"A "+this.properties.OP+" B"},Math3DOperation.prototype.onExecute=function(){var A=this.getInputData(0),B=this.getInputData(1);if(null!=A&&null!=B){A.constructor===Number&&(A=[A,A,A]),B.constructor===Number&&(B=[B,B,B]);var result=this._result;switch(this.properties.OP){case"+":result=vec3.add(result,A,B);break;case"-":result=vec3.sub(result,A,B);break;case"x":case"X":case"*":result=vec3.mul(result,A,B);break;case"/":result=vec3.div(result,A,B);break;case"%":result[0]=A[0]%B[0],result[1]=A[1]%B[1],result[2]=A[2]%B[2];break;case"^":result[0]=Math.pow(A[0],B[0]),result[1]=Math.pow(A[1],B[1]),result[2]=Math.pow(A[2],B[2]);break;case"max":result[0]=Math.max(A[0],B[0]),result[1]=Math.max(A[1],B[1]),result[2]=Math.max(A[2],B[2]);break;case"min":result[0]=Math.min(A[0],B[0]),result[1]=Math.min(A[1],B[1]),result[2]=Math.min(A[2],B[2]);break;case"dot":result=vec3.dot(A,B);break;case"cross":vec3.cross(result,A,B);break;default:console.warn("Unknown operation: "+this.properties.OP)}this.setOutputData(0,result)}},Math3DOperation.prototype.onDrawBackground=function(ctx){this.flags.collapsed||(ctx.font="40px Arial",ctx.fillStyle="#666",ctx.textAlign="center",ctx.fillText(this.properties.OP,.5*this.size[0],.5*(this.size[1]+LiteGraph.NODE_TITLE_HEIGHT)),ctx.textAlign="left")},LiteGraph.registerNodeType("math3d/operation",Math3DOperation),Math3DVec3Scale.title="vec3_scale",Math3DVec3Scale.desc="scales the components of a vec3",Math3DVec3Scale.prototype.onExecute=function(){var f,data,v=this.getInputData(0);null!=v&&(null==(f=this.getInputData(1))&&(f=this.properties.f),(data=this._data)[0]=v[0]*f,data[1]=v[1]*f,data[2]=v[2]*f,this.setOutputData(0,data))},LiteGraph.registerNodeType("math3d/vec3-scale",Math3DVec3Scale),Math3DVec3Length.title="vec3_length",Math3DVec3Length.desc="returns the module of a vector",Math3DVec3Length.prototype.onExecute=function(){var v=this.getInputData(0);null!=v&&(v=Math.sqrt(v[0]*v[0]+v[1]*v[1]+v[2]*v[2]),this.setOutputData(0,v))},LiteGraph.registerNodeType("math3d/vec3-length",Math3DVec3Length),Math3DVec3Normalize.title="vec3_normalize",Math3DVec3Normalize.desc="returns the vector normalized",Math3DVec3Normalize.prototype.onExecute=function(){var dist,data,v=this.getInputData(0);null!=v&&(dist=Math.sqrt(v[0]*v[0]+v[1]*v[1]+v[2]*v[2]),(data=this._data)[0]=v[0]/dist,data[1]=v[1]/dist,data[2]=v[2]/dist,this.setOutputData(0,data))},LiteGraph.registerNodeType("math3d/vec3-normalize",Math3DVec3Normalize),Math3DVec3Lerp.title="vec3_lerp",Math3DVec3Lerp.desc="returns the interpolated vector",Math3DVec3Lerp.prototype.onExecute=function(){var B,f,data,A=this.getInputData(0);null!=A&&null!=(B=this.getInputData(1))&&(f=this.getInputOrProperty("f"),(data=this._data)[0]=A[0]*(1-f)+B[0]*f,data[1]=A[1]*(1-f)+B[1]*f,data[2]=A[2]*(1-f)+B[2]*f,this.setOutputData(0,data))},LiteGraph.registerNodeType("math3d/vec3-lerp",Math3DVec3Lerp),Math3DVec3Dot.title="vec3_dot",Math3DVec3Dot.desc="returns the dot product",Math3DVec3Dot.prototype.onExecute=function(){var B,A=this.getInputData(0);null!=A&&null!=(B=this.getInputData(1))&&(A=A[0]*B[0]+A[1]*B[1]+A[2]*B[2],this.setOutputData(0,A))},LiteGraph.registerNodeType("math3d/vec3-dot",Math3DVec3Dot),global.glMatrix?(Math3DQuaternion.title="Quaternion",Math3DQuaternion.desc="quaternion",Math3DQuaternion.prototype.onExecute=function(){this._value[0]=this.getInputOrProperty("x"),this._value[1]=this.getInputOrProperty("y"),this._value[2]=this.getInputOrProperty("z"),this._value[3]=this.getInputOrProperty("w"),this.properties.normalize&&quat.normalize(this._value,this._value),this.setOutputData(0,this._value)},Math3DQuaternion.prototype.onGetInputs=function(){return[["x","number"],["y","number"],["z","number"],["w","number"]]},LiteGraph.registerNodeType("math3d/quaternion",Math3DQuaternion),Math3DRotation.title="Rotation",Math3DRotation.desc="quaternion rotation",Math3DRotation.prototype.onExecute=function(){var angle=this.getInputData(0),axis=(null==angle&&(angle=this.properties.angle),this.getInputData(1)),axis=(null==axis&&(axis=this.properties.axis),quat.setAxisAngle(this._value,axis,.0174532925*angle));this.setOutputData(0,axis)},LiteGraph.registerNodeType("math3d/rotation",Math3DRotation),MathEulerToQuat.title="Euler->Quat",MathEulerToQuat.desc="Converts euler angles (in degrees) to quaternion",MathEulerToQuat.prototype.onExecute=function(){var euler=this.getInputData(0),euler=(null==euler&&(euler=this.properties.euler),vec3.scale(this._degs,euler,DEG2RAD),this.properties.use_yaw_pitch_roll&&(this._degs=[this._degs[2],this._degs[0],this._degs[1]]),quat.fromEuler(this._value,this._degs));this.setOutputData(0,euler)},LiteGraph.registerNodeType("math3d/euler_to_quat",MathEulerToQuat),MathQuatToEuler.title="Euler->Quat",MathQuatToEuler.desc="Converts rotX,rotY,rotZ in degrees to quat",MathQuatToEuler.prototype.onExecute=function(){var q=this.getInputData(0);q&&(quat.toEuler(this._value,q),vec3.scale(this._value,this._value,DEG2RAD),this.setOutputData(0,this._value))},LiteGraph.registerNodeType("math3d/quat_to_euler",MathQuatToEuler),Math3DRotateVec3.title="Rot. Vec3",Math3DRotateVec3.desc="rotate a point",Math3DRotateVec3.prototype.onExecute=function(){var vec=this.getInputData(0),quat=(null==vec&&(vec=this.properties.vec),this.getInputData(1));null==quat?this.setOutputData(vec):this.setOutputData(0,vec3.transformQuat(vec3.create(),vec,quat))},LiteGraph.registerNodeType("math3d/rotate_vec3",Math3DRotateVec3),Math3DMultQuat.title="Mult. Quat",Math3DMultQuat.desc="rotate quaternion",Math3DMultQuat.prototype.onExecute=function(){var B,A=this.getInputData(0);null!=A&&null!=(B=this.getInputData(1))&&(A=quat.multiply(this._value,A,B),this.setOutputData(0,A))},LiteGraph.registerNodeType("math3d/mult-quat",Math3DMultQuat),Math3DQuatSlerp.title="Quat Slerp",Math3DQuatSlerp.desc="quaternion spherical interpolation",Math3DQuatSlerp.prototype.onExecute=function(){var B,factor,A=this.getInputData(0);null!=A&&null!=(B=this.getInputData(1))&&(factor=this.properties.factor,null!=this.getInputData(2)&&(factor=this.getInputData(2)),A=quat.slerp(this._value,A,B,factor),this.setOutputData(0,A))},LiteGraph.registerNodeType("math3d/quat-slerp",Math3DQuatSlerp),Math3DRemapRange.title="Remap Range",Math3DRemapRange.desc="remap a 3D range",Math3DRemapRange.prototype.onExecute=function(){for(var vec=this.getInputData(0),range_min=(vec&&this._value.set(vec),this.properties.range_min),range_max=this.properties.range_max,target_min=this.properties.target_min,target_max=this.properties.target_max,i=0;i<3;++i){var t,r=range_max[i]-range_min[i];this._clamped[i]=clamp(this._value[i],range_min[i],range_max[i]),0==r?this._value[i]=.5*(target_min[i]+target_max[i]):(r=(this._value[i]-range_min[i])/r,t=(this.properties.clamp&&(r=clamp(r,0,1)),target_max[i]-target_min[i]),this._value[i]=target_min[i]+r*t)}this.setOutputData(0,this._value),this.setOutputData(1,this._clamped)},LiteGraph.registerNodeType("math3d/remap_range",Math3DRemapRange)):LiteGraph.debug&&console.warn("No glmatrix found, some Math3D nodes may not work")}(this); + +!function(global){global=global.LiteGraph;function StringToTable(){this.addInput("","string"),this.addOutput("table","table"),this.addOutput("rows","number"),this.addProperty("value",""),this.addProperty("separator",","),this._table=null}global.wrapFunctionAsNode("string/toString",function(a){if(a&&a.constructor===Object)try{return JSON.stringify(a)}catch(err){}return String(a)},[""],"string"),global.wrapFunctionAsNode("string/compare",function(a,b){return a==b},["string","string"],"boolean"),global.wrapFunctionAsNode("string/concatenate",function(a,b){return void 0===a?b:void 0===b?a:a+b},["string","string"],"string"),global.wrapFunctionAsNode("string/contains",function(a,b){return void 0!==a&&void 0!==b&&-1!=a.indexOf(b)},["string","string"],"boolean"),global.wrapFunctionAsNode("string/toUpperCase",function(a){return null!=a&&a.constructor===String?a.toUpperCase():a},["string"],"string"),global.wrapFunctionAsNode("string/split",function(str,separator){if(null==separator&&(separator=this.properties.separator),null==str)return[];if(str.constructor===String)return str.split(separator||" ");if(str.constructor!==Array)return null;for(var r=[],i=0;isize[0])&&values.shift()}},GraphicsPlot.prototype.onDrawBackground=function(ctx){if(!this.flags.collapsed){var size=this.size,scale=.5*size[1]/this.properties.scale,colors=GraphicsPlot.colors,offset=.5*size[1];if(ctx.fillStyle="#000",ctx.fillRect(0,0,size[0],size[1]),ctx.strokeStyle="#555",ctx.beginPath(),ctx.moveTo(0,offset),ctx.lineTo(size[0],offset),ctx.stroke(),this.inputs)for(var i=0;i<4;++i){var values=this.values[i];if(this.inputs[i]&&this.inputs[i].link){ctx.strokeStyle=colors[i],ctx.beginPath();var v=values[0]*scale*-1+offset;ctx.moveTo(0,clamp(v,0,size[1]));for(var j=1;jsize||tex.height>size)&&(temp_tex=this._preview_temp_tex,this._preview_temp_tex||(temp_tex=new GL.Texture(size,size,{minFilter:gl.NEAREST}),this._preview_temp_tex=temp_tex),tex.copyTo(temp_tex),tex=temp_tex);tex=this._preview_canvas;return tex||(tex=createCanvas(size,size),this._preview_canvas=tex),temp_tex&&temp_tex.toCanvas(tex),tex},LGraphTexture.prototype.getResources=function(res){return this.properties.name&&(res[this.properties.name]=GL.Texture),res},LGraphTexture.prototype.onGetInputs=function(){return[["in","Texture"]]},LGraphTexture.prototype.onGetOutputs=function(){return[["width","number"],["height","number"],["aspect","number"]]},LGraphTexture.replaceCode=function(code,context){return code.replace(/\{\{[a-zA-Z0-9_]*\}\}/g,function(v){return v=v.replace(/[{}]/g,""),context[v]||""})},LiteGraph.registerNodeType("texture/texture",LGraphTexture),LGraphTexturePreview.title="Preview",LGraphTexturePreview.desc="Show a texture in the graph canvas",LGraphTexturePreview.allow_preview=!1,LGraphTexturePreview.prototype.onDrawBackground=function(ctx){var tex,tex_canvas;this.flags.collapsed||(ctx.webgl||LGraphTexturePreview.allow_preview)&&(tex=this.getInputData(0))&&(tex_canvas=null,tex_canvas=!tex.handle&&ctx.webgl?tex:LGraphTexture.generateLowResTexturePreview(tex),ctx.save(),this.properties.flipY&&(ctx.translate(0,this.size[1]),ctx.scale(1,-1)),ctx.drawImage(tex_canvas,0,0,this.size[0],this.size[1]),ctx.restore())},LiteGraph.registerNodeType("texture/preview",LGraphTexturePreview),LGraphTextureSave.title="Save",LGraphTextureSave.desc="Save a texture in the repository",LGraphTextureSave.prototype.getPreviewTexture=function(){return this._texture},LGraphTextureSave.prototype.onExecute=function(){var tex=this.getInputData(0);tex&&(this.properties.generate_mipmaps&&(tex.bind(0),tex.setParameter(gl.TEXTURE_MIN_FILTER,gl.LINEAR_MIPMAP_LINEAR),gl.generateMipmap(tex.texture_type),tex.unbind(0)),this.properties.name&&(LGraphTexture.storeTexture?LGraphTexture.storeTexture(this.properties.name,tex):LGraphTexture.getTexturesContainer()[this.properties.name]=tex),this._texture=tex,this.setOutputData(0,tex),this.setOutputData(1,this.properties.name))},LiteGraph.registerNodeType("texture/save",LGraphTextureSave),LGraphTextureOperation.widgets_info={uvcode:{widget:"code"},pixelcode:{widget:"code"},precision:{widget:"combo",values:LGraphTexture.MODE_VALUES}},LGraphTextureOperation.title="Operation",LGraphTextureOperation.desc="Texture shader operation",LGraphTextureOperation.presets={},LGraphTextureOperation.prototype.getExtraMenuOptions=function(graphcanvas){var that=this;return[{content:that.properties.show?"Hide Texture":"Show Texture",callback:function(){that.properties.show=!that.properties.show}}]},LGraphTextureOperation.prototype.onPropertyChanged=function(){this.has_error=!1},LGraphTextureOperation.prototype.onDrawBackground=function(ctx){this.flags.collapsed||this.size[1]<=20||!this.properties.show||this._tex&&this._tex.gl==ctx&&(ctx.save(),ctx.drawImage(this._tex,0,0,this.size[0],this.size[1]),ctx.restore())},LGraphTextureOperation.prototype.onExecute=function(){var tex=this.getInputData(0);if(this.isOutputConnected(0))if(this.properties.precision===LGraphTexture.PASS_THROUGH)this.setOutputData(0,tex);else{var texB=this.getInputData(1);if(this.properties.uvcode||this.properties.pixelcode){var value,time,width=512,height=512,type=(tex?(width=tex.width,height=tex.height):texB&&(width=texB.width,height=texB.height),texB=texB||GL.Texture.getWhiteTexture(),LGraphTexture.getTextureType(this.properties.precision,tex)),type=(tex||this._tex?this._tex=LGraphTexture.getTargetTexture(tex||this._tex,this._tex,this.properties.precision):this._tex=new GL.Texture(width,height,{type:type,format:gl.RGBA,filter:gl.LINEAR}),""),pixelcode=(this.properties.uvcode&&(type="uv = "+this.properties.uvcode,-1!=this.properties.uvcode.indexOf(";"))&&(type=this.properties.uvcode),""),shader=(this.properties.pixelcode&&(pixelcode="result = "+this.properties.pixelcode,-1!=this.properties.pixelcode.indexOf(";"))&&(pixelcode=this.properties.pixelcode),this._shader);if(!(this.has_error||shader&&this._shader_code==type+"|"+pixelcode)){var final_pixel_code=LGraphTexture.replaceCode(LGraphTextureOperation.pixel_shader,{UV_CODE:type,PIXEL_CODE:pixelcode});try{shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,final_pixel_code),this.boxcolor="#00FF00"}catch(err){return GL.Shader.dumpErrorToConsole(err,Shader.SCREEN_VERTEX_SHADER,final_pixel_code),this.boxcolor="#FF0000",void(this.has_error=!0)}this._shader=shader,this._shader_code=type+"|"+pixelcode}this._shader&&(null!=(value=this.getInputData(2))?this.properties.value=value:value=parseFloat(this.properties.value),time=this.graph.getTime(),this._tex.drawTo(function(){gl.disable(gl.DEPTH_TEST),gl.disable(gl.CULL_FACE),gl.disable(gl.BLEND),tex&&tex.bind(0),texB&&texB.bind(1);var mesh=Mesh.getScreenQuad();shader.uniforms({u_texture:0,u_textureB:1,value:value,texSize:[width,height,1/width,1/height],time:time}).draw(mesh)}),this.setOutputData(0,this._tex))}}},LGraphTextureOperation.pixel_shader="precision highp float;\n\t\t\n\t\tuniform sampler2D u_texture;\n\t\tuniform sampler2D u_textureB;\n\t\tvarying vec2 v_coord;\n\t\tuniform vec4 texSize;\n\t\tuniform float time;\n\t\tuniform float value;\n\t\t\n\t\tvoid main() {\n\t\t\tvec2 uv = v_coord;\n\t\t\t{{UV_CODE}};\n\t\t\tvec4 color4 = texture2D(u_texture, uv);\n\t\t\tvec3 color = color4.rgb;\n\t\t\tvec4 color4B = texture2D(u_textureB, uv);\n\t\t\tvec3 colorB = color4B.rgb;\n\t\t\tvec3 result = color;\n\t\t\tfloat alpha = 1.0;\n\t\t\t{{PIXEL_CODE}};\n\t\t\tgl_FragColor = vec4(result, alpha);\n\t\t}\n\t\t",(LGraphTextureOperation.registerPreset=function(name,code){LGraphTextureOperation.presets[name]=code})("",""),LGraphTextureOperation.registerPreset("bypass","color"),LGraphTextureOperation.registerPreset("add","color + colorB * value"),LGraphTextureOperation.registerPreset("substract","(color - colorB) * value"),LGraphTextureOperation.registerPreset("mate","mix( color, colorB, color4B.a * value)"),LGraphTextureOperation.registerPreset("invert","vec3(1.0) - color"),LGraphTextureOperation.registerPreset("multiply","color * colorB * value"),LGraphTextureOperation.registerPreset("divide","(color / colorB) / value"),LGraphTextureOperation.registerPreset("difference","abs(color - colorB) * value"),LGraphTextureOperation.registerPreset("max","max(color, colorB) * value"),LGraphTextureOperation.registerPreset("min","min(color, colorB) * value"),LGraphTextureOperation.registerPreset("displace","texture2D(u_texture, uv + (colorB.xy - vec2(0.5)) * value).xyz"),LGraphTextureOperation.registerPreset("grayscale","vec3(color.x + color.y + color.z) * value / 3.0"),LGraphTextureOperation.registerPreset("saturation","mix( vec3(color.x + color.y + color.z) / 3.0, color, value )"),LGraphTextureOperation.registerPreset("normalmap","\n\t\tfloat z0 = texture2D(u_texture, uv + vec2(-texSize.z, -texSize.w) ).x;\n\t\tfloat z1 = texture2D(u_texture, uv + vec2(0.0, -texSize.w) ).x;\n\t\tfloat z2 = texture2D(u_texture, uv + vec2(texSize.z, -texSize.w) ).x;\n\t\tfloat z3 = texture2D(u_texture, uv + vec2(-texSize.z, 0.0) ).x;\n\t\tfloat z4 = color.x;\n\t\tfloat z5 = texture2D(u_texture, uv + vec2(texSize.z, 0.0) ).x;\n\t\tfloat z6 = texture2D(u_texture, uv + vec2(-texSize.z, texSize.w) ).x;\n\t\tfloat z7 = texture2D(u_texture, uv + vec2(0.0, texSize.w) ).x;\n\t\tfloat z8 = texture2D(u_texture, uv + vec2(texSize.z, texSize.w) ).x;\n\t\tvec3 normal = vec3( z2 + 2.0*z4 + z7 - z0 - 2.0*z3 - z5, z5 + 2.0*z6 + z7 -z0 - 2.0*z1 - z2, 1.0 );\n\t\tnormal.xy *= value;\n\t\tresult.xyz = normalize(normal) * 0.5 + vec3(0.5);\n\t"),LGraphTextureOperation.registerPreset("threshold","vec3(color.x > colorB.x * value ? 1.0 : 0.0,color.y > colorB.y * value ? 1.0 : 0.0,color.z > colorB.z * value ? 1.0 : 0.0)"),LGraphTextureOperation.prototype.onInspect=function(widgets){var that=this;widgets.addCombo("Presets","",{values:Object.keys(LGraphTextureOperation.presets),callback:function(v){var code=LGraphTextureOperation.presets[v];code&&(that.setProperty("pixelcode",code),that.title=v,widgets.refresh())}})},LiteGraph.registerNodeType("texture/operation",LGraphTextureOperation),LGraphTextureShader.title="Shader",LGraphTextureShader.desc="Texture shader",LGraphTextureShader.widgets_info={code:{type:"code",lang:"glsl"},precision:{widget:"combo",values:LGraphTexture.MODE_VALUES}},LGraphTextureShader.prototype.onPropertyChanged=function(name,value){if("code"==name){var shader=this.getShader();if(shader){var uniforms=shader.uniformInfo;if(this.inputs)for(var already={},i=0;i lumaMax))\n\t\t\t\tcolor = vec4(rgbA, 1.0);\n\t\t\telse\n\t\t\t\tcolor = vec4(rgbB, 1.0);\n\t\t\tif(u_igamma != 1.0)\n\t\t\t\tcolor.xyz = pow( color.xyz, vec3(u_igamma) );\n\t\t\treturn color;\n\t\t}\n\t\t\n\t\tvoid main() {\n\t\t gl_FragColor = applyFXAA( u_texture, v_coord * uViewportSize) ;\n\t\t}\n\t\t",LGraphTextureToViewport.gamma_pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_texture;\n\t\tuniform float u_igamma;\n\t\tvoid main() {\n\t\t\tvec4 color = texture2D( u_texture, v_coord);\n\t\t\tcolor.xyz = pow(color.xyz, vec3(u_igamma) );\n\t\t gl_FragColor = color;\n\t\t}\n\t\t",LiteGraph.registerNodeType("texture/toviewport",LGraphTextureToViewport),LGraphTextureCopy.title="Copy",LGraphTextureCopy.desc="Copy Texture",LGraphTextureCopy.widgets_info={size:{widget:"combo",values:[0,32,64,128,256,512,1024,2048]},precision:{widget:"combo",values:LGraphTexture.MODE_VALUES}},LGraphTextureCopy.prototype.onExecute=function(){var width,height,type,temp,tex=this.getInputData(0);(tex||this._temp_texture)&&this.isOutputConnected(0)&&(tex&&(width=tex.width,height=tex.height,0!=this.properties.size&&(width=this.properties.size,height=this.properties.size),temp=this._temp_texture,type=tex.type,this.properties.precision===LGraphTexture.LOW?type=gl.UNSIGNED_BYTE:this.properties.precision===LGraphTexture.HIGH&&(type=gl.HIGH_PRECISION_FORMAT),temp&&temp.width==width&&temp.height==height&&temp.type==type||(temp=gl.LINEAR,this.properties.generate_mipmaps&&isPowerOfTwo(width)&&isPowerOfTwo(height)&&(temp=gl.LINEAR_MIPMAP_LINEAR),this._temp_texture=new GL.Texture(width,height,{type:type,format:gl.RGBA,minFilter:temp,magFilter:gl.LINEAR})),tex.copyTo(this._temp_texture),this.properties.generate_mipmaps)&&(this._temp_texture.bind(0),gl.generateMipmap(this._temp_texture.texture_type),this._temp_texture.unbind(0)),this.setOutputData(0,this._temp_texture))},LiteGraph.registerNodeType("texture/copy",LGraphTextureCopy),LGraphTextureDownsample.title="Downsample",LGraphTextureDownsample.desc="Downsample Texture",LGraphTextureDownsample.widgets_info={iterations:{type:"number",step:1,precision:0,min:0},precision:{widget:"combo",values:LGraphTexture.MODE_VALUES}},LGraphTextureDownsample.prototype.onExecute=function(){var tex=this.getInputData(0);if((tex||this._temp_texture)&&this.isOutputConnected(0)&&tex&&tex.texture_type===GL.TEXTURE_2D)if(this.properties.iterations<1)this.setOutputData(0,tex);else{var target,shader=LGraphTextureDownsample._shader,width=(shader||(LGraphTextureDownsample._shader=shader=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,LGraphTextureDownsample.pixel_shader)),0|tex.width),height=0|tex.height,type=tex.type,iterations=(this.properties.precision===LGraphTexture.LOW?type=gl.UNSIGNED_BYTE:this.properties.precision===LGraphTexture.HIGH&&(type=gl.HIGH_PRECISION_FORMAT),this.properties.iterations||1),origin=tex,temp=[],options={type:type,format:tex.format},offset=vec2.create(),uniforms={u_offset:offset};this._texture&&GL.Texture.releaseTemporary(this._texture);for(var i=0;i>1||0,height=height>>1||0,target=GL.Texture.getTemporary(width,height,options),temp.push(target),origin.setParameter(GL.TEXTURE_MAG_FILTER,GL.NEAREST),origin.copyTo(target,shader,uniforms),1!=width||1!=height);++i)origin=target;this._texture=temp.pop();for(i=0;itexA.width?texB:texA,this._tex,this.properties.precision),gl.disable(gl.BLEND),gl.disable(gl.DEPTH_TEST),mesh=Mesh.getScreenQuad(),shader=null,uniforms=this._uniforms,texMix?shader=(shader=LGraphTextureMix._shader_tex)||(LGraphTextureMix._shader_tex=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,LGraphTextureMix.pixel_shader,{MIX_TEX:""})):(shader=(shader=LGraphTextureMix._shader_factor)||(LGraphTextureMix._shader_factor=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,LGraphTextureMix.pixel_shader)),factor=null==factor?this.properties.factor:factor,uniforms.u_mix.set([factor,factor,factor,factor])),invert=this.properties.invert,this._tex.drawTo(function(){texA.bind(invert?1:0),texB.bind(invert?0:1),texMix&&texMix.bind(2),shader.uniforms(uniforms).draw(mesh)}),this.setOutputData(0,this._tex))))},LGraphTextureMix.prototype.onGetInputs=function(){return[["factor","number"]]},LGraphTextureMix.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_textureA;\n\t\tuniform sampler2D u_textureB;\n\t\t#ifdef MIX_TEX\n\t\t\tuniform sampler2D u_textureMix;\n\t\t#else\n\t\t\tuniform vec4 u_mix;\n\t\t#endif\n\t\t\n\t\tvoid main() {\n\t\t\t#ifdef MIX_TEX\n\t\t\t vec4 f = texture2D(u_textureMix, v_coord);\n\t\t\t#else\n\t\t\t vec4 f = u_mix;\n\t\t\t#endif\n\t\t gl_FragColor = mix( texture2D(u_textureA, v_coord), texture2D(u_textureB, v_coord), f );\n\t\t}\n\t\t",LiteGraph.registerNodeType("texture/mix",LGraphTextureMix),LGraphTextureEdges.title="Edges",LGraphTextureEdges.desc="Detects edges",LGraphTextureEdges.widgets_info={precision:{widget:"combo",values:LGraphTexture.MODE_VALUES}},LGraphTextureEdges.prototype.onExecute=function(){var tex,mesh,shader,invert,factor,threshold;this.isOutputConnected(0)&&(tex=this.getInputData(0),this.properties.precision===LGraphTexture.PASS_THROUGH?this.setOutputData(0,tex):tex&&(this._tex=LGraphTexture.getTargetTexture(tex,this._tex,this.properties.precision),gl.disable(gl.BLEND),gl.disable(gl.DEPTH_TEST),mesh=Mesh.getScreenQuad(),shader=LGraphTextureEdges._shader,invert=this.properties.invert,factor=this.properties.factor,threshold=this.properties.threshold?1:0,this._tex.drawTo(function(){tex.bind(0),shader.uniforms({u_texture:0,u_isize:[1/tex.width,1/tex.height],u_factor:factor,u_threshold:threshold,u_invert:invert?1:0}).draw(mesh)}),this.setOutputData(0,this._tex)))},LGraphTextureEdges.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_texture;\n\t\tuniform vec2 u_isize;\n\t\tuniform int u_invert;\n\t\tuniform float u_factor;\n\t\tuniform float u_threshold;\n\t\t\n\t\tvoid main() {\n\t\t\tvec4 center = texture2D(u_texture, v_coord);\n\t\t\tvec4 up = texture2D(u_texture, v_coord + u_isize * vec2(0.0,1.0) );\n\t\t\tvec4 down = texture2D(u_texture, v_coord + u_isize * vec2(0.0,-1.0) );\n\t\t\tvec4 left = texture2D(u_texture, v_coord + u_isize * vec2(1.0,0.0) );\n\t\t\tvec4 right = texture2D(u_texture, v_coord + u_isize * vec2(-1.0,0.0) );\n\t\t\tvec4 diff = abs(center - up) + abs(center - down) + abs(center - left) + abs(center - right);\n\t\t\tdiff *= u_factor;\n\t\t\tif(u_invert == 1)\n\t\t\t\tdiff.xyz = vec3(1.0) - diff.xyz;\n\t\t\tif( u_threshold == 0.0 )\n\t\t\t\tgl_FragColor = vec4( diff.xyz, center.a );\n\t\t\telse\n\t\t\t\tgl_FragColor = vec4( diff.x > 0.5 ? 1.0 : 0.0, diff.y > 0.5 ? 1.0 : 0.0, diff.z > 0.5 ? 1.0 : 0.0, center.a );\n\t\t}\n\t\t",LiteGraph.registerNodeType("texture/edges",LGraphTextureEdges),LGraphTextureDepthRange.title="Depth Range",LGraphTextureDepthRange.desc="Generates a texture with a depth range",LGraphTextureDepthRange.prototype.onExecute=function(){var tex,uniforms,range,mesh,shader,precision;this.isOutputConnected(0)&&(tex=this.getInputData(0))&&(precision=gl.UNSIGNED_BYTE,this.properties.high_precision&&(precision=gl.half_float_ext?gl.HALF_FLOAT_OES:gl.FLOAT),this._temp_texture&&this._temp_texture.type==precision&&this._temp_texture.width==tex.width&&this._temp_texture.height==tex.height||(this._temp_texture=new GL.Texture(tex.width,tex.height,{type:precision,format:gl.RGBA,filter:gl.LINEAR})),uniforms=this._uniforms,precision=this.properties.distance,this.isInputConnected(1)&&(precision=this.getInputData(1),this.properties.distance=precision),range=this.properties.range,this.isInputConnected(2)&&(range=this.getInputData(2),this.properties.range=range),uniforms.u_distance=precision,uniforms.u_range=range,gl.disable(gl.BLEND),gl.disable(gl.DEPTH_TEST),mesh=Mesh.getScreenQuad(),LGraphTextureDepthRange._shader||(LGraphTextureDepthRange._shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,LGraphTextureDepthRange.pixel_shader),LGraphTextureDepthRange._shader_onlydepth=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,LGraphTextureDepthRange.pixel_shader,{ONLY_DEPTH:""})),shader=this.properties.only_depth?LGraphTextureDepthRange._shader_onlydepth:LGraphTextureDepthRange._shader,precision=null,precision=tex.near_far_planes||(window.LS&&LS.Renderer._main_camera?LS.Renderer._main_camera._uniforms.u_camera_planes:[.1,1e3]),uniforms.u_camera_planes=precision,this._temp_texture.drawTo(function(){tex.bind(0),shader.uniforms(uniforms).draw(mesh)}),this._temp_texture.near_far_planes=precision,this.setOutputData(0,this._temp_texture))},LGraphTextureDepthRange.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_texture;\n\t\tuniform vec2 u_camera_planes;\n\t\tuniform float u_distance;\n\t\tuniform float u_range;\n\t\t\n\t\tfloat LinearDepth()\n\t\t{\n\t\t\tfloat zNear = u_camera_planes.x;\n\t\t\tfloat zFar = u_camera_planes.y;\n\t\t\tfloat depth = texture2D(u_texture, v_coord).x;\n\t\t\tdepth = depth * 2.0 - 1.0;\n\t\t\treturn zNear * (depth + 1.0) / (zFar + zNear - depth * (zFar - zNear));\n\t\t}\n\t\t\n\t\tvoid main() {\n\t\t\tfloat depth = LinearDepth();\n\t\t\t#ifdef ONLY_DEPTH\n\t\t\t gl_FragColor = vec4(depth);\n\t\t\t#else\n\t\t\t\tfloat diff = abs(depth * u_camera_planes.y - u_distance);\n\t\t\t\tfloat dof = 1.0;\n\t\t\t\tif(diff <= u_range)\n\t\t\t\t\tdof = diff / u_range;\n\t\t\t gl_FragColor = vec4(dof);\n\t\t\t#endif\n\t\t}\n\t\t",LiteGraph.registerNodeType("texture/depth_range",LGraphTextureDepthRange),LGraphTextureLinearDepth.widgets_info={precision:{widget:"combo",values:LGraphTexture.MODE_VALUES}},LGraphTextureLinearDepth.title="Linear Depth",LGraphTextureLinearDepth.desc="Creates a color texture with linear depth",LGraphTextureLinearDepth.prototype.onExecute=function(){var tex,uniforms,mesh,shader,precision;!this.isOutputConnected(0)||!(tex=this.getInputData(0))||tex.format!=gl.DEPTH_COMPONENT&&tex.format!=gl.DEPTH_STENCIL||(precision=this.properties.precision==LGraphTexture.HIGH?gl.HIGH_PRECISION_FORMAT:gl.UNSIGNED_BYTE,this._temp_texture&&this._temp_texture.type==precision&&this._temp_texture.width==tex.width&&this._temp_texture.height==tex.height||(this._temp_texture=new GL.Texture(tex.width,tex.height,{type:precision,format:gl.RGB,filter:gl.LINEAR})),(uniforms=this._uniforms).u_invert=this.properties.invert?1:0,gl.disable(gl.BLEND),gl.disable(gl.DEPTH_TEST),mesh=Mesh.getScreenQuad(),shader=LGraphTextureLinearDepth._shader=LGraphTextureLinearDepth._shader||new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,LGraphTextureLinearDepth.pixel_shader),precision=null,precision=tex.near_far_planes||(window.LS&&LS.Renderer._main_camera?LS.Renderer._main_camera._uniforms.u_camera_planes:[.1,1e3]),uniforms.u_camera_planes=precision,uniforms.u_ires.set([0,0]),this._temp_texture.drawTo(function(){tex.bind(0),shader.uniforms(uniforms).draw(mesh)}),this._temp_texture.near_far_planes=precision,this.setOutputData(0,this._temp_texture))},LGraphTextureLinearDepth.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_texture;\n\t\tuniform vec2 u_camera_planes;\n\t\tuniform int u_invert;\n\t\tuniform vec2 u_ires;\n\t\t\n\t\tvoid main() {\n\t\t\tfloat zNear = u_camera_planes.x;\n\t\t\tfloat zFar = u_camera_planes.y;\n\t\t\tfloat depth = texture2D(u_texture, v_coord + u_ires*0.5).x * 2.0 - 1.0;\n\t\t\tfloat f = zNear * (depth + 1.0) / (zFar + zNear - depth * (zFar - zNear));\n\t\t\tif( u_invert == 1 )\n\t\t\t\tf = 1.0 - f;\n\t\t\tgl_FragColor = vec4(vec3(f),1.0);\n\t\t}\n\t\t",LiteGraph.registerNodeType("texture/linear_depth",LGraphTextureLinearDepth),LGraphTextureBlur.title="Blur",LGraphTextureBlur.desc="Blur a texture",LGraphTextureBlur.widgets_info={precision:{widget:"combo",values:LGraphTexture.MODE_VALUES}},LGraphTextureBlur.max_iterations=20,LGraphTextureBlur.prototype.onExecute=function(){var tex=this.getInputData(0);if(tex&&this.isOutputConnected(0)){var temp=this._final_texture,iterations=(temp&&temp.width==tex.width&&temp.height==tex.height&&temp.type==tex.type||(temp=this._final_texture=new GL.Texture(tex.width,tex.height,{type:tex.type,format:gl.RGBA,filter:gl.LINEAR})),this.properties.iterations);if(this.isInputConnected(1)&&(iterations=this.getInputData(1),this.properties.iterations=iterations),0==(iterations=Math.min(Math.floor(iterations),LGraphTextureBlur.max_iterations)))this.setOutputData(0,tex);else{var intensity=this.properties.intensity,aspect=(this.isInputConnected(2)&&(intensity=this.getInputData(2),this.properties.intensity=intensity),LiteGraph.camera_aspect),scale=(aspect=(aspect=aspect||void 0===window.gl?aspect:gl.canvas.height/gl.canvas.width)||1,aspect=this.properties.preserve_aspect?aspect:1,this.properties.scale||[1,1]);tex.applyBlur(aspect*scale[0],scale[1],intensity,temp);for(var i=1;i>=1),!((width>>=1)<2));i++)currentDestination=textures[i]=GL.Texture.getTemporary(width,height,texture_info),texel_size[0]=1/currentSource.width,texel_size[1]=1/currentSource.height,currentSource.blit(currentDestination,shader.uniforms(uniforms)),currentSource=currentDestination;for(average_texture&&(texel_size[0]=1/currentSource.width,texel_size[1]=1/currentSource.height,uniforms.u_intensity=intensity,uniforms.u_delta=1,currentSource.blit(average_texture,shader.uniforms(uniforms))),gl.enable(gl.BLEND),gl.blendFunc(gl.ONE,gl.ONE),uniforms.u_intensity=this.persistence,uniforms.u_delta=.5,i-=2;0<=i;i--)currentDestination=textures[i],textures[i]=null,texel_size[0]=1/currentSource.width,texel_size[1]=1/currentSource.height,currentSource.blit(currentDestination,shader.uniforms(uniforms)),GL.Texture.releaseTemporary(currentSource),currentSource=currentDestination;gl.disable(gl.BLEND),glow_texture&¤tSource.blit(glow_texture),output_texture&&(average_texture=output_texture,dirt_texture=this.dirt_texture,dirt_factor=this.dirt_factor,uniforms.u_intensity=intensity,shader=(shader=dirt_texture?FXGlow._dirt_final_shader:FXGlow._final_shader)||(dirt_texture?FXGlow._dirt_final_shader=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,FXGlow.final_pixel_shader,{USE_DIRT:""}):FXGlow._final_shader=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,FXGlow.final_pixel_shader)),average_texture.drawTo(function(){tex.bind(0),currentSource.bind(1),dirt_texture&&(shader.setUniform("u_dirt_factor",dirt_factor),shader.setUniform("u_dirt_texture",dirt_texture.bind(2))),shader.toViewport(uniforms)})),GL.Texture.releaseTemporary(currentSource)},FXGlow.cut_pixel_shader="precision highp float;\n\tvarying vec2 v_coord;\n\tuniform sampler2D u_texture;\n\tuniform float u_threshold;\n\tvoid main() {\n\t\tgl_FragColor = max( texture2D( u_texture, v_coord ) - vec4( u_threshold ), vec4(0.0) );\n\t}",FXGlow.scale_pixel_shader="precision highp float;\n\tvarying vec2 v_coord;\n\tuniform sampler2D u_texture;\n\tuniform vec2 u_texel_size;\n\tuniform float u_delta;\n\tuniform float u_intensity;\n\t\n\tvec4 sampleBox(vec2 uv) {\n\t\tvec4 o = u_texel_size.xyxy * vec2(-u_delta, u_delta).xxyy;\n\t\tvec4 s = texture2D( u_texture, uv + o.xy ) + texture2D( u_texture, uv + o.zy) + texture2D( u_texture, uv + o.xw) + texture2D( u_texture, uv + o.zw);\n\t\treturn s * 0.25;\n\t}\n\tvoid main() {\n\t\tgl_FragColor = u_intensity * sampleBox( v_coord );\n\t}",FXGlow.final_pixel_shader="precision highp float;\n\tvarying vec2 v_coord;\n\tuniform sampler2D u_texture;\n\tuniform sampler2D u_glow_texture;\n\t#ifdef USE_DIRT\n\t\tuniform sampler2D u_dirt_texture;\n\t#endif\n\tuniform vec2 u_texel_size;\n\tuniform float u_delta;\n\tuniform float u_intensity;\n\tuniform float u_dirt_factor;\n\t\n\tvec4 sampleBox(vec2 uv) {\n\t\tvec4 o = u_texel_size.xyxy * vec2(-u_delta, u_delta).xxyy;\n\t\tvec4 s = texture2D( u_glow_texture, uv + o.xy ) + texture2D( u_glow_texture, uv + o.zy) + texture2D( u_glow_texture, uv + o.xw) + texture2D( u_glow_texture, uv + o.zw);\n\t\treturn s * 0.25;\n\t}\n\tvoid main() {\n\t\tvec4 glow = sampleBox( v_coord );\n\t\t#ifdef USE_DIRT\n\t\t\tglow = mix( glow, glow * texture2D( u_dirt_texture, v_coord ), u_dirt_factor );\n\t\t#endif\n\t\tgl_FragColor = texture2D( u_texture, v_coord ) + u_intensity * glow;\n\t}",LGraphTextureGlow.title="Glow",LGraphTextureGlow.desc="Filters a texture giving it a glow effect",LGraphTextureGlow.widgets_info={iterations:{type:"number",min:0,max:16,step:1,precision:0},threshold:{type:"number",min:0,max:10,step:.01,precision:2},precision:{widget:"combo",values:LGraphTexture.MODE_VALUES}},LGraphTextureGlow.prototype.onGetInputs=function(){return[["enabled","boolean"],["threshold","number"],["intensity","number"],["persistence","number"],["iterations","number"],["dirt_factor","number"]]},LGraphTextureGlow.prototype.onGetOutputs=function(){return[["average","Texture"]]},LGraphTextureGlow.prototype.onExecute=function(){var fx,type,average_texture,glow_texture,final_texture,tex=this.getInputData(0);tex&&this.isAnyOutputConnected()&&(this.properties.precision===LGraphTexture.PASS_THROUGH||!1===this.getInputOrProperty("enabled")?this.setOutputData(0,tex):(tex.width,tex.height,(fx=this.fx).threshold=this.getInputOrProperty("threshold"),fx.iterations=this.getInputOrProperty("iterations"),fx.intensity=this.getInputOrProperty("intensity"),fx.persistence=this.getInputOrProperty("persistence"),fx.dirt_texture=this.getInputData(1),fx.dirt_factor=this.getInputOrProperty("dirt_factor"),fx.scale=this.properties.scale,type=LGraphTexture.getTextureType(this.properties.precision,tex),average_texture=null,!this.isOutputConnected(2)||(average_texture=this._average_texture)&&average_texture.type==tex.type&&average_texture.format==tex.format||(average_texture=this._average_texture=new GL.Texture(1,1,{type:tex.type,format:tex.format,filter:gl.LINEAR})),glow_texture=null,!this.isOutputConnected(1)||(glow_texture=this._glow_texture)&&glow_texture.width==tex.width&&glow_texture.height==tex.height&&glow_texture.type==type&&glow_texture.format==tex.format||(glow_texture=this._glow_texture=new GL.Texture(tex.width,tex.height,{type:type,format:tex.format,filter:gl.LINEAR})),final_texture=null,!this.isOutputConnected(0)||(final_texture=this._final_texture)&&final_texture.width==tex.width&&final_texture.height==tex.height&&final_texture.type==type&&final_texture.format==tex.format||(final_texture=this._final_texture=new GL.Texture(tex.width,tex.height,{type:type,format:tex.format,filter:gl.LINEAR})),fx.applyFX(tex,final_texture,glow_texture,average_texture),this.isOutputConnected(0)&&this.setOutputData(0,final_texture),this.isOutputConnected(1)&&this.setOutputData(1,average_texture),this.isOutputConnected(2)&&this.setOutputData(2,glow_texture)))},LiteGraph.registerNodeType("texture/glow",LGraphTextureGlow),LGraphTextureKuwaharaFilter.title="Kuwahara Filter",LGraphTextureKuwaharaFilter.desc="Filters a texture giving an artistic oil canvas painting",LGraphTextureKuwaharaFilter.max_radius=10,LGraphTextureKuwaharaFilter._shaders=[],LGraphTextureKuwaharaFilter.prototype.onExecute=function(){var intensity,aspect,shader,mesh,temp,tex=this.getInputData(0);tex&&this.isOutputConnected(0)&&((temp=this._temp_texture)&&temp.width==tex.width&&temp.height==tex.height&&temp.type==tex.type||(this._temp_texture=new GL.Texture(tex.width,tex.height,{type:tex.type,format:gl.RGBA,filter:gl.LINEAR})),temp=this.properties.radius,0==(temp=Math.min(Math.floor(temp),LGraphTextureKuwaharaFilter.max_radius))?this.setOutputData(0,tex):(intensity=this.properties.intensity,aspect=(aspect=(aspect=LiteGraph.camera_aspect)||void 0===window.gl?aspect:gl.canvas.height/gl.canvas.width)||1,aspect=this.properties.preserve_aspect?aspect:1,LGraphTextureKuwaharaFilter._shaders[temp]||(LGraphTextureKuwaharaFilter._shaders[temp]=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,LGraphTextureKuwaharaFilter.pixel_shader,{RADIUS:temp.toFixed(0)})),shader=LGraphTextureKuwaharaFilter._shaders[temp],mesh=GL.Mesh.getScreenQuad(),tex.bind(0),this._temp_texture.drawTo(function(){shader.uniforms({u_texture:0,u_intensity:intensity,u_resolution:[tex.width,tex.height],u_iResolution:[1/tex.width,1/tex.height]}).draw(mesh)}),this.setOutputData(0,this._temp_texture)))},LGraphTextureKuwaharaFilter.pixel_shader="\nprecision highp float;\nvarying vec2 v_coord;\nuniform sampler2D u_texture;\nuniform float u_intensity;\nuniform vec2 u_resolution;\nuniform vec2 u_iResolution;\n#ifndef RADIUS\n\t#define RADIUS 7\n#endif\nvoid main() {\n\n\tconst int radius = RADIUS;\n\tvec2 fragCoord = v_coord;\n\tvec2 src_size = u_iResolution;\n\tvec2 uv = v_coord;\n\tfloat n = float((radius + 1) * (radius + 1));\n\tint i;\n\tint j;\n\tvec3 m0 = vec3(0.0); vec3 m1 = vec3(0.0); vec3 m2 = vec3(0.0); vec3 m3 = vec3(0.0);\n\tvec3 s0 = vec3(0.0); vec3 s1 = vec3(0.0); vec3 s2 = vec3(0.0); vec3 s3 = vec3(0.0);\n\tvec3 c;\n\t\n\tfor (int j = -radius; j <= 0; ++j) {\n\t\tfor (int i = -radius; i <= 0; ++i) {\n\t\t\tc = texture2D(u_texture, uv + vec2(i,j) * src_size).rgb;\n\t\t\tm0 += c;\n\t\t\ts0 += c * c;\n\t\t}\n\t}\n\t\n\tfor (int j = -radius; j <= 0; ++j) {\n\t\tfor (int i = 0; i <= radius; ++i) {\n\t\t\tc = texture2D(u_texture, uv + vec2(i,j) * src_size).rgb;\n\t\t\tm1 += c;\n\t\t\ts1 += c * c;\n\t\t}\n\t}\n\t\n\tfor (int j = 0; j <= radius; ++j) {\n\t\tfor (int i = 0; i <= radius; ++i) {\n\t\t\tc = texture2D(u_texture, uv + vec2(i,j) * src_size).rgb;\n\t\t\tm2 += c;\n\t\t\ts2 += c * c;\n\t\t}\n\t}\n\t\n\tfor (int j = 0; j <= radius; ++j) {\n\t\tfor (int i = -radius; i <= 0; ++i) {\n\t\t\tc = texture2D(u_texture, uv + vec2(i,j) * src_size).rgb;\n\t\t\tm3 += c;\n\t\t\ts3 += c * c;\n\t\t}\n\t}\n\t\n\tfloat min_sigma2 = 1e+2;\n\tm0 /= n;\n\ts0 = abs(s0 / n - m0 * m0);\n\t\n\tfloat sigma2 = s0.r + s0.g + s0.b;\n\tif (sigma2 < min_sigma2) {\n\t\tmin_sigma2 = sigma2;\n\t\tgl_FragColor = vec4(m0, 1.0);\n\t}\n\t\n\tm1 /= n;\n\ts1 = abs(s1 / n - m1 * m1);\n\t\n\tsigma2 = s1.r + s1.g + s1.b;\n\tif (sigma2 < min_sigma2) {\n\t\tmin_sigma2 = sigma2;\n\t\tgl_FragColor = vec4(m1, 1.0);\n\t}\n\t\n\tm2 /= n;\n\ts2 = abs(s2 / n - m2 * m2);\n\t\n\tsigma2 = s2.r + s2.g + s2.b;\n\tif (sigma2 < min_sigma2) {\n\t\tmin_sigma2 = sigma2;\n\t\tgl_FragColor = vec4(m2, 1.0);\n\t}\n\t\n\tm3 /= n;\n\ts3 = abs(s3 / n - m3 * m3);\n\t\n\tsigma2 = s3.r + s3.g + s3.b;\n\tif (sigma2 < min_sigma2) {\n\t\tmin_sigma2 = sigma2;\n\t\tgl_FragColor = vec4(m3, 1.0);\n\t}\n}\n",LiteGraph.registerNodeType("texture/kuwahara",LGraphTextureKuwaharaFilter),LGraphTextureXDoGFilter.title="XDoG Filter",LGraphTextureXDoGFilter.desc="Filters a texture giving an artistic ink style",LGraphTextureXDoGFilter.max_radius=10,LGraphTextureXDoGFilter._shaders=[],LGraphTextureXDoGFilter.prototype.onExecute=function(){var temp,shader,mesh,sigma,k,p,epsilon,phi,tex=this.getInputData(0);tex&&this.isOutputConnected(0)&&((temp=this._temp_texture)&&temp.width==tex.width&&temp.height==tex.height&&temp.type==tex.type||(this._temp_texture=new GL.Texture(tex.width,tex.height,{type:tex.type,format:gl.RGBA,filter:gl.LINEAR})),shader=LGraphTextureXDoGFilter._xdog_shader=LGraphTextureXDoGFilter._xdog_shader||new GL.Shader(Shader.SCREEN_VERTEX_SHADER,LGraphTextureXDoGFilter.xdog_pixel_shader),mesh=GL.Mesh.getScreenQuad(),sigma=this.properties.sigma,k=this.properties.k,p=this.properties.p,epsilon=this.properties.epsilon,phi=this.properties.phi,tex.bind(0),this._temp_texture.drawTo(function(){shader.uniforms({src:0,sigma:sigma,k:k,p:p,epsilon:epsilon,phi:phi,cvsWidth:tex.width,cvsHeight:tex.height}).draw(mesh)}),this.setOutputData(0,this._temp_texture))},LGraphTextureXDoGFilter.xdog_pixel_shader="\nprecision highp float;\nuniform sampler2D src;\n\nuniform float cvsHeight;\nuniform float cvsWidth;\n\nuniform float sigma;\nuniform float k;\nuniform float p;\nuniform float epsilon;\nuniform float phi;\nvarying vec2 v_coord;\n\nfloat cosh(float val)\n{\n\tfloat tmp = exp(val);\n\tfloat cosH = (tmp + 1.0 / tmp) / 2.0;\n\treturn cosH;\n}\n\nfloat tanh(float val)\n{\n\tfloat tmp = exp(val);\n\tfloat tanH = (tmp - 1.0 / tmp) / (tmp + 1.0 / tmp);\n\treturn tanH;\n}\n\nfloat sinh(float val)\n{\n\tfloat tmp = exp(val);\n\tfloat sinH = (tmp - 1.0 / tmp) / 2.0;\n\treturn sinH;\n}\n\nvoid main(void){\n\tvec3 destColor = vec3(0.0);\n\tfloat tFrag = 1.0 / cvsHeight;\n\tfloat sFrag = 1.0 / cvsWidth;\n\tvec2 Frag = vec2(sFrag,tFrag);\n\tvec2 uv = gl_FragCoord.st;\n\tfloat twoSigmaESquared = 2.0 * sigma * sigma;\n\tfloat twoSigmaRSquared = twoSigmaESquared * k * k;\n\tint halfWidth = int(ceil( 1.0 * sigma * k ));\n\n\tconst int MAX_NUM_ITERATION = 99999;\n\tvec2 sum = vec2(0.0);\n\tvec2 norm = vec2(0.0);\n\n\tfor(int cnt=0;cnt (2*halfWidth+1)*(2*halfWidth+1)){break;}\n\t\tint i = int(cnt / (2*halfWidth+1)) - halfWidth;\n\t\tint j = cnt - halfWidth - int(cnt / (2*halfWidth+1)) * (2*halfWidth+1);\n\n\t\tfloat d = length(vec2(i,j));\n\t\tvec2 kernel = vec2( exp( -d * d / twoSigmaESquared ), \n\t\t\t\t\t\t\texp( -d * d / twoSigmaRSquared ));\n\n\t\tvec2 L = texture2D(src, (uv + vec2(i,j)) * Frag).xx;\n\n\t\tnorm += kernel;\n\t\tsum += kernel * L;\n\t}\n\n\tsum /= norm;\n\n\tfloat H = 100.0 * ((1.0 + p) * sum.x - p * sum.y);\n\tfloat edge = ( H > epsilon )? 1.0 : 1.0 + tanh( phi * (H - epsilon));\n\tdestColor = vec3(edge);\n\tgl_FragColor = vec4(destColor, 1.0);\n}",LiteGraph.registerNodeType("texture/xDoG",LGraphTextureXDoGFilter),LGraphTextureWebcam.title="Webcam",LGraphTextureWebcam.desc="Webcam texture",LGraphTextureWebcam.is_webcam_open=!1,LGraphTextureWebcam.prototype.openStream=function(){var constraints,that;navigator.getUserMedia&&(constraints={audio:!(this._waiting_confirmation=!0),video:{facingMode:this.properties.facingMode}},navigator.mediaDevices.getUserMedia(constraints).then(this.streamReady.bind(this)).catch(function(e){LGraphTextureWebcam.is_webcam_open=!1,console.log("Webcam rejected",e),that._webcam_stream=!1,that.boxcolor="red",that.trigger("stream_error")}),that=this)},LGraphTextureWebcam.prototype.closeStream=function(){if(this._webcam_stream){var tracks=this._webcam_stream.getTracks();if(tracks.length)for(var i=0;iinputs_y+LiteGraph.NODE_TITLE_HEIGHT&&ctx.drawImage(tex,10,y,this.size[0]-20,this.size[1]-inputs_y-LiteGraph.NODE_TITLE_HEIGHT),y=this.size[1]-LiteGraph.NODE_TITLE_HEIGHT+.5,tex=LiteGraph.isInsideRectangle(pos[0],pos[1],this.pos[0],this.pos[1]+y,this.size[0],LiteGraph.NODE_TITLE_HEIGHT),ctx.fillStyle=tex?"#555":"#222",ctx.beginPath(),this._shape==LiteGraph.BOX_SHAPE?ctx.rect(0,y,this.size[0]+1,LiteGraph.NODE_TITLE_HEIGHT):ctx.roundRect(0,y,this.size[0]+1,LiteGraph.NODE_TITLE_HEIGHT,0,8),ctx.fill(),ctx.textAlign="center",ctx.font="24px Arial",ctx.fillStyle=tex?"#DDD":"#999",ctx.fillText("+",.5*this.size[0],y+24))},LGraphShaderGraph.prototype.onMouseDown=function(e,localpos,graphcanvas){var y=this.size[1]-LiteGraph.NODE_TITLE_HEIGHT+.5;localpos[1]>y&&graphcanvas.showSubgraphPropertiesDialog(this)},LGraphShaderGraph.prototype.onDrawSubgraphBackground=function(graphcanvas){},LGraphShaderGraph.prototype.getExtraMenuOptions=function(graphcanvas){var that=this;return[{content:"Print Code",callback:function(){var code=that._context.computeShaderCode();console.log(code.vs_code,code.fs_code)}}]},LiteGraph.registerNodeType("texture/shaderGraph",LGraphShaderGraph),LGraphShaderUniform.title="Uniform",LGraphShaderUniform.desc="Input data for the shader",LGraphShaderUniform.prototype.getTitle=function(){return this.properties.name&&this.flags.collapsed?this.properties.type+" "+this.properties.name:"Uniform"},LGraphShaderUniform.prototype.onPropertyChanged=function(name,value){this.outputs[0].name=this.properties.type+" "+this.properties.name},LGraphShaderUniform.prototype.onGetCode=function(context){if(this.shader_destination){var type=this.properties.type;if(!type){if(!context.onGetPropertyInfo)return;var info=context.onGetPropertyInfo(this.property.name);if(!info)return;type=info.type}"number"==type?type="float":"texture"==type&&(type="sampler2D"),-1!=LGShaders.GLSL_types.indexOf(type)&&(context.addUniform("u_"+this.properties.name,type),this.setOutputData(0,type))}},LGraphShaderUniform.prototype.getOutputVarName=function(slot){return"u_"+this.properties.name},registerShaderNode("input/uniform",LGraphShaderUniform),LGraphShaderAttribute.title="Attribute",LGraphShaderAttribute.desc="Input data from mesh attribute",LGraphShaderAttribute.prototype.getTitle=function(){return"att. "+this.properties.name},LGraphShaderAttribute.prototype.onGetCode=function(context){var type;this.shader_destination&&(type=this.properties.type)&&-1!=LGShaders.GLSL_types.indexOf(type)&&("number"==type&&(type="float"),"coord"!=this.properties.name&&context.addCode("varying"," varying "+type+" v_"+this.properties.name+";"),this.setOutputData(0,type))},LGraphShaderAttribute.prototype.getOutputVarName=function(slot){return"v_"+this.properties.name},registerShaderNode("input/attribute",LGraphShaderAttribute),LGraphShaderSampler2D.title="Sampler2D",LGraphShaderSampler2D.desc="Reads a pixel from a texture",LGraphShaderSampler2D.prototype.onGetCode=function(context){var texname,varname,code;this.shader_destination&&(texname=getInputLinkID(this,0),code="vec4 "+(varname=getShaderNodeVarName(this))+" = vec4(0.0);\n",texname&&(code+=varname+" = texture2D("+texname+","+(getInputLinkID(this,1)||context.buffer_names.uvs)+");\n"),getOutputLinkID(this,0)&&(code+="vec4 "+getOutputLinkID(this,0)+" = "+varname+";\n"),getOutputLinkID(this,1)&&(code+="vec3 "+getOutputLinkID(this,1)+" = "+varname+".xyz;\n"),context.addCode("code",code,this.shader_destination),this.setOutputData(0,"vec4"),this.setOutputData(1,"vec3"))},registerShaderNode("texture/sampler2D",LGraphShaderSampler2D),LGraphShaderConstant.title="const",LGraphShaderConstant.prototype.getTitle=function(){return this.flags.collapsed?valueToGLSL(this.properties.value,this.properties.type,2):"Const"},LGraphShaderConstant.prototype.onPropertyChanged=function(name,value){"type"==name&&(this.outputs[0].type!=value&&(this.disconnectOutput(0),this.outputs[0].type=value),this.widgets.length=1,this.updateWidgets()),"value"==name&&(value.length?(this.widgets[1].value=value[1],2this.properties.max_value)return}this.trigger("on_midi",midi_event)}},LiteGraph.registerNodeType("midi/filter",LGMIDIFilter),LGMIDIEvent.title="MIDIEvent",LGMIDIEvent.desc="Create a MIDI Event",LGMIDIEvent.color="#243",LGMIDIEvent.prototype.onAction=function(event,midi_event){"assign"==event?(this.properties.channel=midi_event.channel,this.properties.cmd=midi_event.cmd,this.properties.value1=midi_event.data[1],this.properties.value2=midi_event.data[2],midi_event.cmd==MIDIEvent.NOTEON?this.gate=!0:midi_event.cmd==MIDIEvent.NOTEOFF&&(this.gate=!1)):((midi_event=this.midi_event).channel=this.properties.channel,this.properties.cmd&&this.properties.cmd.constructor===String?midi_event.setCommandFromString(this.properties.cmd):midi_event.cmd=this.properties.cmd,midi_event.data[0]=midi_event.cmd|midi_event.channel,midi_event.data[1]=Number(this.properties.value1),midi_event.data[2]=Number(this.properties.value2),this.trigger("on_midi",midi_event))},LGMIDIEvent.prototype.onExecute=function(){var props=this.properties;if(this.inputs)for(var i=0;ix+w||pos[1]>key_info))return i}}return-1},LGMIDIKeys.prototype.onAction=function(event,params){if("reset"==event)for(var i=0;i=this.size[0]&&(x=this.size[0]-1),ctx.strokeStyle="red",ctx.beginPath(),ctx.moveTo(x,h),ctx.lineTo(x,0),ctx.stroke())}},LGAudioVisualization.title="Visualization",LGAudioVisualization.desc="Audio Visualization",LiteGraph.registerNodeType("audio/visualization",LGAudioVisualization),LGAudioBandSignal.prototype.onExecute=function(){var v,pos,band;this._freqs=this.getInputData(0),this._freqs&&(band=this.properties.band,(band=(band=void 0!==(v=this.getInputData(1))?v:band)/(LGAudio.getAudioContext().sampleRate/this._freqs.length)*2)<(v=0)&&(v=this._freqs[0]),v=band>=this._freqs.length?this._freqs[this._freqs.length-1]:this._freqs[pos=0|band]*(1-(band=band-pos))+this._freqs[1+pos]*band,this.setOutputData(0,v/255*this.properties.amplitude))},LGAudioBandSignal.prototype.onGetInputs=function(){return[["band","number"]]},LGAudioBandSignal.title="Signal",LGAudioBandSignal.desc="extract the signal of some frequency",LiteGraph.registerNodeType("audio/signal",LGAudioBandSignal),LGAudioScript.prototype.onAdded=function(graph){graph.status==LGraph.STATUS_RUNNING&&(this.audionode.onaudioprocess=this._callback)},LGAudioScript["@code"]={widget:"code",type:"code"},LGAudioScript.prototype.onStart=function(){this.audionode.onaudioprocess=this._callback},LGAudioScript.prototype.onStop=function(){this.audionode.onaudioprocess=LGAudioScript._bypass_function},LGAudioScript.prototype.onPause=function(){this.audionode.onaudioprocess=LGAudioScript._bypass_function},LGAudioScript.prototype.onUnpause=function(){this.audionode.onaudioprocess=this._callback},LGAudioScript.prototype.onExecute=function(){},LGAudioScript.prototype.onRemoved=function(){this.audionode.onaudioprocess=LGAudioScript._bypass_function},LGAudioScript.prototype.processCode=function(){try{var func=new Function("properties",this.properties.code);this._script=new func(this.properties),this._old_code=this.properties.code,this._callback=this._script.onaudioprocess}catch(err){console.error("Error in onaudioprocess code",err),this._callback=LGAudioScript._bypass_function,this.audionode.onaudioprocess=this._callback}},LGAudioScript.prototype.onPropertyChanged=function(name,value){"code"==name&&(this.properties.code=value,this.processCode(),this.graph)&&this.graph.status==LGraph.STATUS_RUNNING&&(this.audionode.onaudioprocess=this._callback)},LGAudioScript.default_function=function(){this.onaudioprocess=function(audioProcessingEvent){for(var inputBuffer=audioProcessingEvent.inputBuffer,outputBuffer=audioProcessingEvent.outputBuffer,channel=0;channel{if(resp.ok)return this.boxcolor="#0F0",resp.text();this.boxcolor="#F00",that.trigger("error")}).then(data=>{that._data=data,that._fetching=null,that.trigger("ready")}))},HTTPRequestNode.prototype.onAction=function(evt){"request"==evt&&this.fetch()},HTTPRequestNode.prototype.onExecute=function(){this.setOutputData(1,this._data)},HTTPRequestNode.prototype.onGetOutputs=function(){return[["error",LiteGraph.EVENT]]},LiteGraph.registerNodeType("network/httprequest",HTTPRequestNode)}(this); + diff --git a/build/litegraph.min.js b/build/litegraph.min.js deleted file mode 100644 index 3cdfbb345..000000000 --- a/build/litegraph.min.js +++ /dev/null @@ -1,915 +0,0 @@ -var $jscomp=$jscomp||{};$jscomp.scope={};$jscomp.ASSUME_ES5=!1;$jscomp.ASSUME_NO_NATIVE_MAP=!1;$jscomp.ASSUME_NO_NATIVE_SET=!1;$jscomp.defineProperty=$jscomp.ASSUME_ES5||"function"==typeof Object.defineProperties?Object.defineProperty:function(y,c,k){y!=Array.prototype&&y!=Object.prototype&&(y[c]=k.value)};$jscomp.getGlobal=function(y){return"undefined"!=typeof window&&window===y?y:"undefined"!=typeof global&&null!=global?global:y};$jscomp.global=$jscomp.getGlobal(this);$jscomp.SYMBOL_PREFIX="jscomp_symbol_"; -$jscomp.initSymbol=function(){$jscomp.initSymbol=function(){};$jscomp.global.Symbol||($jscomp.global.Symbol=$jscomp.Symbol)};$jscomp.Symbol=function(){var y=0;return function(c){return $jscomp.SYMBOL_PREFIX+(c||"")+y++}}(); -$jscomp.initSymbolIterator=function(){$jscomp.initSymbol();var y=$jscomp.global.Symbol.iterator;y||(y=$jscomp.global.Symbol.iterator=$jscomp.global.Symbol("iterator"));"function"!=typeof Array.prototype[y]&&$jscomp.defineProperty(Array.prototype,y,{configurable:!0,writable:!0,value:function(){return $jscomp.arrayIterator(this)}});$jscomp.initSymbolIterator=function(){}};$jscomp.arrayIterator=function(y){var c=0;return $jscomp.iteratorPrototype(function(){return ck&&(k=Math.max(0,r+k));if(null==n||n>r)n=r;n=Number(n);0>n&&(n=Math.max(0,r+n));for(k=Number(k||0);k=u}},"es6","es3");$jscomp.findInternal=function(y,c,k){y instanceof String&&(y=String(y));for(var n=y.length,r=0;rc?-k:k}},"es6","es3"); -(function(y){function c(a){e.debug&&console.log("Graph created");this.list_of_graphcanvas=null;this.clear();a&&this.configure(a)}function k(a,b,d,f,g,e){this.id=a;this.type=b;this.origin_id=d;this.origin_slot=f;this.target_id=g;this.target_slot=e;this._data=null;this._pos=new Float32Array(2)}function n(a){this._ctor(a)}function r(a){this._ctor(a)}function u(a,b){this.offset=new Float32Array([0,0]);this.scale=1;this.max_scale=10;this.min_scale=.1;this.onredraw=null;this.enabled=!0;this.last_mouse= -[0,0];this.element=null;this.visible_area=new Float32Array(4);a&&(this.element=a,b||this.bindEvents(a))}function h(a,b,d){this.options=d=d||{};this.background_image=h.DEFAULT_BACKGROUND_IMAGE;a&&a.constructor===String&&(a=document.querySelector(a));this.ds=new u;this.zoom_modify_alpha=!0;this.title_text_font=""+e.NODE_TEXT_SIZE+"px Arial";this.inner_text_font="normal "+e.NODE_SUBTEXT_SIZE+"px Arial";this.node_title_color=e.NODE_TITLE_COLOR;this.default_link_color=e.LINK_COLOR;this.default_connection_color= -{input_off:"#778",input_on:"#7F7",output_off:"#778",output_on:"#7F7"};this.default_connection_color_byType={};this.default_connection_color_byTypeOff={};this.highquality_render=!0;this.use_gradients=!1;this.editor_alpha=1;this.pause_rendering=!1;this.clear_background=!0;this.clear_background_color="#222";this.read_only=!1;this.render_only_selected=!0;this.live_mode=!1;this.allow_interaction=this.allow_dragnodes=this.allow_dragcanvas=this.show_info=!0;this.multi_select=!1;this.allow_reconnect_links= -this.allow_searchbox=!0;this.drag_mode=this.align_to_grid=!1;this.filter=this.dragging_rectangle=null;this.set_canvas_dirty_on_mouse_event=!0;this.always_render_background=!1;this.render_canvas_border=this.render_shadows=!0;this.render_connections_shadows=!1;this.render_connections_border=!0;this.render_connection_arrows=this.render_curved_connections=!1;this.render_collapsed_slots=!0;this.render_execution_order=!1;this.render_link_tooltip=this.render_title_colored=!0;this.links_render_mode=e.SPLINE_LINK; -this.mouse=[0,0];this.canvas_mouse=this.graph_mouse=[0,0];this.onAfterChange=this.onBeforeChange=this.onConnectingChange=this.onSelectionChange=this.onNodeMoved=this.onDrawLinkTooltip=this.onDrawOverlay=this.onDrawForeground=this.onDrawBackground=this.onMouse=this.onSearchBoxSelection=this.onSearchBox=null;this.connections_width=3;this.round_radius=8;this.over_link_center=this.node_widget=this.current_node=null;this.last_mouse_position=[0,0];this.visible_area=this.ds.visible_area;this.visible_links= -[];this.viewport=d.viewport||null;b&&b.attachCanvas(this);this.setCanvas(a,d.skip_events);this.clear();d.skip_render||this.startRendering();this.autoresize=d.autoresize}function E(a,b){return Math.sqrt((b[0]-a[0])*(b[0]-a[0])+(b[1]-a[1])*(b[1]-a[1]))}function D(a,b,d,f,g,e){return da&&fb?!0:!1}function J(a,b){var d=a[0]+a[2],f=a[1]+a[3],g=b[1]+b[3];return a[0]>b[0]+b[2]||a[1]>g||dz.width-c.width-10&&(g=z.width-c.width-10),z.height&&a>z.height-c.height-10&&(a=z.height-c.height-10));q.style.left=g+"px";q.style.top=a+"px";b.scale&&(q.style.transform="scale("+b.scale+")")}function H(a){this.points=a;this.nearest=this.selected=-1;this.size=null;this.must_update=!0;this.margin=5}function F(a,b,d){return b>a?b:d>a/4).toString(16)})},isValidConnection:function(a, -b){if(""==a||"*"===a)a=0;if(""==b||"*"===b)b=0;if(!a||!b||a==b||a==e.EVENT&&b==e.ACTION)return!0;a=String(a);b=String(b);a=a.toLowerCase();b=b.toLowerCase();if(-1==a.indexOf(",")&&-1==b.indexOf(","))return a==b;a=a.split(",");b=b.split(",");for(var d=0;dz&&(z=t.size[v]);c+=t.size[b==e.VERTICAL_LAYOUT?0:1]+a+e.NODE_TITLE_HEIGHT}d+=z+a}this.setDirtyCanvas(!0,!0)};c.prototype.getTime=function(){return this.globaltime};c.prototype.getFixedTime=function(){return this.fixedtime};c.prototype.getElapsedTime=function(){return this.elapsed_time};c.prototype.sendEventToAllNodes=function(a, -b,d){d=d||e.ALWAYS;var f=this._nodes_in_order?this._nodes_in_order:this._nodes;if(f)for(var g=0,q=f.length;g=e.MAX_NUMBER_OF_NODES)throw"LiteGraph: max number of nodes in a graph reached";if(e.use_uuids){if(null==a.id||-1==a.id)a.id=e.uuidv4()}else null==a.id||-1==a.id? -a.id=++this.last_node_id:this.last_node_ida.length||(this._pos[0]=a[0],this._pos[1]=a[1])},get:function(){return this._pos},enumerable:!0});this.id=e.use_uuids?e.uuidv4():-1;this.type=null;this.inputs=[];this.outputs=[];this.connections=[];this.properties={};this.properties_info=[];this.flags={}};n.prototype.configure=function(a){this.graph&& -this.graph._version++;for(var b in a)if("properties"==b)for(var d in a.properties){if(this.properties[d]=a.properties[d],this.onPropertyChanged)this.onPropertyChanged(d,a.properties[d])}else null!=a[b]&&("object"==typeof a[b]?this[b]&&this[b].configure?this[b].configure(a[b]):this[b]=e.cloneObject(a[b],this[b]):this[b]=a[b]);a.title||(this.title=this.constructor.title);if(this.inputs)for(d=0;d=this.outputs.length)){var d=this.outputs[a];if(d&&(d._data=b,this.outputs[a].links))for(d=0;d=this.outputs.length)){var d=this.outputs[a];if(d&&(d.type=b,this.outputs[a].links))for(d=0;d=this.inputs.length||null==this.inputs[a].link)){a=this.graph.links[this.inputs[a].link];if(!a)return null;if(!b)return a.data;b=this.graph.getNodeById(a.origin_id);if(!b)return a.data;if(b.updateOutputData)b.updateOutputData(a.origin_slot);else if(b.onExecute)b.onExecute();return a.data}};n.prototype.getInputDataType=function(a){if(!this.inputs||a>=this.inputs.length||null==this.inputs[a].link)return null;a=this.graph.links[this.inputs[a].link];if(!a)return null; -var b=this.graph.getNodeById(a.origin_id);return b?(a=b.outputs[a.origin_slot])?a.type:null:a.type};n.prototype.getInputDataByName=function(a,b){a=this.findInputSlot(a);return-1==a?null:this.getInputData(a,b)};n.prototype.isInputConnected=function(a){return this.inputs?a=this.inputs.length)return null;a=this.inputs[a];return a&&null!==a.link?(a=this.graph.links[a.link])?this.graph.getNodeById(a.origin_id):null:null};n.prototype.getInputOrProperty=function(a){if(!this.inputs||!this.inputs.length)return this.properties?this.properties[a]:null;for(var b=0,d=this.inputs.length;b=this.outputs.length?null:this.outputs[a]._data};n.prototype.getOutputInfo=function(a){return this.outputs?a=this.outputs.length)return null;a=this.outputs[a];if(!a.links||0==a.links.length)return null;for(var b=[],d=0;da&&this.pos[1]-g-db)return!0;return!1};n.prototype.getSlotInPosition=function(a,b){var d=new Float32Array(2);if(this.inputs)for(var f=0,g=this.inputs.length;f=this.outputs.length)return e.debug&&console.log("Connect: Error, slot number not found"),null;b&&b.constructor=== -Number&&(b=this.graph.getNodeById(b));if(!b)throw"target node is null";if(b==this)return null;if(d.constructor===String){if(d=b.findInputSlot(d),-1==d)return e.debug&&console.log("Connect: Error, no slot of name "+d),null}else if(d===e.EVENT)if(e.do_add_triggers_slots)b.changeMode(e.ON_TRIGGER),d=b.findInputSlot("onTrigger");else return null;else if(!b.inputs||d>=b.inputs.length)return e.debug&&console.log("Connect: Error, slot number not found"),null;var f=b.inputs[d],g=this.outputs[a];if(!this.outputs[a])return null; -b.onBeforeConnectInput&&(d=b.onBeforeConnectInput(d));if(!1===d||null===d||!e.isValidConnection(g.type,f.type))return this.setDirtyCanvas(!1,!0),null;if(b.onConnectInput&&!1===b.onConnectInput(d,g.type,g,this,a)||this.onConnectOutput&&!1===this.onConnectOutput(a,f.type,f,b,d))return null;b.inputs[d]&&null!=b.inputs[d].link&&(this.graph.beforeChange(),b.disconnectInput(d,{doProcessChange:!1}));if(null!==g.links&&g.links.length)switch(g.type){case e.EVENT:e.allow_multi_output_for_events||(this.graph.beforeChange(), -this.disconnectOutput(a,!1,{doProcessChange:!1}))}var q=e.use_uuids?e.uuidv4():++this.graph.last_link_id;q=new k(q,f.type||g.type,this.id,a,b.id,d);this.graph.links[q.id]=q;null==g.links&&(g.links=[]);g.links.push(q.id);b.inputs[d].link=q.id;this.graph&&this.graph._version++;if(this.onConnectionsChange)this.onConnectionsChange(e.OUTPUT,a,!0,q,g);if(b.onConnectionsChange)b.onConnectionsChange(e.INPUT,d,!0,q,f);this.graph&&this.graph.onNodeConnectionChange&&(this.graph.onNodeConnectionChange(e.INPUT, -b,d,this,a),this.graph.onNodeConnectionChange(e.OUTPUT,this,a,b,d));this.setDirtyCanvas(!1,!0);this.graph.afterChange();this.graph.connectionChange(this,q);return q};n.prototype.disconnectOutput=function(a,b){if(a.constructor===String){if(a=this.findOutputSlot(a),-1==a)return e.debug&&console.log("Connect: Error, no slot of name "+a),!1}else if(!this.outputs||a>=this.outputs.length)return e.debug&&console.log("Connect: Error, slot number not found"),!1;var d=this.outputs[a];if(!d||!d.links||0==d.links.length)return!1; -if(b){b.constructor===Number&&(b=this.graph.getNodeById(b));if(!b)throw"Target Node not found";for(var f=0,g=d.links.length;f=this.inputs.length)return e.debug&&console.log("Connect: Error, slot number not found"),!1;var b=this.inputs[a];if(!b)return!1;var d=this.inputs[a].link;if(null!=d){this.inputs[a].link=null;var f=this.graph.links[d];if(f){var g=this.graph.getNodeById(f.origin_id);if(!g)return!1;var q=g.outputs[f.origin_slot];if(!q||!q.links||0==q.links.length)return!1;for(var c=0,l=q.links.length;cb&&this.inputs[b].pos)return d[0]=this.pos[0]+this.inputs[b].pos[0],d[1]=this.pos[1]+ -this.inputs[b].pos[1],d;if(!a&&f>b&&this.outputs[b].pos)return d[0]=this.pos[0]+this.outputs[b].pos[0],d[1]=this.pos[1]+this.outputs[b].pos[1],d;if(this.horizontal)return d[0]=this.pos[0]+this.size[0]/f*(b+.5),d[1]=a?this.pos[1]-e.NODE_TITLE_HEIGHT:this.pos[1]+this.size[1],d;d[0]=a?this.pos[0]+g:this.pos[0]+this.size[0]+1-g;d[1]=this.pos[1]+(b+.7)*e.NODE_SLOT_HEIGHT+(this.constructor.slot_start_y||0);return d};n.prototype.alignToGrid=function(){this.pos[0]=e.CANVAS_GRID_SIZE*Math.round(this.pos[0]/ -e.CANVAS_GRID_SIZE);this.pos[1]=e.CANVAS_GRID_SIZE*Math.round(this.pos[1]/e.CANVAS_GRID_SIZE)};n.prototype.trace=function(a){this.console||(this.console=[]);this.console.push(a);this.console.length>n.MAX_CONSOLE&&this.console.shift();if(this.graph.onNodeTrace)this.graph.onNodeTrace(this,a)};n.prototype.setDirtyCanvas=function(a,b){this.graph&&this.graph.sendActionToCanvas("setDirty",[a,b])};n.prototype.loadImage=function(a){var b=new Image;b.src=e.node_images_path+a;b.ready=!1;var d=this;b.onload= -function(){this.ready=!0;d.setDirtyCanvas(!0)};return b};n.prototype.captureInput=function(a){if(this.graph&&this.graph.list_of_graphcanvas)for(var b=this.graph.list_of_graphcanvas,d=0;da.length||(this._pos[0]=a[0],this._pos[1]=a[1])},get:function(){return this._pos},enumerable:!0});Object.defineProperty(this,"size",{set:function(a){!a||2>a.length||(this._size[0]=Math.max(140,a[0]),this._size[1]=Math.max(80,a[1]))},get:function(){return this._size},enumerable:!0})};r.prototype.configure=function(a){this.title=a.title;this._bounding.set(a.bounding);this.color=a.color;this.font_size=a.font_size};r.prototype.serialize=function(){var a= -this._bounding;return{title:this.title,bounding:[Math.round(a[0]),Math.round(a[1]),Math.round(a[2]),Math.round(a[3])],color:this.color,font_size:this.font_size}};r.prototype.move=function(a,b,d){this._pos[0]+=a;this._pos[1]+=b;if(!d)for(d=0;d=this.viewport[0]&&f=this.viewport[1]&&dthis.max_scale&&(a=this.max_scale);if(a!=this.scale&&this.element){var d=this.element.getBoundingClientRect();if(d&&(b= -b||[.5*d.width,.5*d.height],d=this.convertCanvasToOffset(b),this.scale=a,.01>Math.abs(this.scale-1)&&(this.scale=1),a=this.convertCanvasToOffset(b),a=[a[0]-d[0],a[1]-d[1]],this.offset[0]+=a[0],this.offset[1]+=a[1],this.onredraw))this.onredraw(this)}};u.prototype.changeDeltaScale=function(a,b){this.changeScale(this.scale*a,b)};u.prototype.reset=function(){this.scale=1;this.offset[0]=0;this.offset[1]=0};y.LGraphCanvas=e.LGraphCanvas=h;h.DEFAULT_BACKGROUND_IMAGE="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAIAAAD/gAIDAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAQBJREFUeNrs1rEKwjAUhlETUkj3vP9rdmr1Ysammk2w5wdxuLgcMHyptfawuZX4pJSWZTnfnu/lnIe/jNNxHHGNn//HNbbv+4dr6V+11uF527arU7+u63qfa/bnmh8sWLBgwYJlqRf8MEptXPBXJXa37BSl3ixYsGDBMliwFLyCV/DeLIMFCxYsWLBMwSt4Be/NggXLYMGCBUvBK3iNruC9WbBgwYJlsGApeAWv4L1ZBgsWLFiwYJmCV/AK3psFC5bBggULloJX8BpdwXuzYMGCBctgwVLwCl7Be7MMFixYsGDBsu8FH1FaSmExVfAxBa/gvVmwYMGCZbBg/W4vAQYA5tRF9QYlv/QAAAAASUVORK5CYII="; -h.link_type_colors={"-1":e.EVENT_LINK_COLOR,number:"#AAA",node:"#DCA"};h.gradients={};h.prototype.clear=function(){this.fps=this.render_time=this.last_draw_time=this.frame=0;this.dragging_rectangle=null;this.selected_nodes={};this.selected_group=null;this.visible_nodes=[];this.connecting_node=this.node_capturing_input=this.node_over=this.node_dragged=null;this.highlighted_links={};this.dragging_canvas=!1;this.dirty_bgcanvas=this.dirty_canvas=!0;this.node_widget=this.node_in_panel=this.dirty_area= -null;this.last_mouse=[0,0];this.last_mouseclick=0;this.pointer_is_double=this.pointer_is_down=!1;this.visible_area.set([0,0,0,0]);if(this.onClear)this.onClear()};h.prototype.setGraph=function(a,b){this.graph!=a&&(b||this.clear(),!a&&this.graph?this.graph.detachCanvas(this):(a.attachCanvas(this),this._graph_stack&&(this._graph_stack=null),this.setDirty(!0,!0)))};h.prototype.getTopGraph=function(){return this._graph_stack.length?this._graph_stack[0]:this.graph};h.prototype.openSubgraph=function(a){if(!a)throw"graph cannot be null"; -if(this.graph==a)throw"graph cannot be the same";this.clear();this.graph&&(this._graph_stack||(this._graph_stack=[]),this._graph_stack.push(this.graph));a.attachCanvas(this);this.checkPanels();this.setDirty(!0,!0)};h.prototype.closeSubgraph=function(){if(this._graph_stack&&0!=this._graph_stack.length){var a=this.graph._subgraph_node,b=this._graph_stack.pop();this.selected_nodes={};this.highlighted_links={};b.attachCanvas(this);this.setDirty(!0,!0);a&&(this.centerOnNode(a),this.selectNodes([a]));this.ds.offset= -[0,0];this.ds.scale=1}};h.prototype.getCurrentGraph=function(){return this.graph};h.prototype.setCanvas=function(a,b){if(a&&a.constructor===String&&(a=document.getElementById(a),!a))throw"Error creating LiteGraph canvas: Canvas not found";if(a!==this.canvas&&(!a&&this.canvas&&(b||this.unbindEvents()),this.canvas=a,this.ds.element=a)){a.className+=" lgraphcanvas";a.data=this;a.tabindex="1";this.bgcanvas=null;this.bgcanvas||(this.bgcanvas=document.createElement("canvas"),this.bgcanvas.width=this.canvas.width, -this.bgcanvas.height=this.canvas.height);if(null==a.getContext){if("canvas"!=a.localName)throw"Element supplied for LGraphCanvas must be a element, you passed a "+a.localName;throw"This browser doesn't support Canvas";}null==(this.ctx=a.getContext("2d"))&&(a.webgl_enabled||console.warn("This canvas seems to be WebGL, enabling WebGL renderer"),this.enableWebGL());b||this.bindEvents()}};h.prototype._doNothing=function(a){a.preventDefault();return!1};h.prototype._doReturnTrue=function(a){a.preventDefault(); -return!0};h.prototype.bindEvents=function(){if(this._events_binded)console.warn("LGraphCanvas: events already binded");else{var a=this.canvas,b=this.getCanvasWindow().document;this._mousedown_callback=this.processMouseDown.bind(this);this._mousewheel_callback=this.processMouseWheel.bind(this);this._mousemove_callback=this.processMouseMove.bind(this);this._mouseup_callback=this.processMouseUp.bind(this);e.pointerListenerAdd(a,"down",this._mousedown_callback,!0);a.addEventListener("mousewheel",this._mousewheel_callback, -!1);e.pointerListenerAdd(a,"up",this._mouseup_callback,!0);e.pointerListenerAdd(a,"move",this._mousemove_callback);a.addEventListener("contextmenu",this._doNothing);a.addEventListener("DOMMouseScroll",this._mousewheel_callback,!1);this._key_callback=this.processKey.bind(this);a.addEventListener("keydown",this._key_callback,!0);b.addEventListener("keyup",this._key_callback,!0);this._ondrop_callback=this.processDrop.bind(this);a.addEventListener("dragover",this._doNothing,!1);a.addEventListener("dragend", -this._doNothing,!1);a.addEventListener("drop",this._ondrop_callback,!1);a.addEventListener("dragenter",this._doReturnTrue,!1);this._events_binded=!0}};h.prototype.unbindEvents=function(){if(this._events_binded){var a=this.getCanvasWindow().document;e.pointerListenerRemove(this.canvas,"move",this._mousedown_callback);e.pointerListenerRemove(this.canvas,"up",this._mousedown_callback);e.pointerListenerRemove(this.canvas,"down",this._mousedown_callback);this.canvas.removeEventListener("mousewheel",this._mousewheel_callback); -this.canvas.removeEventListener("DOMMouseScroll",this._mousewheel_callback);this.canvas.removeEventListener("keydown",this._key_callback);a.removeEventListener("keyup",this._key_callback);this.canvas.removeEventListener("contextmenu",this._doNothing);this.canvas.removeEventListener("drop",this._ondrop_callback);this.canvas.removeEventListener("dragenter",this._doReturnTrue);this._ondrop_callback=this._key_callback=this._mousewheel_callback=this._mousedown_callback=null;this._events_binded=!1}else console.warn("LGraphCanvas: no events binded")}; -h.getFileExtension=function(a){var b=a.indexOf("?");-1!=b&&(a=a.substr(0,b));b=a.lastIndexOf(".");return-1==b?"":a.substr(b+1).toLowerCase()};h.prototype.enableWebGL=function(){if("undefined"===typeof GL)throw"litegl.js must be included to use a WebGL canvas";if("undefined"===typeof enableWebGLCanvas)throw"webglCanvas.js must be included to use this feature";this.gl=this.ctx=enableWebGLCanvas(this.canvas);this.ctx.webgl=!0;this.bgcanvas=this.canvas;this.bgctx=this.gl;this.canvas.webgl_enabled=!0}; -h.prototype.setDirty=function(a,b){a&&(this.dirty_canvas=!0);b&&(this.dirty_bgcanvas=!0)};h.prototype.getCanvasWindow=function(){if(!this.canvas)return window;var a=this.canvas.ownerDocument;return a.defaultView||a.parentWindow};h.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))};h.prototype.stopRendering=function(){this.is_rendering= -!1};h.prototype.blockClick=function(){this.block_click=!0;this.last_mouseclick=0};h.prototype.processMouseDown=function(a){this.set_canvas_dirty_on_mouse_event&&(this.dirty_canvas=!0);if(this.graph){this.adjustMouseEvent(a);var b=this.getCanvasWindow();h.active_canvas=this;var d=this,f=a.clientX,g=a.clientY;this.ds.viewport=this.viewport;f=!this.viewport||this.viewport&&f>=this.viewport[0]&&f=this.viewport[1]&&gg-this.last_mouseclick&&c;this.mouse[0]=a.clientX;this.mouse[1]=a.clientY;this.graph_mouse[0]=a.canvasX;this.graph_mouse[1]=a.canvasY;this.last_click_position= -[this.mouse[0],this.mouse[1]];this.pointer_is_double=this.pointer_is_down&&c?!0:!1;this.pointer_is_down=!0;this.canvas.focus();e.closeAllContextMenus(b);if(!this.onMouse||1!=this.onMouse(a)){if(1!=a.which||this.pointer_is_double)if(2==a.which)if(e.middle_click_slot_add_default_node){if(q&&this.allow_interaction&&!f&&!this.read_only&&!this.connecting_node&&!q.flags.collapsed&&!this.live_mode){g=f=c=!1;if(q.outputs)for(v=0,l=q.outputs.length;vq.size[0]-e.NODE_TITLE_HEIGHT&& -0>l[1]&&(d=this,setTimeout(function(){d.openSubgraph(q.subgraph)},10)),this.live_mode&&(v=c=!0));v?q.is_selected||this.processNodeSelected(q,a):(this.allow_dragnodes&&(this.graph.beforeChange(),this.node_dragged=q),this.processNodeSelected(q,a));this.dirty_canvas=!0}}else if(!f){if(!this.read_only)for(v=0;vl[0]+4||a.canvasYl[1]+4)){this.showLinkMenu(c,a);this.over_link_center=null; -break}this.selected_group=this.graph.getGroupOnPos(a.canvasX,a.canvasY);this.selected_group_resizing=!1;this.selected_group&&!this.read_only&&(a.ctrlKey&&(this.dragging_rectangle=null),10>E([a.canvasX,a.canvasY],[this.selected_group.pos[0]+this.selected_group.size[0],this.selected_group.pos[1]+this.selected_group.size[1]])*this.ds.scale?this.selected_group_resizing=!0:this.selected_group.recomputeInsideNodes());g&&!this.read_only&&this.allow_searchbox&&(this.showSearchBox(a),a.preventDefault(),a.stopPropagation()); -c=!0}!f&&c&&this.allow_dragcanvas&&(this.dragging_canvas=!0)}this.last_mouse[0]=a.clientX;this.last_mouse[1]=a.clientY;this.last_mouseclick=e.getTime();this.last_mouse_dragging=!0;this.graph.change();(!b.document.activeElement||"input"!=b.document.activeElement.nodeName.toLowerCase()&&"textarea"!=b.document.activeElement.nodeName.toLowerCase())&&a.preventDefault();a.stopPropagation();if(this.onMouseDown)this.onMouseDown(a);return!1}}}};h.prototype.processMouseMove=function(a){this.autoresize&&this.resize(); -this.set_canvas_dirty_on_mouse_event&&(this.dirty_canvas=!0);if(this.graph){h.active_canvas=this;this.adjustMouseEvent(a);var b=[a.clientX,a.clientY];this.mouse[0]=b[0];this.mouse[1]=b[1];var d=[b[0]-this.last_mouse[0],b[1]-this.last_mouse[1]];this.last_mouse=b;this.graph_mouse[0]=a.canvasX;this.graph_mouse[1]=a.canvasY;if(this.block_click)return a.preventDefault(),!1;a.dragging=this.last_mouse_dragging;this.node_widget&&(this.processNodeWidgets(this.node_widget[0],this.graph_mouse,a,this.node_widget[1]), -this.dirty_canvas=!0);var f=this.graph.getNodeOnPos(a.canvasX,a.canvasY,this.visible_nodes);if(this.dragging_rectangle)this.dragging_rectangle[2]=a.canvasX-this.dragging_rectangle[0],this.dragging_rectangle[3]=a.canvasY-this.dragging_rectangle[1],this.dirty_canvas=!0;else if(this.selected_group&&!this.read_only)this.selected_group_resizing?this.selected_group.size=[a.canvasX-this.selected_group.pos[0],a.canvasY-this.selected_group.pos[1]]:(this.selected_group.move(d[0]/this.ds.scale,d[1]/this.ds.scale, -a.ctrlKey),this.selected_group._nodes.length&&(this.dirty_canvas=!0)),this.dirty_bgcanvas=!0;else if(this.dragging_canvas)this.ds.offset[0]+=d[0]/this.ds.scale,this.ds.offset[1]+=d[1]/this.ds.scale,this.dirty_bgcanvas=this.dirty_canvas=!0;else if((this.allow_interaction||f&&f.flags.allow_interaction)&&!this.read_only){this.connecting_node&&(this.dirty_canvas=!0);b=0;for(var g=this.graph._nodes.length;bc[0]+4||a.canvasYc[1]+4)){g=q;break}g!=this.over_link_center&&(this.over_link_center=g,this.dirty_canvas=!0);this.canvas&&(this.canvas.style.cursor="")}if(this.node_capturing_input&& -this.node_capturing_input!=f&&this.node_capturing_input.onMouseMove)this.node_capturing_input.onMouseMove(a,[a.canvasX-this.node_capturing_input.pos[0],a.canvasY-this.node_capturing_input.pos[1]],this);if(this.node_dragged&&!this.live_mode){for(b in this.selected_nodes)f=this.selected_nodes[b],f.pos[0]+=d[0]/this.ds.scale,f.pos[1]+=d[1]/this.ds.scale,f.is_selected||this.processNodeSelected(f,a);this.dirty_bgcanvas=this.dirty_canvas=!0}this.resizing_node&&!this.live_mode&&(d=[a.canvasX-this.resizing_node.pos[0], -a.canvasY-this.resizing_node.pos[1]],b=this.resizing_node.computeSize(),d[0]=Math.max(b[0],d[0]),d[1]=Math.max(b[1],d[1]),this.resizing_node.setSize(d),this.canvas.style.cursor="se-resize",this.dirty_bgcanvas=this.dirty_canvas=!0)}a.preventDefault();return!1}};h.prototype.processMouseUp=function(a){var b=void 0===a.isPrimary||a.isPrimary;if(!b)return!1;this.set_canvas_dirty_on_mouse_event&&(this.dirty_canvas=!0);if(this.graph){var d=this.getCanvasWindow().document;h.active_canvas=this;this.options.skip_events|| -(e.pointerListenerRemove(d,"move",this._mousemove_callback,!0),e.pointerListenerAdd(this.canvas,"move",this._mousemove_callback,!0),e.pointerListenerRemove(d,"up",this._mouseup_callback,!0));this.adjustMouseEvent(a);d=e.getTime();a.click_time=d-this.last_mouseclick;this.last_mouse_dragging=!1;this.last_click_position=null;this.block_click&&(this.block_click=!1);if(1==a.which){this.node_widget&&this.processNodeWidgets(this.node_widget[0],this.graph_mouse,a);this.node_widget=null;this.selected_group&& -(this.selected_group.move(this.selected_group.pos[0]-Math.round(this.selected_group.pos[0]),this.selected_group.pos[1]-Math.round(this.selected_group.pos[1]),a.ctrlKey),this.selected_group.pos[0]=Math.round(this.selected_group.pos[0]),this.selected_group.pos[1]=Math.round(this.selected_group.pos[1]),this.selected_group._nodes.length&&(this.dirty_canvas=!0),this.selected_group=null);this.selected_group_resizing=!1;var f=this.graph.getNodeOnPos(a.canvasX,a.canvasY,this.visible_nodes);if(this.dragging_rectangle){if(this.graph){d= -this.graph._nodes;var g=new Float32Array(4),q=Math.abs(this.dragging_rectangle[2]),c=Math.abs(this.dragging_rectangle[3]),l=0>this.dragging_rectangle[3]?this.dragging_rectangle[1]-c:this.dragging_rectangle[1];this.dragging_rectangle[0]=0>this.dragging_rectangle[2]?this.dragging_rectangle[0]-q:this.dragging_rectangle[0];this.dragging_rectangle[1]=l;this.dragging_rectangle[2]=q;this.dragging_rectangle[3]=c;if(!f||10a.click_time&&D(a.canvasX,a.canvasY,f.pos[0],f.pos[1]-e.NODE_TITLE_HEIGHT,e.NODE_TITLE_HEIGHT,e.NODE_TITLE_HEIGHT)&&f.collapse();this.dirty_bgcanvas=this.dirty_canvas=!0;this.node_dragged.pos[0]=Math.round(this.node_dragged.pos[0]);this.node_dragged.pos[1]=Math.round(this.node_dragged.pos[1]);(this.graph.config.align_to_grid||this.align_to_grid)&&this.node_dragged.alignToGrid();if(this.onNodeMoved)this.onNodeMoved(this.node_dragged);this.graph.afterChange(this.node_dragged); -this.node_dragged=null}else{f=this.graph.getNodeOnPos(a.canvasX,a.canvasY,this.visible_nodes);!f&&300>a.click_time&&this.deselectAllNodes();this.dirty_canvas=!0;this.dragging_canvas=!1;if(this.node_over&&this.node_over.onMouseUp)this.node_over.onMouseUp(a,[a.canvasX-this.node_over.pos[0],a.canvasY-this.node_over.pos[1]],this);if(this.node_capturing_input&&this.node_capturing_input.onMouseUp)this.node_capturing_input.onMouseUp(a,[a.canvasX-this.node_capturing_input.pos[0],a.canvasY-this.node_capturing_input.pos[1]])}}else 2== -a.which?(this.dirty_canvas=!0,this.dragging_canvas=!1):3==a.which&&(this.dirty_canvas=!0,this.dragging_canvas=!1);b&&(this.pointer_is_double=this.pointer_is_down=!1);this.graph.change();a.stopPropagation();a.preventDefault();return!1}};h.prototype.processMouseWheel=function(a){if(this.graph&&this.allow_dragcanvas){var b=null!=a.wheelDeltaY?a.wheelDeltaY:-60*a.detail;this.adjustMouseEvent(a);var d=a.clientX,f=a.clientY;if(!this.viewport||this.viewport&&d>=this.viewport[0]&&d=this.viewport[1]&&fb&&(d*=1/1.1),this.ds.changeScale(d,[a.clientX,a.clientY]),this.graph.change(),a.preventDefault(),!1}};h.prototype.isOverNodeBox=function(a,b,d){var f=e.NODE_TITLE_HEIGHT;return D(b,d,a.pos[0]+2,a.pos[1]+2-f,f-4,f-4)?!0:!1};h.prototype.isOverNodeInput=function(a,b,d,f){if(a.inputs)for(var g=0,e=a.inputs.length;gb.nodes[g].pos[0]&&(d[0]=b.nodes[g].pos[0],f[0]=g),d[1]>b.nodes[g].pos[1]&&(d[1]=b.nodes[g].pos[1],f[1]=g)):(d=[b.nodes[g].pos[0],b.nodes[g].pos[1]],f=[g,g]);f=[];for(g=0;g=this.viewport[0]&&b=this.viewport[1]&&dd-this.graph._last_trigger_time)&&this.drawBackCanvas();(this.dirty_canvas||a)&&this.drawFrontCanvas();this.fps=this.render_time?1/this.render_time:0;this.frame+=1}};h.prototype.drawFrontCanvas= -function(){this.dirty_canvas=!1;this.ctx||(this.ctx=this.bgcanvas.getContext("2d"));var a=this.ctx;if(a){var b=this.canvas;a.start2D&&!this.viewport&&(a.start2D(),a.restore(),a.setTransform(1,0,0,1,0,0));var d=this.viewport||this.dirty_area;d&&(a.save(),a.beginPath(),a.rect(d[0],d[1],d[2],d[3]),a.clip());this.clear_background&&(d?a.clearRect(d[0],d[1],d[2],d[3]):a.clearRect(0,0,b.width,b.height));this.bgcanvas==this.canvas?this.drawBackCanvas():a.drawImage(this.bgcanvas,0,0);if(this.onRender)this.onRender(b, -a);this.show_info&&this.renderInfo(a,d?d[0]:0,d?d[1]:0);if(this.graph){a.save();this.ds.toCanvasContext(a);b=this.computeVisibleNodes(null,this.visible_nodes);for(var f=0;f> ";b.fillText(f+d.getTitle(),.5*a.width,40);b.restore()}d=!1;this.onRenderBackground&&(d=this.onRenderBackground(a,b));this.viewport||(b.restore(),b.setTransform(1,0,0,1,0,0));this.visible_links.length=0;if(this.graph){b.save();this.ds.toCanvasContext(b);1.5>this.ds.scale&&!d&&this.clear_background_color&& -(b.fillStyle=this.clear_background_color,b.fillRect(this.visible_area[0],this.visible_area[1],this.visible_area[2],this.visible_area[3]));if(this.background_image&&.5this.ds.scale;if(this.live_mode){if(!a.flags.collapsed&&(b.shadowColor="transparent",a.onDrawForeground))a.onDrawForeground(b,this,this.canvas)}else{var c=this.editor_alpha;b.globalAlpha=c;this.render_shadows&&!g?(b.shadowColor=e.DEFAULT_SHADOW_COLOR,b.shadowOffsetX=2*this.ds.scale, -b.shadowOffsetY=2*this.ds.scale,b.shadowBlur=3*this.ds.scale):b.shadowColor="transparent";if(!a.flags.collapsed||!a.onDrawCollapsed||1!=a.onDrawCollapsed(b,this)){var l=a._shape||e.BOX_SHAPE;B.set(a.size);var m=a.horizontal;if(a.flags.collapsed){b.font=this.inner_text_font;var h=a.getTitle?a.getTitle():a.title;null!=h&&(a._collapsed_width=Math.min(a.size[0],b.measureText(h).width+2*e.NODE_TITLE_HEIGHT),B[0]=a._collapsed_width,B[1]=0)}a.clip_area&&(b.save(),b.beginPath(),l==e.BOX_SHAPE?b.rect(0,0, -B[0],B[1]):l==e.ROUND_SHAPE?b.roundRect(0,0,B[0],B[1],[10]):l==e.CIRCLE_SHAPE&&b.arc(.5*B[0],.5*B[1],.5*B[0],0,2*Math.PI),b.clip());a.has_errors&&(f="red");this.drawNodeShape(a,b,B,d,f,a.is_selected,a.mouseOver);b.shadowColor="transparent";if(a.onDrawForeground)a.onDrawForeground(b,this,this.canvas);b.textAlign=m?"center":"left";b.font=this.inner_text_font;f=!g;var t=this.connecting_output;l=this.connecting_input;b.lineWidth=1;h=0;var v=new Float32Array(2);if(!a.flags.collapsed){if(a.inputs)for(d= -0;dthis.ds.scale,z=a._shape||a.constructor.shape||e.ROUND_SHAPE,t=a.constructor.title_mode,v=!0;t==e.TRANSPARENT_TITLE||t==e.NO_TITLE?v=!1:t==e.AUTOHIDE_TITLE&&l&&(v=!0);M[0]=0;M[1]=v?-g:0;M[2]=d[0]+1;M[3]=v?d[1]+g:d[1];l=b.globalAlpha;b.beginPath();z==e.BOX_SHAPE||q?b.fillRect(M[0],M[1],M[2],M[3]):z==e.ROUND_SHAPE||z==e.CARD_SHAPE?b.roundRect(M[0],M[1],M[2],M[3],z==e.CARD_SHAPE?[this.round_radius, -this.round_radius,0,0]:[this.round_radius]):z==e.CIRCLE_SHAPE&&b.arc(.5*d[0],.5*d[1],.5*d[0],0,2*Math.PI);b.fill();!a.flags.collapsed&&v&&(b.shadowColor="transparent",b.fillStyle="rgba(0,0,0,0.2)",b.fillRect(0,-1,M[2],2));b.shadowColor="transparent";if(a.onDrawBackground)a.onDrawBackground(b,this,this.canvas,this.graph_mouse);if(v||t==e.TRANSPARENT_TITLE){if(a.onDrawTitleBar)a.onDrawTitleBar(b,g,d,this.ds.scale,f);else if(t!=e.TRANSPARENT_TITLE&&(a.constructor.title_color||this.render_title_colored)){v= -a.constructor.title_color||f;a.flags.collapsed&&(b.shadowColor=e.DEFAULT_SHADOW_COLOR);if(this.use_gradients){var C=h.gradients[v];C||(C=h.gradients[v]=b.createLinearGradient(0,0,400,0),C.addColorStop(0,v),C.addColorStop(1,"#000"));b.fillStyle=C}else b.fillStyle=v;b.beginPath();z==e.BOX_SHAPE||q?b.rect(0,-g,d[0]+1,g):(z==e.ROUND_SHAPE||z==e.CARD_SHAPE)&&b.roundRect(0,-g,d[0]+1,g,a.flags.collapsed?[this.round_radius]:[this.round_radius,this.round_radius,0,0]);b.fill();b.shadowColor="transparent"}v= -!1;e.node_box_coloured_by_mode&&e.NODE_MODES_COLORS[a.mode]&&(v=e.NODE_MODES_COLORS[a.mode]);e.node_box_coloured_when_on&&(v=a.action_triggered?"#FFF":a.execute_triggered?"#AAA":v);if(a.onDrawTitleBox)a.onDrawTitleBox(b,g,d,this.ds.scale);else z==e.ROUND_SHAPE||z==e.CIRCLE_SHAPE||z==e.CARD_SHAPE?(q&&(b.fillStyle="black",b.beginPath(),b.arc(.5*g,-.5*g,6,0,2*Math.PI),b.fill()),b.fillStyle=a.boxcolor||v||e.NODE_DEFAULT_BOXCOLOR,q?b.fillRect(.5*g-5,-.5*g-5,10,10):(b.beginPath(),b.arc(.5*g,-.5*g,5,0,2* -Math.PI),b.fill())):(q&&(b.fillStyle="black",b.fillRect(.5*(g-10)-1,-.5*(g+10)-1,12,12)),b.fillStyle=a.boxcolor||v||e.NODE_DEFAULT_BOXCOLOR,b.fillRect(.5*(g-10),-.5*(g+10),10,10));b.globalAlpha=l;if(a.onDrawTitleText)a.onDrawTitleText(b,g,d,this.ds.scale,this.title_text_font,c);!q&&(b.font=this.title_text_font,l=String(a.getTitle()))&&(b.fillStyle=c?e.NODE_SELECTED_TITLE_COLOR:a.constructor.title_text_color||this.node_title_color,a.flags.collapsed?(b.textAlign="left",b.measureText(l),b.fillText(l.substr(0, -20),g,e.NODE_TITLE_TEXT_Y-g),b.textAlign="left"):(b.textAlign="left",b.fillText(l,g,e.NODE_TITLE_TEXT_Y-g)));a.flags.collapsed||!a.subgraph||a.skip_subgraph_button||(l=e.NODE_TITLE_HEIGHT,v=a.size[0]-l,C=e.isInsideRectangle(this.graph_mouse[0]-a.pos[0],this.graph_mouse[1]-a.pos[1],v+2,-l+2,l-4,l-4),b.fillStyle=C?"#888":"#555",z==e.BOX_SHAPE||q?b.fillRect(v+2,-l+2,l-4,l-4):(b.beginPath(),b.roundRect(v+2,-l+2,l-4,l-4,[4]),b.fill()),b.fillStyle="#333",b.beginPath(),b.moveTo(v+.2*l,.6*-l),b.lineTo(v+ -.8*l,.6*-l),b.lineTo(v+.5*l,.3*-l),b.fill());if(a.onDrawTitle)a.onDrawTitle(b)}if(c){if(a.onBounding)a.onBounding(M);t==e.TRANSPARENT_TITLE&&(M[1]-=g,M[3]+=g);b.lineWidth=1;b.globalAlpha=.8;b.beginPath();z==e.BOX_SHAPE?b.rect(-6+M[0],-6+M[1],12+M[2],12+M[3]):z==e.ROUND_SHAPE||z==e.CARD_SHAPE&&a.flags.collapsed?b.roundRect(-6+M[0],-6+M[1],12+M[2],12+M[3],[2*this.round_radius]):z==e.CARD_SHAPE?b.roundRect(-6+M[0],-6+M[1],12+M[2],12+M[3],[2*this.round_radius,2,2*this.round_radius,2]):z==e.CIRCLE_SHAPE&& -b.arc(.5*d[0],.5*d[1],.5*d[0]+6,0,2*Math.PI);b.strokeStyle=e.NODE_BOX_OUTLINE_COLOR;b.stroke();b.strokeStyle=f;b.globalAlpha=1}0m[2]&&(m[0]+=m[2],m[2]=Math.abs(m[2]));0>m[3]&& -(m[1]+=m[3],m[3]=Math.abs(m[3]));if(J(m,l)){var x=k.outputs[t];t=c.inputs[z];if(x&&t&&(k=x.dir||(k.horizontal?e.DOWN:e.RIGHT),t=t.dir||(c.horizontal?e.UP:e.LEFT),this.renderLink(a,v,C,h,!1,0,null,k,t),h&&h._last_time&&1E3>b-h._last_time)){x=2-.002*(b-h._last_time);var G=a.globalAlpha;a.globalAlpha=G*x;this.renderLink(a,v,C,h,!0,x,"white",k,t);a.globalAlpha=G}}}}}}a.globalAlpha=1};h.prototype.renderLink=function(a,b,d,f,g,c,l,m,w,t){f&&this.visible_links.push(f);!l&&f&&(l=f.color||h.link_type_colors[f.type]); -l||(l=this.default_link_color);null!=f&&this.highlighted_links[f.id]&&(l="#FFF");m=m||e.RIGHT;w=w||e.LEFT;var q=E(b,d);this.render_connections_border&&.6b[1]?0:Math.PI,a.save(),a.translate(C[0],C[1]),a.rotate(q),a.beginPath(),a.moveTo(-5,-3),a.lineTo(0,7),a.lineTo(5, --3),a.fill(),a.restore(),a.save(),a.translate(f[0],f[1]),a.rotate(t),a.beginPath(),a.moveTo(-5,-3),a.lineTo(0,7),a.lineTo(5,-3),a.fill(),a.restore()),a.beginPath(),a.arc(g[0],g[1],5,0,2*Math.PI),a.fill());if(c)for(a.fillStyle=l,C=0;5>C;++C)c=(.001*e.getTime()+.2*C)%1,g=this.computeConnectionPoint(b,d,c,m,w),a.beginPath(),a.arc(g[0],g[1],5,0,2*Math.PI),a.fill()};h.prototype.computeConnectionPoint=function(a,b,d,f,g){f=f||e.RIGHT;g=g||e.LEFT;var c=E(a,b),l=[a[0],a[1]],m=[b[0],b[1]];switch(f){case e.LEFT:l[0]+= --.25*c;break;case e.RIGHT:l[0]+=.25*c;break;case e.UP:l[1]+=-.25*c;break;case e.DOWN:l[1]+=.25*c}switch(g){case e.LEFT:m[0]+=-.25*c;break;case e.RIGHT:m[0]+=.25*c;break;case e.UP:m[1]+=-.25*c;break;case e.DOWN:m[1]+=.25*c}f=(1-d)*(1-d)*(1-d);g=3*(1-d)*(1-d)*d;c=3*(1-d)*d*d;d*=d*d;return[f*a[0]+g*l[0]+c*m[0]+d*b[0],f*a[1]+g*l[1]+c*m[1]+d*b[1]]};h.prototype.drawExecutionOrder=function(a){a.shadowColor="transparent";a.globalAlpha=.25;a.textAlign="center";a.strokeStyle="white";a.globalAlpha=.75;for(var b= -this.visible_nodes,d=0;dn&&(n=0);1k&&(k=0),1c||c>A-12||lx.last_y+G||void 0===x.last_y)){f=x.value;switch(x.type){case "button":d.type===e.pointerevents_method+"down"&&(x.callback&&setTimeout(function(){x.callback(x,t,a,b,d)},20),this.dirty_canvas=x.clicked=!0);break;case "slider":f=x.value;v=F((c-15)/(A-30),0,1);if(x.options.read_only)break;x.value=x.options.min+(x.options.max-x.options.min)*v;f!=x.value&&setTimeout(function(){g(x,x.value)},20);this.dirty_canvas= -!0;break;case "number":case "combo":f=x.value;if(d.type==e.pointerevents_method+"move"&&"number"==x.type)h&&(x.value+=.1*h*(x.options.step||1)),null!=x.options.min&&x.valuex.options.max&&(x.value=x.options.max);else if(d.type==e.pointerevents_method+"down"){var w=x.options.values;w&&w.constructor===Function&&(w=x.options.values(x,a));var k=null;"number"!=x.type&&(k=w.constructor===Array?w:Object.keys(w));c=40>c?-1:c>A-40?1:0;if("number"== -x.type)x.value+=.1*c*(x.options.step||1),null!=x.options.min&&x.valuex.options.max&&(x.value=x.options.max);else if(c)v=-1,this.last_mouseclick=0,v=w.constructor===Object?k.indexOf(String(x.value))+c:k.indexOf(x.value)+c,v>=k.length&&(v=k.length-1),0>v&&(v=0),x.value=w.constructor===Array?w[v]:v;else{var n=w!=k?Object.values(w):w;new e.ContextMenu(n,{scale:Math.max(1,this.ds.scale),event:d,className:"dark",callback:function(a,b, -d){w!=k&&(a=n.indexOf(a));this.value=a;g(this,a);t.dirty_canvas=!0;return!1}.bind(x)},v)}}else d.type==e.pointerevents_method+"up"&&"number"==x.type&&(c=40>c?-1:c>A-40?1:0,200>d.click_time&&0==c&&this.prompt("Value",x.value,function(a){if(/^[0-9+\-*/()\s]+|\d+\.\d+$/.test(a))try{a=eval(a)}catch(V){}this.value=Number(a);g(this,this.value)}.bind(x),d));f!=x.value&&setTimeout(function(){g(this,this.value)}.bind(x),20);this.dirty_canvas=!0;break;case "toggle":d.type==e.pointerevents_method+"down"&&(x.value= -!x.value,setTimeout(function(){g(x,x.value)},20));break;case "string":case "text":d.type==e.pointerevents_method+"down"&&this.prompt("Value",x.value,function(a){g(this,a)}.bind(x),d,x.options?x.options.multiline:!1);break;default:x.mouse&&(this.dirty_canvas=x.mouse(d,[c,l],a))}if(f!=x.value){if(a.onWidgetChanged)a.onWidgetChanged(x.name,x.value,f,x);a.graph._version++}return x}}}return null};h.prototype.drawGroups=function(a,b){if(this.graph){a=this.graph._groups;b.save();b.globalAlpha=.5*this.editor_alpha; -for(var d=0;dd&&.01>b.editor_alpha&&(clearInterval(f),1>d&&(b.live_mode=!0));1d.pos[0]+d.size[0])d=c;if(null===f||l+t>f.pos[1]+f.size[1])f=c;if(null===g||m"+(w.label?w.label:m)+""+a+"",value:m})}if(l.length)return new e.ContextMenu(l,{event:d,callback:function(a,b,d,f){g&&(b=this.getBoundingClientRect(),c.showEditPropertyValue(g,a.value,{position:[b.left,b.top]}))},parentMenu:f,allow_html:!0, -node:g},b),!1}};h.decodeHTML=function(a){var b=document.createElement("div");b.innerText=a;return b.innerHTML};h.onMenuResizeNode=function(a,b,d,f,g){if(g){a=function(a){a.size=a.computeSize();if(a.onResize)a.onResize(a.size)};b=h.active_canvas;if(!b.selected_nodes||1>=Object.keys(b.selected_nodes).length)a(g);else for(var e in b.selected_nodes)a(b.selected_nodes[e]);g.setDirtyCanvas(!0,!0)}};h.prototype.showLinkMenu=function(a,b){var d=this,f=d.graph.getNodeById(a.origin_id),g=d.graph.getNodeById(a.target_id), -c=!1;f&&f.outputs&&f.outputs[a.origin_slot]&&(c=f.outputs[a.origin_slot].type);var l=!1;g&&g.outputs&&g.outputs[a.target_slot]&&(l=g.inputs[a.target_slot].type);var m=new e.ContextMenu(["Add Node",null,"Delete",null],{event:b,title:null!=a.data?a.data.constructor.name:null,callback:function(b,e,q){switch(b){case "Add Node":h.onMenuAdd(null,null,q,m,function(b){b.inputs&&b.inputs.length&&b.outputs&&b.outputs.length&&f.connectByType(a.origin_slot,b,c)&&(b.connectByType(a.target_slot,g,l),b.pos[0]-= -.5*b.size[0])});break;case "Delete":d.graph.removeLink(a.id)}}});return!1};h.prototype.createDefaultNodeForSlot=function(a){a=a||{};a=Object.assign({nodeFrom:null,slotFrom:null,nodeTo:null,slotTo:null,position:[],nodeType:null,posAdd:[0,0],posSizeFix:[0,0]},a);var b=a.nodeFrom&&null!==a.slotFrom,d=!b&&a.nodeTo&&null!==a.slotTo;if(!b&&!d)return console.warn("No data passed to createDefaultNodeForSlot "+a.nodeFrom+" "+a.slotFrom+" "+a.nodeTo+" "+a.slotTo),!1;if(!a.nodeType)return console.warn("No type to createDefaultNodeForSlot"), -!1;var f=b?a.nodeFrom:a.nodeTo,g=b?a.slotFrom:a.slotTo;switch(typeof g){case "string":d=b?f.findOutputSlot(g,!1):f.findInputSlot(g,!1);g=b?f.outputs[g]:f.inputs[g];break;case "object":d=b?f.findOutputSlot(g.name):f.findInputSlot(g.name);break;case "number":d=g;g=b?f.outputs[g]:f.inputs[g];break;default:return console.warn("Cant get slot information "+g),!1}!1!==g&&!1!==d||console.warn("createDefaultNodeForSlot bad slotX "+g+" "+d);f=g.type==e.EVENT?"_event_":g.type;if((g=b?e.slot_types_default_out: -e.slot_types_default_in)&&g[f]){nodeNewType=!1;if("object"==typeof g[f]||"array"==typeof g[f])for(var c in g[f]){if(a.nodeType==g[f][c]||"AUTO"==a.nodeType){nodeNewType=g[f][c];break}}else if(a.nodeType==g[f]||"AUTO"==a.nodeType)nodeNewType=g[f];if(nodeNewType){c=!1;"object"==typeof nodeNewType&&nodeNewType.node&&(c=nodeNewType,nodeNewType=nodeNewType.node);if(g=e.createNode(nodeNewType)){if(c){if(c.properties)for(var l in c.properties)g.addProperty(l,c.properties[l]);if(c.inputs)for(l in g.inputs= -[],c.inputs)g.addOutput(c.inputs[l][0],c.inputs[l][1]);if(c.outputs)for(l in g.outputs=[],c.outputs)g.addOutput(c.outputs[l][0],c.outputs[l][1]);c.title&&(g.title=c.title);c.json&&g.configure(c.json)}this.graph.add(g);g.pos=[a.position[0]+a.posAdd[0]+(a.posSizeFix[0]?a.posSizeFix[0]*g.size[0]:0),a.position[1]+a.posAdd[1]+(a.posSizeFix[1]?a.posSizeFix[1]*g.size[1]:0)];b?a.nodeFrom.connectByType(d,g,f):a.nodeTo.connectByTypeOutput(d,g,f);return!0}console.log("failed creating "+nodeNewType)}}return!1}; -h.prototype.showConnectionMenu=function(a){a=a||{};var b=Object.assign({nodeFrom:null,slotFrom:null,nodeTo:null,slotTo:null,e:null},a),d=this,f=b.nodeFrom&&b.slotFrom;a=!f&&b.nodeTo&&b.slotTo;if(!f&&!a)return console.warn("No data passed to showConnectionMenu"),!1;a=f?b.nodeFrom:b.nodeTo;var g=f?b.slotFrom:b.slotTo,c=!1;switch(typeof g){case "string":c=f?a.findOutputSlot(g,!1):a.findInputSlot(g,!1);g=f?a.outputs[g]:a.inputs[g];break;case "object":c=f?a.findOutputSlot(g.name):a.findInputSlot(g.name); -break;case "number":c=g;g=f?a.outputs[g]:a.inputs[g];break;default:return console.warn("Cant get slot information "+g),!1}a=["Add Node",null];d.allow_searchbox&&(a.push("Search"),a.push(null));var l=g.type==e.EVENT?"_event_":g.type,m=f?e.slot_types_default_out:e.slot_types_default_in;if(m&&m[l])if("object"==typeof m[l]||"array"==typeof m[l])for(var w in m[l])a.push(m[l][w]);else a.push(m[l]);var t=new e.ContextMenu(a,{event:b.e,title:(g&&""!=g.name?g.name+(l?" | ":""):"")+(g&&l?l:""),callback:function(a, -e,x){switch(a){case "Add Node":h.onMenuAdd(null,null,x,t,function(a){f?b.nodeFrom.connectByType(c,a,l):b.nodeTo.connectByTypeOutput(c,a,l)});break;case "Search":f?d.showSearchBox(x,{node_from:b.nodeFrom,slot_from:g,type_filter_in:l}):d.showSearchBox(x,{node_to:b.nodeTo,slot_from:g,type_filter_out:l});break;default:d.createDefaultNodeForSlot(Object.assign(b,{position:[b.e.canvasX,b.e.canvasY],nodeType:a}))}}});return!1};h.onShowPropertyEditor=function(a,b,d,f,g){function c(){if(w){var b=w.value;"Number"== -a.type?b=Number(b):"Boolean"==a.type&&(b=!!b);g[l]=b;m.parentNode&&m.parentNode.removeChild(m);g.setDirtyCanvas(!0,!0)}}var l=a.property||"title";b=g[l];var m=document.createElement("div");m.is_modified=!1;m.className="graphdialog";m.innerHTML="";m.close=function(){m.parentNode&&m.parentNode.removeChild(m)};m.querySelector(".name").innerText=l;var w=m.querySelector(".value");w&&(w.value=b,w.addEventListener("blur", -function(a){this.focus()}),w.addEventListener("keydown",function(a){m.is_modified=!0;if(27==a.keyCode)m.close();else if(13==a.keyCode)c();else if(13!=a.keyCode&&"textarea"!=a.target.localName)return;a.preventDefault();a.stopPropagation()}));b=h.active_canvas.canvas;d=b.getBoundingClientRect();var t=f=-20;d&&(f-=d.left,t-=d.top);event?(m.style.left=event.clientX+f+"px",m.style.top=event.clientY+t+"px"):(m.style.left=.5*b.width+f+"px",m.style.top=.5*b.height+t+"px");m.querySelector("button").addEventListener("click", -c);b.parentNode.appendChild(m);w&&w.focus();var v=null;m.addEventListener("mouseleave",function(a){e.dialog_close_on_mouse_leave&&!m.is_modified&&e.dialog_close_on_mouse_leave&&(v=setTimeout(m.close,e.dialog_close_on_mouse_leave_delay))});m.addEventListener("mouseenter",function(a){e.dialog_close_on_mouse_leave&&v&&clearTimeout(v)})};h.prototype.prompt=function(a,b,d,f,g){var c=this;a=a||"";var l=document.createElement("div");l.is_modified=!1;l.className="graphdialog rounded";l.innerHTML=g?" ": -" ";l.close=function(){c.prompt_box=null;l.parentNode&&l.parentNode.removeChild(l)};g=h.active_canvas.canvas;g.parentNode.appendChild(l);1h.search_limit))break}}q=null;if(Array.prototype.filter)q=Object.keys(e.registered_node_types).filter(g);else for(x in q=[],e.registered_node_types)g(x)&&q.push(x);for(x=0;xh.search_limit);x++);if(b.show_general_after_typefiltered&&(G.value|| -Q.value)){filtered_extra=[];for(x in e.registered_node_types)g(x,{inTypeOverride:G&&G.value?"*":!1,outTypeOverride:Q&&Q.value?"*":!1})&&filtered_extra.push(x);for(x=0;xh.search_limit);x++);}if((G.value||Q.value)&&0==A.childNodes.length&&b.show_general_if_none_on_typefilter){filtered_extra=[];for(x in e.registered_node_types)g(x,{skipFilter:!0})&&filtered_extra.push(x);for(x=0;xh.search_limit);x++);}}}b=Object.assign({slot_from:null,node_from:null,node_to:null,do_type_filter:e.search_filter_enabled,type_filter_in:!1,type_filter_out:!1,show_general_if_none_on_typefilter:!0,show_general_after_typefiltered:!0,hide_on_mouse_leave:e.search_hide_on_mouse_leave,show_all_if_empty:!0,show_all_on_open:e.search_show_all_on_open},b||{});var c=this,l=h.active_canvas,m=l.canvas,w=m.ownerDocument||document,t=document.createElement("div");t.className= -"litegraph litesearchbox graphdialog rounded";t.innerHTML="Search ";b.do_type_filter&&(t.innerHTML+="",t.innerHTML+="");t.innerHTML+="
";w.fullscreenElement?w.fullscreenElement.appendChild(t):(w.body.appendChild(t),w.body.style.overflow="hidden");if(b.do_type_filter)var v= -t.querySelector(".slot_in_type_filter"),C=t.querySelector(".slot_out_type_filter");t.close=function(){c.search_box=null;this.blur();m.focus();w.body.style.overflow="";setTimeout(function(){c.canvas.focus()},20);t.parentNode&&t.parentNode.removeChild(t)};1v.height-200&&(A.style.maxHeight=v.height-a.layerY-20+"px");K.focus();b.show_all_on_open&&g();return t};h.prototype.showEditPropertyValue=function(a,b,d){function f(){g(C.value)}function g(f){e&&e.values&&e.values.constructor===Object&&void 0!=e.values[f]&&(f=e.values[f]);"number"==typeof a.properties[b]&& -(f=Number(f));if("array"==c||"object"==c)f=JSON.parse(f);a.properties[b]=f;a.graph&&a.graph._version++;if(a.onPropertyChanged)a.onPropertyChanged(b,f);if(d.onclose)d.onclose();v.close();a.setDirtyCanvas(!0,!0)}if(a&&void 0!==a.properties[b]){d=d||{};var e=a.getPropertyInfo(b),c=e.type,l="";if("string"==c||"number"==c||"array"==c||"object"==c)l="";else if("enum"!=c&&"combo"!=c||!e.values)if("boolean"==c||"toggle"==c)l="";else{console.warn("unknown type: "+c);return}else{l=""}var v=this.createDialog(""+(e.label?e.label:b)+""+l+"",d),C=!1;if("enum"!=c&&"combo"!=c||!e.values)if("boolean"==c||"toggle"==c)(C=v.querySelector("input"))&& -C.addEventListener("click",function(a){v.modified();g(!!C.checked)});else{if(C=v.querySelector("input"))C.addEventListener("blur",function(a){this.focus()}),t=void 0!==a.properties[b]?a.properties[b]:"","string"!==c&&(t=JSON.stringify(t)),C.value=t,C.addEventListener("keydown",function(a){if(27==a.keyCode)v.close();else if(13==a.keyCode)f();else if(13!=a.keyCode){v.modified();return}a.preventDefault();a.stopPropagation()})}else C=v.querySelector("select"),C.addEventListener("change",function(a){v.modified(); -g(a.target.value)});C&&C.focus();v.querySelector("button").addEventListener("click",f);return v}};h.prototype.createDialog=function(a,b){b=Object.assign({checkForInput:!1,closeOnLeave:!0,closeOnLeave_checkModified:!0},b||{});var d=document.createElement("div");d.className="graphdialog";d.innerHTML=a;d.is_modified=!1;a=this.canvas.getBoundingClientRect();var f=-20,g=-20;a&&(f-=a.left,g-=a.top);b.position?(f+=b.position[0],g+=b.position[1]):b.event?(f+=b.event.clientX,g+=b.event.clientY):(f+=.5*this.canvas.width, -g+=.5*this.canvas.height);d.style.left=f+"px";d.style.top=g+"px";this.canvas.parentNode.appendChild(d);b.checkForInput&&(a=[],(a=d.querySelectorAll("input"))&&a.forEach(function(a){a.addEventListener("keydown",function(a){d.modified();if(27==a.keyCode)d.close();else if(13!=a.keyCode)return;a.preventDefault();a.stopPropagation()});a.focus()}));d.modified=function(){d.is_modified=!0};d.close=function(){d.parentNode&&d.parentNode.removeChild(d)};var c=null,l=!1;d.addEventListener("mouseleave",function(a){l|| -(b.closeOnLeave||e.dialog_close_on_mouse_leave)&&!d.is_modified&&e.dialog_close_on_mouse_leave&&(c=setTimeout(d.close,e.dialog_close_on_mouse_leave_delay))});d.addEventListener("mouseenter",function(a){(b.closeOnLeave||e.dialog_close_on_mouse_leave)&&c&&clearTimeout(c)});(a=d.querySelectorAll("select"))&&a.forEach(function(a){a.addEventListener("click",function(a){l++});a.addEventListener("blur",function(a){l=0});a.addEventListener("change",function(a){l=-1})});return d};h.prototype.createPanel=function(a, -b){b=b||{};var d=b.window||window,f=document.createElement("div");f.className="litegraph dialog";f.innerHTML="
";f.header=f.querySelector(".dialog-header");b.width&&(f.style.width=b.width+(b.width.constructor===Number?"px":""));b.height&&(f.style.height=b.height+(b.height.constructor===Number?"px":""));b.closable&& -(b=document.createElement("span"),b.innerHTML="✕",b.classList.add("close"),b.addEventListener("click",function(){f.close()}),f.header.appendChild(b));f.title_element=f.querySelector(".dialog-title");f.title_element.innerText=a;f.content=f.querySelector(".dialog-content");f.alt_content=f.querySelector(".dialog-alt-content");f.footer=f.querySelector(".dialog-footer");f.close=function(){if(f.onClose&&"function"==typeof f.onClose)f.onClose();f.parentNode&&f.parentNode.removeChild(f);this.parentNode&& -this.parentNode.removeChild(this)};f.toggleAltContent=function(a){if("undefined"!=typeof a){var b=a?"block":"none";a=a?"none":"block"}else b="block"!=f.alt_content.style.display?"block":"none",a="block"!=f.alt_content.style.display?"none":"block";f.alt_content.style.display=b;f.content.style.display=a};f.toggleFooterVisibility=function(a){f.footer.style.display="undefined"!=typeof a?a?"block":"none":"block"!=f.footer.style.display?"block":"none"};f.clear=function(){this.content.innerHTML=""};f.addHTML= -function(a,b,d){var g=document.createElement("div");b&&(g.className=b);g.innerHTML=a;d?f.footer.appendChild(g):f.content.appendChild(g);return g};f.addButton=function(a,b,d){var g=document.createElement("button");g.innerText=a;g.options=d;g.classList.add("btn");g.addEventListener("click",b);f.footer.appendChild(g);return g};f.addSeparator=function(){var a=document.createElement("div");a.className="separator";f.content.appendChild(a)};f.addWidget=function(a,b,c,l,m){function g(a,b){l.callback&&l.callback(a, -b,l);m&&m(a,b,l)}l=l||{};var q=String(c);a=a.toLowerCase();"number"==a&&(q=c.toFixed(3));var C=document.createElement("div");C.className="property";C.innerHTML="";C.querySelector(".property_name").innerText=l.label||b;var x=C.querySelector(".property_value");x.innerText=q;C.dataset.property=b;C.dataset.type=l.type||a;C.options=l;C.value=c;if("code"==a)C.addEventListener("click",function(a){f.inner_showCodePad(this.dataset.property)}); -else if("boolean"==a)C.classList.add("boolean"),c&&C.classList.add("bool-on"),C.addEventListener("click",function(){var a=this.dataset.property;this.value=!this.value;this.classList.toggle("bool-on");this.querySelector(".property_value").innerText=this.value?"true":"false";g(a,this.value)});else if("string"==a||"number"==a)x.setAttribute("contenteditable",!0),x.addEventListener("keydown",function(b){"Enter"!=b.code||"string"==a&&b.shiftKey||(b.preventDefault(),this.blur())}),x.addEventListener("blur", -function(){var a=this.innerText,b=this.parentNode.dataset.property;"number"==this.parentNode.dataset.type&&(a=Number(a));g(b,a)});else if("enum"==a||"combo"==a)q=h.getPropertyPrintableValue(c,l.values),x.innerText=q,x.addEventListener("click",function(a){var b=this.parentNode.dataset.property,f=this;new e.ContextMenu(l.values||[],{event:a,className:"dark",callback:function(a,d,c){f.innerText=a;g(b,a);return!1}},d)});f.content.appendChild(C);return C};if(f.onOpen&&"function"==typeof f.onOpen)f.onOpen(); -return f};h.getPropertyPrintableValue=function(a,b){if(!b||b.constructor===Array)return String(a);if(b.constructor===Object){var d="",f;for(f in b)if(b[f]==a){d=f;break}return String(a)+" ("+d+")"}};h.prototype.closePanels=function(){var a=document.querySelector("#node-panel");a&&a.close();(a=document.querySelector("#option-panel"))&&a.close()};h.prototype.showShowGraphOptionsPanel=function(a,b,d,f){if(this.constructor&&"HTMLDivElement"==this.constructor.name){if(!(b&&b.event&&b.event.target&&b.event.target.lgraphcanvas)){console.warn("Canvas not found"); -return}var g=b.event.target.lgraphcanvas}else g=this;g.closePanels();a=g.getCanvasWindow();panel=g.createPanel("Options",{closable:!0,window:a,onOpen:function(){g.OPTIONPANEL_IS_OPEN=!0},onClose:function(){g.OPTIONPANEL_IS_OPEN=!1;g.options_panel=null}});g.options_panel=panel;panel.id="option-panel";panel.classList.add("settings");(function(){panel.content.innerHTML="";var a=function(a,b,d){d&&d.key&&(a=d.key);d.values&&(b=Object.values(d.values).indexOf(b));g[a]=b},b=e.availableCanvasOptions;b.sort(); -for(var d in b){var f=b[d];panel.addWidget("boolean",f,g[f],{key:f,on:"True",off:"False"},a)}panel.addWidget("combo","Render mode",e.LINK_RENDER_MODES[g.links_render_mode],{key:"links_render_mode",values:e.LINK_RENDER_MODES},a);panel.addSeparator();panel.footer.innerHTML=""})();g.canvas.parentNode.appendChild(panel)};h.prototype.showShowNodePanel=function(a){function b(){g.content.innerHTML="";g.addHTML(""+a.type+""+(a.constructor.desc||"")+""); -g.addHTML("

Properties

");var b=function(b,d){f.graph.beforeChange(a);switch(b){case "Title":a.title=d;break;case "Mode":b=Object.values(e.NODE_MODES).indexOf(d);0<=b&&e.NODE_MODES[b]?a.changeMode(b):console.warn("unexpected mode: "+d);break;case "Color":h.node_colors[d]?(a.color=h.node_colors[d].color,a.bgcolor=h.node_colors[d].bgcolor):console.warn("unexpected color: "+d);break;default:a.setProperty(b,d)}f.graph.afterChange();f.dirty_canvas=!0};g.addWidget("string","Title",a.title,{},b); -g.addWidget("combo","Mode",e.NODE_MODES[a.mode],{values:e.NODE_MODES},b);var d="";void 0!==a.color&&(d=Object.keys(h.node_colors).filter(function(b){return h.node_colors[b].color==a.color}));g.addWidget("combo","Color",d,{values:Object.keys(h.node_colors)},b);for(var c in a.properties){d=a.properties[c];var l=a.getPropertyInfo(c);a.onAddPropertyToPanel&&a.onAddPropertyToPanel(c,g)||g.addWidget(l.widget||l.type,c,d,l,b)}g.addSeparator();if(a.onShowCustomPanelInfo)a.onShowCustomPanelInfo(g);g.footer.innerHTML= -"";g.addButton("Delete",function(){a.block_delete||(a.graph.remove(a),g.close())}).classList.add("delete")}this.SELECTED_NODE=a;this.closePanels();var d=this.getCanvasWindow(),f=this,g=this.createPanel(a.title||"",{closable:!0,window:d,onOpen:function(){f.NODEPANEL_IS_OPEN=!0},onClose:function(){f.NODEPANEL_IS_OPEN=!1;f.node_panel=null}});f.node_panel=g;g.id="node-panel";g.node=a;g.classList.add("settings");g.inner_showCodePad=function(d){g.classList.remove("settings");g.classList.add("centered"); -g.alt_content.innerHTML="";var f=g.alt_content.querySelector("textarea"),c=function(){g.toggleAltContent(!1);g.toggleFooterVisibility(!0);f.parentNode.removeChild(f);g.classList.add("settings");g.classList.remove("centered");b()};f.value=a.properties[d];f.addEventListener("keydown",function(b){"Enter"==b.code&&b.ctrlKey&&(a.setProperty(d,f.value),c())});g.toggleAltContent(!0);g.toggleFooterVisibility(!1);f.style.height="calc(100% - 40px)";var e=g.addButton("Assign", -function(){a.setProperty(d,f.value);c()});g.alt_content.appendChild(e);e=g.addButton("Close",c);e.style.float="right";g.alt_content.appendChild(e)};b();this.canvas.parentNode.appendChild(g)};h.prototype.showSubgraphPropertiesDialog=function(a){function b(){f.clear();if(a.inputs)for(var d=0;d","subgraph_property"); -e.dataset.name=c.name;e.dataset.slot=d;e.querySelector(".name").innerText=c.name;e.querySelector(".type").innerText=c.type;e.querySelector("button").addEventListener("click",function(d){a.removeInput(Number(this.parentNode.dataset.slot));b()})}}}console.log("showing subgraph properties dialog");var d=this.canvas.parentNode.querySelector(".subgraph_dialog");d&&d.close();var f=this.createPanel("Subgraph Inputs",{closable:!0,width:500});f.node=a;f.classList.add("subgraph_dialog");f.addHTML(" + NameType", -"subgraph_property extra",!0).querySelector("button").addEventListener("click",function(d){d=this.parentNode;var f=d.querySelector(".name").value,g=d.querySelector(".type").value;f&&-1==a.findInputSlot(f)&&(a.addInput(f,g),d.querySelector(".name").value="",d.querySelector(".type").value="",b())});b();this.canvas.parentNode.appendChild(f);return f};h.prototype.showSubgraphPropertiesDialogRight=function(a){function b(){g.clear();if(a.outputs)for(var d=0;d","subgraph_property");c.dataset.name=f.name;c.dataset.slot=d;c.querySelector(".name").innerText=f.name;c.querySelector(".type").innerText=f.type;c.querySelector("button").addEventListener("click",function(d){a.removeOutput(Number(this.parentNode.dataset.slot));b()})}}}function d(){var d=this.parentNode,f=d.querySelector(".name").value,g=d.querySelector(".type").value;f&&-1==a.findOutputSlot(f)&& -(a.addOutput(f,g),d.querySelector(".name").value="",d.querySelector(".type").value="",b())}var f=this.canvas.parentNode.querySelector(".subgraph_dialog");f&&f.close();var g=this.createPanel("Subgraph Outputs",{closable:!0,width:500});g.node=a;g.classList.add("subgraph_dialog");f=g.addHTML(" + NameType","subgraph_property extra",!0);f.querySelector(".name").addEventListener("keydown", -function(a){13==a.keyCode&&d.apply(this)});f.querySelector("button").addEventListener("click",function(a){d.apply(this)});b();this.canvas.parentNode.appendChild(g);return g};h.prototype.checkPanels=function(){if(this.canvas)for(var a=this.canvas.parentNode.querySelectorAll(".litegraph.dialog"),b=0;b=Object.keys(a.selected_nodes).length)g.collapse(); -else for(var c in a.selected_nodes)a.selected_nodes[c].collapse();g.graph.afterChange()};h.onMenuNodePin=function(a,b,d,f,g){g.pin()};h.onMenuNodeMode=function(a,b,d,f,g){new e.ContextMenu(e.NODE_MODES,{event:d,callback:function(a){if(g){var b=Object.values(e.NODE_MODES).indexOf(a),d=function(d){0<=b&&e.NODE_MODES[b]?d.changeMode(b):(console.warn("unexpected mode: "+a),d.changeMode(e.ALWAYS))},f=h.active_canvas;if(!f.selected_nodes||1>=Object.keys(f.selected_nodes).length)d(g);else for(var c in f.selected_nodes)d(f.selected_nodes[c])}}, -parentMenu:f,node:g});return!1};h.onMenuNodeColors=function(a,b,d,f,g){if(!g)throw"no node for color";b=[];b.push({value:null,content:"No color"});for(var c in h.node_colors)a=h.node_colors[c],a={value:c,content:""+c+""},b.push(a);new e.ContextMenu(b,{event:d,callback:function(a){if(g){var b=a.value?h.node_colors[a.value]: -null;a=function(a){b?a.constructor===e.LGraphGroup?a.color=b.groupcolor:(a.color=b.color,a.bgcolor=b.bgcolor):(delete a.color,delete a.bgcolor)};var d=h.active_canvas;if(!d.selected_nodes||1>=Object.keys(d.selected_nodes).length)a(g);else for(var f in d.selected_nodes)a(d.selected_nodes[f]);g.setDirtyCanvas(!0,!0)}},parentMenu:f,node:g});return!1};h.onMenuNodeShapes=function(a,b,d,f,g){if(!g)throw"no node passed";new e.ContextMenu(e.VALID_SHAPES,{event:d,callback:function(a){if(g){g.graph.beforeChange(); -var b=h.active_canvas;if(!b.selected_nodes||1>=Object.keys(b.selected_nodes).length)g.shape=a;else for(var d in b.selected_nodes)b.selected_nodes[d].shape=a;g.graph.afterChange();g.setDirtyCanvas(!0)}},parentMenu:f,node:g});return!1};h.onMenuNodeRemove=function(a,b,d,f,g){if(!g)throw"no node passed";a=g.graph;a.beforeChange();b=h.active_canvas;if(!b.selected_nodes||1>=Object.keys(b.selected_nodes).length)!1!==g.removable&&a.remove(g);else for(var c in b.selected_nodes)d=b.selected_nodes[c],!1!==d.removable&& -a.remove(d);a.afterChange();g.setDirtyCanvas(!0,!0)};h.onMenuNodeToSubgraph=function(a,b,d,f,g){a=g.graph;if(b=h.active_canvas)d=Object.values(b.selected_nodes||{}),d.length||(d=[g]),f=e.createNode("graph/subgraph"),f.pos=g.pos.concat(),a.add(f),f.buildFromNodes(d),b.deselectAllNodes(),g.setDirtyCanvas(!0,!0)};h.onMenuNodeClone=function(a,b,d,f,g){g.graph.beforeChange();var c={};a=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),c[b.id]=b)}};b=h.active_canvas; -if(!b.selected_nodes||1>=Object.keys(b.selected_nodes).length)a(g);else for(var e in b.selected_nodes)a(b.selected_nodes[e]);Object.keys(c).length&&b.selectNodes(c);g.graph.afterChange();g.setDirtyCanvas(!0,!0)};h.node_colors={red:{color:"#322",bgcolor:"#533",groupcolor:"#A88"},brown:{color:"#332922",bgcolor:"#593930",groupcolor:"#b06634"},green:{color:"#232",bgcolor:"#353",groupcolor:"#8A8"},blue:{color:"#223",bgcolor:"#335",groupcolor:"#88A"},pale_blue:{color:"#2a363b",bgcolor:"#3f5159",groupcolor:"#3f789e"}, -cyan:{color:"#233",bgcolor:"#355",groupcolor:"#8AA"},purple:{color:"#323",bgcolor:"#535",groupcolor:"#a1309b"},yellow:{color:"#432",bgcolor:"#653",groupcolor:"#b58b2a"},black:{color:"#222",bgcolor:"#000",groupcolor:"#444"}};h.prototype.getCanvasMenuOptions=function(){if(this.getMenuOptions)var a=this.getMenuOptions();else a=[{content:"Add Node",has_submenu:!0,callback:h.onMenuAdd},{content:"Add Group",callback:h.onGroupAdd}],1Name", -f),l=e.querySelector("input");l&&c&&(l.value=c.label||"");var t=function(){a.graph.beforeChange();l.value&&(c&&(c.label=l.value),d.setDirty(!0));e.close();a.graph.afterChange()};e.querySelector("button").addEventListener("click",t);l.addEventListener("keydown",function(a){e.is_modified=!0;if(27==a.keyCode)e.close();else if(13==a.keyCode)t();else if(13!=a.keyCode&&"textarea"!=a.target.localName)return;a.preventDefault();a.stopPropagation()});l.focus()}},extra:a};a&&(c.title=a.type);var l=null;a&&(l= -a.getSlotInPosition(b.canvasX,b.canvasY),h.active_node=a);l?(g=[],a.getSlotMenuOptions?g=a.getSlotMenuOptions(l):(l&&l.output&&l.output.links&&l.output.links.length&&g.push({content:"Disconnect Links",slot:l}),b=l.input||l.output,b.removable&&g.push(b.locked?"Cannot remove":{content:"Remove Slot",slot:l}),b.nameLocked||g.push({content:"Rename Slot",slot:l})),c.title=(l.input?l.input.type:l.output.type)||"*",l.input&&l.input.type==e.ACTION&&(c.title="Action"),l.output&&l.output.type==e.EVENT&&(c.title= -"Event")):a?g=this.getNodeMenuOptions(a):(g=this.getCanvasMenuOptions(),(l=this.graph.getGroupOnPos(b.canvasX,b.canvasY))&&g.push(null,{content:"Edit Group",has_submenu:!0,submenu:{title:"Group",extra:l,options:this.getGroupMenuOptions(l)}}));g&&new e.ContextMenu(g,c,f)};e.compareObjects=function(a,b){for(var d in a)if(a[d]!=b[d])return!1;return!0};e.distance=E;e.colorToString=function(a){return"rgba("+Math.round(255*a[0]).toFixed()+","+Math.round(255*a[1]).toFixed()+","+Math.round(255*a[2]).toFixed()+ -","+(4==a.length?a[3].toFixed(2):"1.0")+")"};e.isInsideRectangle=D;e.growBounding=function(a,b,d){ba[2]&&(a[2]=b);da[3]&&(a[3]=d)};e.isInsideBounding=function(a,b){return a[0]b[1][0]||a[1]>b[1][1]?!1:!0};e.overlapBounding=J;e.hex2num=function(a){"#"==a.charAt(0)&&(a=a.slice(1));a=a.toUpperCase();for(var b=Array(3),d=0,f,g,c=0;6>c;c+=2)f="0123456789ABCDEF".indexOf(a.charAt(c)),g="0123456789ABCDEF".indexOf(a.charAt(c+1)),b[d]=16*f+g,d++;return b}; -e.num2hex=function(a){for(var b="#",d,f,g=0;3>g;g++)d=a[g]/16,f=a[g]%16,b+="0123456789ABCDEF".charAt(d)+"0123456789ABCDEF".charAt(f);return b};L.prototype.addItem=function(a,b,d){function f(a){var b=this.value;b&&b.has_submenu&&g.call(this,a)}function g(a){var b=this.value,f=!0;c.current_submenu&&c.current_submenu.close(a);if(d.callback){var g=d.callback.call(this,b,d,a,c,d.node);!0===g&&(f=!1)}if(b&&(b.callback&&!d.ignore_item_callbacks&&!0!==b.disabled&&(g=b.callback.call(this,b,d,a,c,d.extra), -!0===g&&(f=!1)),b.submenu)){if(!b.submenu.options)throw"ContextMenu submenu needs options";new c.constructor(b.submenu.options,{callback:b.submenu.callback,event:a,parentMenu:c,ignore_item_callbacks:b.submenu.ignore_item_callbacks,title:b.submenu.title,extra:b.submenu.extra,autoopen:d.autoopen});f=!1}f&&!c.lock&&c.close()}var c=this;d=d||{};var l=document.createElement("div");l.className="litemenu-entry submenu";var m=!1;if(null===b)l.classList.add("separator");else{l.innerHTML=b&&b.title?b.title: -a;if(l.value=b)b.disabled&&(m=!0,l.classList.add("disabled")),(b.submenu||b.has_submenu)&&l.classList.add("has_submenu");"function"==typeof b?(l.dataset.value=a,l.onclick_callback=b):l.dataset.value=b;b.className&&(l.className+=" "+b.className)}this.root.appendChild(l);m||l.addEventListener("click",g);!m&&d.autoopen&&e.pointerListenerAdd(l,"enter",f);return l};L.prototype.close=function(a,b){this.root.parentNode&&this.root.parentNode.removeChild(this.root);this.parentMenu&&!b&&(this.parentMenu.lock= -!1,this.parentMenu.current_submenu=null,void 0===a?this.parentMenu.close():a&&!L.isCursorOverElement(a,this.parentMenu.root)&&L.trigger(this.parentMenu.root,e.pointerevents_method+"leave",a));this.current_submenu&&this.current_submenu.close(a,!0);this.root.closing_timer&&clearTimeout(this.root.closing_timer)};L.trigger=function(a,b,d,f){var g=document.createEvent("CustomEvent");g.initCustomEvent(b,!0,!0,d);g.srcElement=f;a.dispatchEvent?a.dispatchEvent(g):a.__events&&a.__events.dispatchEvent(g);return g}; -L.prototype.getTopMenu=function(){return this.options.parentMenu?this.options.parentMenu.getTopMenu():this};L.prototype.getFirstEvent=function(){return this.options.parentMenu?this.options.parentMenu.getFirstEvent():this.options.event};L.isCursorOverElement=function(a,b){var d=a.clientX;a=a.clientY;return(b=b.getBoundingClientRect())?a>b.top&&ab.left&&dMath.abs(b))return f[1];a=(a-f[0])/b;return f[1]*(1-a)+g[1]*a}}return 0}};H.prototype.draw=function(a,b,d,f,g,c){if(d=this.points){this.size=b;var e=b[0]-2*this.margin;b=b[1]-2*this.margin;g=g||"#666";a.save();a.translate(this.margin,this.margin);f&&(a.fillStyle="#111",a.fillRect(0,0,e,b),a.fillStyle="#222",a.fillRect(.5*e,0,1,b),a.strokeStyle="#333", -a.strokeRect(0,0,e,b));a.strokeStyle=g;c&&(a.globalAlpha=.5);a.beginPath();for(f=0;fa[1])){var f=this.size[0]-2*this.margin,g=this.size[1]-2*this.margin,c=a[0]-this.margin;a=a[1]-this.margin; -this.selected=this.getCloserPoint([c,a],30/b.ds.scale);-1==this.selected&&(b=[c/f,1-a/g],d.push(b),d.sort(function(a,b){return a[0]-b[0]}),this.selected=d.indexOf(b),this.must_update=!0);if(-1!=this.selected)return!0}};H.prototype.onMouseMove=function(a,b){var d=this.points;if(d){var f=this.selected;if(!(0>f)){var g=(a[0]-this.margin)/(this.size[0]-2*this.margin),c=(a[1]-this.margin)/(this.size[1]-2*this.margin);this._nearest=this.getCloserPoint([a[0]-this.margin,a[1]-this.margin],30/b.ds.scale); -if(b=d[f]){var e=0==f||f==d.length-1;!e&&(-10>a[0]||a[0]>this.size[0]+10||-10>a[1]||a[1]>this.size[1]+10)?(d.splice(f,1),this.selected=-1):(b[0]=e?0==f?0:1:F(g,0,1),b[1]=1-F(c,0,1),d.sort(function(a,b){return a[0]-b[0]}),this.selected=d.indexOf(b),this.must_update=!0)}}}};H.prototype.onMouseUp=function(a,b){this.selected=-1;return!1};H.prototype.getCloserPoint=function(a,b){var d=this.points;if(!d)return-1;b=b||30;for(var f=this.size[0]-2*this.margin,g=this.size[1]-2*this.margin,c=d.length,e=[0,0], -l=1E6,m=-1,t=0;tl||w>b||(m=t,l=w)}return m};e.CurveEditor=H;e.getParameterNames=function(a){return(a+"").replace(/[/][/].*$/gm,"").replace(/\s+/g,"").replace(/[/][*][^/*]*[*][/]/g,"").split("){",1)[0].replace(/^[^(]*[(]/,"").replace(/=[^,]+/g,"").split(",").filter(Boolean)};e.pointerListenerAdd=function(a,b,d,f){f=void 0===f?!1:f;if(a&&a.addEventListener&&b&&"function"===typeof d){var g=e.pointerevents_method;if("pointer"==g&&!window.PointerEvent)switch(console.warn("sMethod=='pointer' && !window.PointerEvent"), -console.log("Converting pointer["+b+"] : down move up cancel enter TO touchstart touchmove touchend, etc .."),b){case "down":g="touch";b="start";break;case "move":g="touch";break;case "up":g="touch";b="end";break;case "cancel":g="touch";break;case "enter":console.log("debug: Should I send a move event?");break;default:console.warn("PointerEvent not available in this browser ? The event "+b+" would not be called")}switch(b){case "down":case "up":case "move":case "over":case "out":case "enter":a.addEventListener(g+ -b,d,f);case "leave":case "cancel":case "gotpointercapture":case "lostpointercapture":if("mouse"!=g)return a.addEventListener(g+b,d,f);default:return a.addEventListener(b,d,f)}}};e.pointerListenerRemove=function(a,b,d,f){f=void 0===f?!1:f;if(a&&a.removeEventListener&&b&&"function"===typeof d)switch(b){case "down":case "up":case "move":case "over":case "out":case "enter":"pointer"!=e.pointerevents_method&&"mouse"!=e.pointerevents_method||a.removeEventListener(e.pointerevents_method+b,d,f);case "leave":case "cancel":case "gotpointercapture":case "lostpointercapture":if("pointer"== -e.pointerevents_method)return a.removeEventListener(e.pointerevents_method+b,d,f);default:return a.removeEventListener(b,d,f)}};y.clamp=F;"undefined"==typeof window||window.requestAnimationFrame||(window.requestAnimationFrame=window.webkitRequestAnimationFrame||window.mozRequestAnimationFrame||function(a){window.setTimeout(a,1E3/60)})})(this); -"undefined"!=typeof exports&&(exports.LiteGraph=this.LiteGraph,exports.LGraph=this.LGraph,exports.LLink=this.LLink,exports.LGraphNode=this.LGraphNode,exports.LGraphGroup=this.LGraphGroup,exports.DragAndScale=this.DragAndScale,exports.LGraphCanvas=this.LGraphCanvas,exports.ContextMenu=this.ContextMenu); -(function(y){function c(){this.addOutput("in ms","number");this.addOutput("in sec","number")}function k(){this.size=[140,80];this.properties={enabled:!0};this.enabled=!0;this.subgraph=new I.LGraph;this.subgraph._subgraph_node=this;this.subgraph._is_subgraph=!0;this.subgraph.onTrigger=this.onSubgraphTrigger.bind(this);this.subgraph.onInputAdded=this.onSubgraphNewInput.bind(this);this.subgraph.onInputRenamed=this.onSubgraphRenamedInput.bind(this);this.subgraph.onInputTypeChanged=this.onSubgraphTypeChangeInput.bind(this); -this.subgraph.onInputRemoved=this.onSubgraphRemovedInput.bind(this);this.subgraph.onOutputAdded=this.onSubgraphNewOutput.bind(this);this.subgraph.onOutputRenamed=this.onSubgraphRenamedOutput.bind(this);this.subgraph.onOutputTypeChanged=this.onSubgraphTypeChangeOutput.bind(this);this.subgraph.onOutputRemoved=this.onSubgraphRemovedOutput.bind(this)}function n(){this.addOutput("","number");this.name_in_graph="";this.properties={name:"",type:"number",value:0};var a=this;this.name_widget=this.addWidget("text", -"Name",this.properties.name,function(b){b&&a.setProperty("name",b)});this.type_widget=this.addWidget("text","Type",this.properties.type,function(b){a.setProperty("type",b)});this.value_widget=this.addWidget("number","Value",this.properties.value,function(b){a.setProperty("value",b)});this.widgets_up=!0;this.size=[180,90]}function r(){this.addInput("","");this.name_in_graph="";this.properties={name:"",type:""};this.name_widget=this.addWidget("text","Name",this.properties.name,"name");this.type_widget= -this.addWidget("text","Type",this.properties.type,"type");this.widgets_up=!0;this.size=[180,60]}function u(){this.addOutput("value","number");this.addProperty("value",1);this.widget=this.addWidget("number","value",1,"value");this.widgets_up=!0;this.size=[180,30]}function h(){this.addOutput("bool","boolean");this.addProperty("value",!0);this.widget=this.addWidget("toggle","value",!0,"value");this.widgets_up=this.serialize_widgets=!0;this.size=[140,30]}function E(){this.addOutput("string","string"); -this.addProperty("value","");this.widget=this.addWidget("text","value","","value");this.widgets_up=!0;this.size=[180,30]}function D(){this.addOutput("obj","object");this.size=[120,30];this._object={}}function J(){this.addInput("url","string");this.addOutput("file","string");this.addProperty("url","");this.addProperty("type","text");this.widget=this.addWidget("text","url","","url");this._data=null}function L(){this.addInput("parse",I.ACTION);this.addInput("json","string");this.addOutput("done",I.EVENT); -this.addOutput("object","object");this.widget=this.addWidget("button","parse","",this.parse.bind(this));this._obj=this._str=null}function H(){this.addOutput("data","object");this.addProperty("value","");this.widget=this.addWidget("text","json","","value");this.widgets_up=!0;this.size=[140,30];this._value=null}function F(){this._value=[];this.addInput("json","");this.addOutput("arrayOut","array");this.addOutput("length","number");this.addProperty("value","[]");this.widget=this.addWidget("text","array", -this.properties.value,"value");this.widgets_up=!0;this.size=[140,50]}function e(){this.addInput("arr","array");this.addInput("value","");this.addOutput("arr","array");this.properties={index:0};this.widget=this.addWidget("number","i",this.properties.index,"index",{precision:0,step:10,min:0})}function K(){this.addInput("array","array,table,string");this.addInput("index","number");this.addOutput("value","");this.addProperty("index",0)}function B(){this.addInput("table","table");this.addInput("row","number"); -this.addInput("col","number");this.addOutput("value","");this.addProperty("row",0);this.addProperty("column",0)}function M(){this.addInput("obj","object");this.addOutput("property",0);this.addProperty("value",0);this.widget=this.addWidget("text","prop.","",this.setValue.bind(this));this.widgets_up=!0;this.size=[140,30];this._value=null}function l(){this.addInput("obj","");this.addOutput("keys","array");this.size=[140,30]}function m(){this.addInput("obj","");this.addInput("value","");this.addOutput("obj", -"");this.properties={property:""};this.name_widget=this.addWidget("text","prop.",this.properties.property,"property")}function w(){this.addInput("A","object");this.addInput("B","object");this.addOutput("out","object");this._result={};var a=this;this.addWidget("button","clear","",function(){a._result={}});this.size=this.computeSize()}function N(){this.size=[60,30];this.addInput("in");this.addOutput("out");this.properties={varname:"myname",container:N.LITEGRAPH};this.value=null}function a(a){return a&& -null!=a.length?Number(a.length):0}function a(a){return a&&null!=a.length?Number(a.length):0}function b(){this.size=[60,30];this.addInput("data",0);this.addInput("download",I.ACTION);this.properties={filename:"data.json"};this.value=null;var a=this;this.addWidget("button","Download","",function(b){a.value&&a.downloadAsFile()})}function d(){this.size=[60,30];this.addInput("value",0,{label:""});this.value=0}function f(){this.addInput("in",0);this.addOutput("out",0);this.size=[40,30]}function g(){this.mode= -I.ON_EVENT;this.size=[80,30];this.addProperty("msg","");this.addInput("log",I.EVENT);this.addInput("msg",0)}function q(){this.mode=I.ON_EVENT;this.addProperty("msg","");this.addInput("",I.EVENT);this.widget=this.addWidget("text","Text","","msg");this.widgets_up=!0;this.size=[200,30]}function z(){this.size=[60,30];this.addProperty("onExecute","return A;");this.addInput("A",0);this.addInput("B",0);this.addOutput("out",0);this._func=null;this.data={}}function P(){this.addInput("A",0);this.addInput("B", -0);this.addOutput("true","boolean");this.addOutput("false","boolean");this.addProperty("A",1);this.addProperty("B",1);this.addProperty("OP","==","enum",{values:P.values});this.addWidget("combo","Op.",this.properties.OP,{property:"OP",values:P.values});this.size=[80,60]}var I=y.LiteGraph;c.title="Time";c.desc="Time";c.prototype.onExecute=function(){this.setOutputData(0,1E3*this.graph.globaltime);this.setOutputData(1,this.graph.globaltime)};I.registerNodeType("basic/time",c);k.title="Subgraph";k.desc= -"Graph inside a node";k.title_color="#334";k.prototype.onGetInputs=function(){return[["enabled","boolean"]]};k.prototype.onDblClick=function(a,b,d){var f=this;setTimeout(function(){d.openSubgraph(f.subgraph)},10)};k.prototype.onAction=function(a,b){this.subgraph.onAction(a,b)};k.prototype.onExecute=function(){if(this.enabled=this.getInputOrProperty("enabled")){if(this.inputs)for(var a=0;aa&&(b[0]=c?this.trigger(null,e,h):this._pending.push([c,e])};D.prototype.onExecute= -function(c,e){c=1E3*this.graph.elapsed_time;this.isInputConnected(1)&&(this.properties.time_in_ms=this.getInputData(1));for(var h=0;he[1]))return this.old_y=c.canvasY,this.captureInput(!0),this.mouse_captured=!0};n.prototype.onMouseMove=function(c){if(this.mouse_captured){var e=this.old_y-c.canvasY;c.shiftKey&&(e*=10);if(c.metaKey||c.altKey)e*=.1;this.old_y=c.canvasY;c=this._remainder+e/n.pixels_threshold;this._remainder=c%1;c=clamp(this.properties.value+ -(c|0)*this.properties.step,this.properties.min,this.properties.max);this.properties.value=c;this.graph._version++;this.setDirtyCanvas(!0)}};n.prototype.onMouseUp=function(c,e){200>c.click_time&&(this.properties.value=clamp(this.properties.value+(e[1]>.5*this.size[1]?-1:1)*this.properties.step,this.properties.min,this.properties.max),this.graph._version++,this.setDirtyCanvas(!0));this.mouse_captured&&(this.mouse_captured=!1,this.captureInput(!1))};H.registerNodeType("widget/number",n);r.title="Combo"; -r.desc="Widget to select from a list";r.prototype.onExecute=function(){this.setOutputData(0,this.properties.value)};r.prototype.onPropertyChanged=function(c,e){"values"==c?(this._values=e.split(";"),this.widget.options.values=this._values):"value"==c&&(this.widget.value=e)};H.registerNodeType("widget/combo",r);u.title="Knob";u.desc="Circular controller";u.size=[80,100];u.prototype.onDrawForeground=function(c){if(!this.flags.collapsed){-1==this.value&&(this.value=(this.properties.value-this.properties.min)/ -(this.properties.max-this.properties.min));var e=.5*this.size[0],h=.5*this.size[1],k=.5*Math.min(this.size[0],this.size[1])-5;c.globalAlpha=1;c.save();c.translate(e,h);c.rotate(.75*Math.PI);c.fillStyle="rgba(0,0,0,0.5)";c.beginPath();c.moveTo(0,0);c.arc(0,0,k,0,1.5*Math.PI);c.fill();c.strokeStyle="black";c.fillStyle=this.properties.color;c.lineWidth=2;c.beginPath();c.moveTo(0,0);c.arc(0,0,k-4,0,1.5*Math.PI*Math.max(.01,this.value));c.closePath();c.fill();c.lineWidth=1;c.globalAlpha=1;c.restore(); -c.fillStyle="black";c.beginPath();c.arc(e,h,.75*k,0,2*Math.PI,!0);c.fill();c.fillStyle=this.mouseOver?"white":this.properties.color;c.beginPath();var n=this.value*Math.PI*1.5+.75*Math.PI;c.arc(e+Math.cos(n)*k*.65,h+Math.sin(n)*k*.65,.05*k,0,2*Math.PI,!0);c.fill();c.fillStyle=this.mouseOver?"white":"#AAA";c.font=Math.floor(.5*k)+"px Arial";c.textAlign="center";c.fillText(this.properties.value.toFixed(this.properties.precision),e,h+.15*k)}};u.prototype.onExecute=function(){this.setOutputData(0,this.properties.value); -this.boxcolor=H.colorToString([this.value,this.value,this.value])};u.prototype.onMouseDown=function(c){this.center=[.5*this.size[0],.5*this.size[1]+20];this.radius=.5*this.size[0];if(20>c.canvasY-this.pos[1]||H.distance([c.canvasX,c.canvasY],[this.pos[0]+this.center[0],this.pos[1]+this.center[1]])>this.radius)return!1;this.oldmouse=[c.canvasX-this.pos[0],c.canvasY-this.pos[1]];this.captureInput(!0);return!0};u.prototype.onMouseMove=function(c){if(this.oldmouse){c=[c.canvasX-this.pos[0],c.canvasY- -this.pos[1]];var e=this.value;e-=.01*(c[1]-this.oldmouse[1]);1e&&(e=0);this.value=e;this.properties.value=this.properties.min+(this.properties.max-this.properties.min)*this.value;this.oldmouse=c;this.setDirtyCanvas(!0)}};u.prototype.onMouseUp=function(c){this.oldmouse&&(this.oldmouse=null,this.captureInput(!1))};u.prototype.onPropertyChanged=function(c,e){if("min"==c||"max"==c||"value"==c)return this.properties[c]=parseFloat(e),!0};H.registerNodeType("widget/knob",u);h.title="Inner Slider"; -h.prototype.onPropertyChanged=function(c,e){"value"==c&&(this.slider.value=e)};h.prototype.onExecute=function(){this.setOutputData(0,this.properties.value)};H.registerNodeType("widget/internal_slider",h);E.title="H.Slider";E.desc="Linear slider controller";E.prototype.onDrawForeground=function(c){-1==this.value&&(this.value=(this.properties.value-this.properties.min)/(this.properties.max-this.properties.min));c.globalAlpha=1;c.lineWidth=1;c.fillStyle="#000";c.fillRect(2,2,this.size[0]-4,this.size[1]- -4);c.fillStyle=this.properties.color;c.beginPath();c.rect(4,4,(this.size[0]-8)*this.value,this.size[1]-8);c.fill()};E.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=H.colorToString([this.value,this.value,this.value])};E.prototype.onMouseDown=function(c){if(0>c.canvasY-this.pos[1])return!1;this.oldmouse=[c.canvasX-this.pos[0],c.canvasY-this.pos[1]];this.captureInput(!0); -return!0};E.prototype.onMouseMove=function(c){if(this.oldmouse){c=[c.canvasX-this.pos[0],c.canvasY-this.pos[1]];var e=this.value;e+=(c[0]-this.oldmouse[0])/this.size[0];1e&&(e=0);this.value=e;this.oldmouse=c;this.setDirtyCanvas(!0)}};E.prototype.onMouseUp=function(c){this.oldmouse=null;this.captureInput(!1)};E.prototype.onMouseLeave=function(c){};H.registerNodeType("widget/hslider",E);D.title="Progress";D.desc="Shows data in linear progress";D.prototype.onExecute=function(){var c=this.getInputData(0); -void 0!=c&&(this.properties.value=c)};D.prototype.onDrawForeground=function(c){c.lineWidth=1;c.fillStyle=this.properties.color;var e=(this.properties.value-this.properties.min)/(this.properties.max-this.properties.min);e=Math.min(1,e);e=Math.max(0,e);c.fillRect(2,2,(this.size[0]-4)*e,this.size[1]-4)};H.registerNodeType("widget/progress",D);J.title="Text";J.desc="Shows the input value";J.widgets=[{name:"resize",text:"Resize box",type:"button"},{name:"led_text",text:"LED",type:"minibutton"},{name:"normal_text", -text:"Normal",type:"minibutton"}];J.prototype.onDrawForeground=function(c){c.fillStyle=this.properties.color;var e=this.properties.value;this.properties.glowSize?(c.shadowColor=this.properties.color,c.shadowOffsetX=0,c.shadowOffsetY=0,c.shadowBlur=this.properties.glowSize):c.shadowColor="transparent";var h=this.properties.fontsize;c.textAlign=this.properties.align;c.font=h.toString()+"px "+this.properties.font;this.str="number"==typeof e?e.toFixed(this.properties.decimals):e;if("string"==typeof this.str){e= -this.str.replace(/[\r\n]/g,"\\n").split("\\n");for(var k=0;kr?k.xbox.axes.lx:0,this._left_axis[1]=Math.abs(k.xbox.axes.ly)>r?k.xbox.axes.ly:0,this._right_axis[0]=Math.abs(k.xbox.axes.rx)>r?k.xbox.axes.rx:0,this._right_axis[1]=Math.abs(k.xbox.axes.ry)>r?k.xbox.axes.ry:0,this._triggers[0]=Math.abs(k.xbox.axes.ltrigger)>r?k.xbox.axes.ltrigger: -0,this._triggers[1]=Math.abs(k.xbox.axes.rtrigger)>r?k.xbox.axes.rtrigger:0);if(this.outputs)for(r=0;rr;r++)if(k[r]){k=k[r];r=this.xbox_mapping;r||(r=this.xbox_mapping={axes:[], -buttons:{},hat:"",hatmap:c.CENTER});r.axes.lx=k.axes[0];r.axes.ly=k.axes[1];r.axes.rx=k.axes[2];r.axes.ry=k.axes[3];r.axes.ltrigger=k.buttons[6].value;r.axes.rtrigger=k.buttons[7].value;r.hat="";r.hatmap=c.CENTER;for(var u=0;uu)r.buttons[c.mapping_array[u]]=k.buttons[u].pressed,k.buttons[u].was_pressed&&this.trigger(c.mapping_array[u]+"_button_event");else switch(u){case 12:k.buttons[u].pressed&&(r.hat+="up",r.hatmap|=c.UP); -break;case 13:k.buttons[u].pressed&&(r.hat+="down",r.hatmap|=c.DOWN);break;case 14:k.buttons[u].pressed&&(r.hat+="left",r.hatmap|=c.LEFT);break;case 15:k.buttons[u].pressed&&(r.hat+="right",r.hatmap|=c.RIGHT);break;case 16:r.buttons.home=k.buttons[u].pressed}k.xbox=r;return k}};c.prototype.onDrawBackground=function(c){if(!this.flags.collapsed){var k=this._left_axis,n=this._right_axis;c.strokeStyle="#88A";c.strokeRect(.5*(k[0]+1)*this.size[0]-4,.5*(k[1]+1)*this.size[1]-4,8,8);c.strokeStyle="#8A8"; -c.strokeRect(.5*(n[0]+1)*this.size[0]-4,.5*(n[1]+1)*this.size[1]-4,8,8);k=this.size[1]/this._current_buttons.length;c.fillStyle="#AEB";for(n=0;n","enum",{values:N.values});this.addWidget("combo", -"Cond.",this.properties.OP,{property:"OP",values:N.values});this.size=[80,60]}function a(){this.addInput("in",0);this.addInput("cond","boolean");this.addOutput("true",0);this.addOutput("false",0);this.size=[80,60]}function b(){this.addInput("inc","number");this.addOutput("total","number");this.addProperty("increment",1);this.addProperty("value",0)}function d(){this.addInput("v","number");this.addOutput("sin","number");this.addProperty("amplitude",1);this.addProperty("offset",0);this.bgImageUrl="nodes/imgs/icon-sin.png"} -function f(){this.addInput("x","number");this.addInput("y","number");this.addOutput("","number");this.properties={x:1,y:1,formula:"x+y"};this.code_widget=this.addWidget("text","F(x,y)",this.properties.formula,function(a,b,d){d.properties.formula=a});this.addWidget("toggle","allow",v.allow_scripts,function(a){v.allow_scripts=a});this._func=null}function g(){this.addInput("vec2","vec2");this.addOutput("x","number");this.addOutput("y","number")}function q(){this.addInputs([["x","number"],["y","number"]]); -this.addOutput("vec2","vec2");this.properties={x:0,y:0};this._data=new Float32Array(2)}function z(){this.addInput("vec3","vec3");this.addOutput("x","number");this.addOutput("y","number");this.addOutput("z","number")}function P(){this.addInputs([["x","number"],["y","number"],["z","number"]]);this.addOutput("vec3","vec3");this.properties={x:0,y:0,z:0};this._data=new Float32Array(3)}function I(){this.addInput("vec4","vec4");this.addOutput("x","number");this.addOutput("y","number");this.addOutput("z", -"number");this.addOutput("w","number")}function t(){this.addInputs([["x","number"],["y","number"],["z","number"],["w","number"]]);this.addOutput("vec4","vec4");this.properties={x:0,y:0,z:0,w:0};this._data=new Float32Array(4)}var v=y.LiteGraph;c.title="Converter";c.desc="type A to type B";c.prototype.onExecute=function(){var a=this.getInputData(0);if(null!=a&&this.outputs)for(var b=0;ba&&(a+=1024);var c=Math.floor(a);a-=c;d=h.data[c];c=h.data[1023==c?0:c+1];b&&(a=a*a*a*(a*(6*a-15)+10));return d*(1-a)+c*a};h.prototype.onExecute=function(){var a=this.getInputData(0)|| -0,b=this.properties.octaves||1,d=0,c=1;a+=this.properties.seed||0;for(var f=this.properties.speed||1,g=0,e=0;ec);++e);a=this.properties.min;this._last_v=d/g*(this.properties.max-a)+a;this.setOutputData(0,this._last_v)};h.prototype.onDrawBackground=function(a){this.outputs[0].label=(this._last_v||0).toFixed(3)};v.registerNodeType("math/noise",h);E.title="Spikes";E.desc="spike every random time";E.prototype.onExecute= -function(){var a=this.graph.elapsed_time;this._remaining_time-=a;this._blink_time-=a;a=0;0this._remaining_time?(this._remaining_time=Math.random()*(this.properties.max_time-this.properties.min_time)+this.properties.min_time,this._blink_time=this.properties.duration,this.boxcolor="#FFF"):this.boxcolor="#000";this.setOutputData(0,a)};v.registerNodeType("math/spikes",E);D.title="Clamp";D.desc="Clamp number between min and max"; -D.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&(a=Math.max(this.properties.min,a),a=Math.min(this.properties.max,a),this.setOutputData(0,a))};D.prototype.getCode=function(a){a="";this.isInputConnected(0)&&(a+="clamp({{0}},"+this.properties.min+","+this.properties.max+")");return a};v.registerNodeType("math/clamp",D);J.title="Lerp";J.desc="Linear Interpolation";J.prototype.onExecute=function(){var a=this.getInputData(0);null==a&&(a=0);var b=this.getInputData(1);null==b&&(b=0); -var d=this.properties.f,c=this.getInputData(2);void 0!==c&&(d=c);this.setOutputData(0,a*(1-d)+b*d)};J.prototype.onGetInputs=function(){return[["f","number"]]};v.registerNodeType("math/lerp",J);L.title="Abs";L.desc="Absolute";L.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&this.setOutputData(0,Math.abs(a))};v.registerNodeType("math/abs",L);H.title="Floor";H.desc="Floor number to remove fractional part";H.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&this.setOutputData(0, -Math.floor(a))};v.registerNodeType("math/floor",H);F.title="Frac";F.desc="Returns fractional part";F.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&this.setOutputData(0,a%1)};v.registerNodeType("math/frac",F);e.title="Smoothstep";e.desc="Smoothstep";e.prototype.onExecute=function(){var a=this.getInputData(0);if(void 0!==a){var b=this.properties.A;a=clamp((a-b)/(this.properties.B-b),0,1);this.setOutputData(0,a*a*(3-2*a))}};v.registerNodeType("math/smoothstep",e);K.title="Scale"; -K.desc="v * factor";K.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&this.setOutputData(0,a*this.properties.factor)};v.registerNodeType("math/scale",K);B.title="Gate";B.desc="if v is true, then outputs A, otherwise B";B.prototype.onExecute=function(){var a=this.getInputData(0);this.setOutputData(0,this.getInputData(a?1:2))};v.registerNodeType("math/gate",B);M.title="Average";M.desc="Average Filter";M.prototype.onExecute=function(){var a=this.getInputData(0);null==a&&(a=0);var b= -this._values.length;this._values[this._current%b]=a;this._current+=1;this._current>b&&(this._current=0);for(var d=a=0;db&&(b=1);this.properties.samples=Math.round(b);a=this._values;this._values=new Float32Array(this.properties.samples);a.length<=this._values.length?this._values.set(a):this._values.set(a.subarray(0,this._values.length))};v.registerNodeType("math/average",M);l.title="TendTo";l.desc="moves the output value always closer to the input"; -l.prototype.onExecute=function(){var a=this.getInputData(0);null==a&&(a=0);var b=this.properties.factor;this._value=null==this._value?a:this._value*(1-b)+a*b;this.setOutputData(0,this._value)};v.registerNodeType("math/tendTo",l);m.values="+ - * / % ^ max min".split(" ");m.funcs={"+":function(a,b){return a+b},"-":function(a,b){return a-b},x:function(a,b){return a*b},X:function(a,b){return a*b},"*":function(a,b){return a*b},"/":function(a,b){return a/b},"%":function(a,b){return a%b},"^":function(a, -b){return Math.pow(a,b)},max:function(a,b){return Math.max(a,b)},min:function(a,b){return Math.min(a,b)}};m.title="Operation";m.desc="Easy math operators";m["@OP"]={type:"enum",title:"operation",values:m.values};m.size=[100,60];m.prototype.getTitle=function(){return"max"==this.properties.OP||"min"==this.properties.OP?this.properties.OP+"(A,B)":"A "+this.properties.OP+" B"};m.prototype.setValue=function(a){"string"==typeof a&&(a=parseFloat(a));this.properties.value=a};m.prototype.onPropertyChanged= -function(a,b){"OP"==a&&(this._func=m.funcs[this.properties.OP],this._func||(console.warn("Unknown operation: "+this.properties.OP),this._func=function(a){return a}))};m.prototype.onExecute=function(){var a=this.getInputData(0),b=this.getInputData(1);null!=a?a.constructor===Number&&(this.properties.A=a):a=this.properties.A;null!=b?this.properties.B=b:b=this.properties.B;var d=m.funcs[this.properties.OP];if(a.constructor===Number)var c=d(a,b);else if(a.constructor===Array){c=this._result;c.length=a.length; -for(var f=0;fB":g=a>b;break;case "A=B":g=a>=b}this.setOutputData(d,g)}}};w.prototype.onGetOutputs=function(){return[["A==B","boolean"],["A!=B","boolean"],["A>B","boolean"],["A=B","boolean"],["A<=B","boolean"]]};v.registerNodeType("math/compare",w);v.registerSearchboxExtra("math/compare","==",{outputs:[["A==B","boolean"]],title:"A==B"});v.registerSearchboxExtra("math/compare","!=",{outputs:[["A!=B","boolean"]],title:"A!=B"});v.registerSearchboxExtra("math/compare",">",{outputs:[["A>B","boolean"]], -title:"A>B"});v.registerSearchboxExtra("math/compare","<",{outputs:[["A=",{outputs:[["A>=B","boolean"]],title:"A>=B"});v.registerSearchboxExtra("math/compare","<=",{outputs:[["A<=B","boolean"]],title:"A<=B"});N.values="> < == != <= >= || &&".split(" ");N["@OP"]={type:"enum",title:"operation",values:N.values};N.title="Condition";N.desc="evaluates condition between A and B";N.prototype.getTitle=function(){return"A "+this.properties.OP+ -" B"};N.prototype.onExecute=function(){var a=this.getInputData(0);void 0===a?a=this.properties.A:this.properties.A=a;var b=this.getInputData(1);void 0===b?b=this.properties.B:this.properties.B=b;var d=!0;switch(this.properties.OP){case ">":d=a>b;break;case "<":d=a=":d=a>=b;break;case "||":d=a||b;break;case "&&":d=a&&b}this.setOutputData(0,d);this.setOutputData(1,!d)};v.registerNodeType("math/condition",N);a.title= -"Branch";a.desc="If condition is true, outputs IN in true, otherwise in false";a.prototype.onExecute=function(){var a=this.getInputData(0);this.getInputData(1)?(this.setOutputData(0,a),this.setOutputData(1,null)):(this.setOutputData(0,null),this.setOutputData(1,a))};v.registerNodeType("math/branch",a);b.title="Accumulate";b.desc="Increments a value every time";b.prototype.onExecute=function(){null===this.properties.value&&(this.properties.value=0);var a=this.getInputData(0);this.properties.value= -null!==a?this.properties.value+a:this.properties.value+this.properties.increment;this.setOutputData(0,this.properties.value)};v.registerNodeType("math/accumulate",b);d.title="Trigonometry";d.desc="Sin Cos Tan";d.prototype.onExecute=function(){var a=this.getInputData(0);null==a&&(a=0);var b=this.properties.amplitude,d=this.findInputSlot("amplitude");-1!=d&&(b=this.getInputData(d));var c=this.properties.offset;d=this.findInputSlot("offset");-1!=d&&(c=this.getInputData(d));d=0;for(var f=this.outputs.length;d< -f;++d){switch(this.outputs[d].name){case "sin":var g=Math.sin(a);break;case "cos":g=Math.cos(a);break;case "tan":g=Math.tan(a);break;case "asin":g=Math.asin(a);break;case "acos":g=Math.acos(a);break;case "atan":g=Math.atan(a)}this.setOutputData(d,b*g+c)}};d.prototype.onGetInputs=function(){return[["v","number"],["amplitude","number"],["offset","number"]]};d.prototype.onGetOutputs=function(){return[["sin","number"],["cos","number"],["tan","number"],["asin","number"],["acos","number"],["atan","number"]]}; -v.registerNodeType("math/trigonometry",d);v.registerSearchboxExtra("math/trigonometry","SIN()",{outputs:[["sin","number"]],title:"SIN()"});v.registerSearchboxExtra("math/trigonometry","COS()",{outputs:[["cos","number"]],title:"COS()"});v.registerSearchboxExtra("math/trigonometry","TAN()",{outputs:[["tan","number"]],title:"TAN()"});f.title="Formula";f.desc="Compute formula";f.size=[160,100];M.prototype.onPropertyChanged=function(a,b){"formula"==a&&(this.code_widget.value=b)};f.prototype.onExecute= -function(){if(v.allow_scripts){var a=this.getInputData(0),b=this.getInputData(1);null!=a?this.properties.x=a:a=this.properties.x;null!=b?this.properties.y=b:b=this.properties.y;try{this._func&&this._func_code==this.properties.formula||(this._func=new Function("x","y","TIME","return "+this.properties.formula),this._func_code=this.properties.formula);var d=this._func(a,b,this.graph.globaltime);this.boxcolor=null}catch(A){this.boxcolor="red"}this.setOutputData(0,d)}};f.prototype.getTitle=function(){return this._func_code|| -"Formula"};f.prototype.onDrawBackground=function(){var a=this.properties.formula;this.outputs&&this.outputs.length&&(this.outputs[0].label=a)};v.registerNodeType("math/formula",f);g.title="Vec2->XY";g.desc="vector 2 to components";g.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&(this.setOutputData(0,a[0]),this.setOutputData(1,a[1]))};v.registerNodeType("math3d/vec2-to-xy",g);q.title="XY->Vec2";q.desc="components to vector2";q.prototype.onExecute=function(){var a=this.getInputData(0); -null==a&&(a=this.properties.x);var b=this.getInputData(1);null==b&&(b=this.properties.y);var d=this._data;d[0]=a;d[1]=b;this.setOutputData(0,d)};v.registerNodeType("math3d/xy-to-vec2",q);z.title="Vec3->XYZ";z.desc="vector 3 to components";z.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]))};v.registerNodeType("math3d/vec3-to-xyz",z);P.title="XYZ->Vec3";P.desc="components to vector3";P.prototype.onExecute= -function(){var a=this.getInputData(0);null==a&&(a=this.properties.x);var b=this.getInputData(1);null==b&&(b=this.properties.y);var d=this.getInputData(2);null==d&&(d=this.properties.z);var c=this._data;c[0]=a;c[1]=b;c[2]=d;this.setOutputData(0,c)};v.registerNodeType("math3d/xyz-to-vec3",P);I.title="Vec4->XYZW";I.desc="vector 4 to components";I.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&(this.setOutputData(0,a[0]),this.setOutputData(1,a[1]),this.setOutputData(2,a[2]),this.setOutputData(3, -a[3]))};v.registerNodeType("math3d/vec4-to-xyzw",I);t.title="XYZW->Vec4";t.desc="components to vector4";t.prototype.onExecute=function(){var a=this.getInputData(0);null==a&&(a=this.properties.x);var b=this.getInputData(1);null==b&&(b=this.properties.y);var d=this.getInputData(2);null==d&&(d=this.properties.z);var c=this.getInputData(3);null==c&&(c=this.properties.w);var f=this._data;f[0]=a;f[1]=b;f[2]=d;f[3]=c;this.setOutputData(0,f)};v.registerNodeType("math3d/xyzw-to-vec4",t)})(this); -(function(y){function c(){this.addInput("T","vec3");this.addInput("R","vec3");this.addInput("S","vec3");this.addOutput("mat4","mat4");this.properties={T:[0,0,0],R:[0,0,0],S:[1,1,1],R_in_degrees:!0};this._result=mat4.create();this._must_update=!0}function k(){this.addInput("A","number,vec3");this.addInput("B","number,vec3");this.addOutput("=","number,vec3");this.addProperty("OP","+","enum",{values:k.values});this._result=vec3.create()}function n(){this.addInput("in","vec3");this.addInput("f","number"); -this.addOutput("out","vec3");this.properties={f:1};this._data=new Float32Array(3)}function r(){this.addInput("in","vec3");this.addOutput("out","number")}function u(){this.addInput("in","vec3");this.addOutput("out","vec3");this._data=new Float32Array(3)}function h(){this.addInput("A","vec3");this.addInput("B","vec3");this.addInput("f","vec3");this.addOutput("out","vec3");this.properties={f:.5};this._data=new Float32Array(3)}function E(){this.addInput("A","vec3");this.addInput("B","vec3");this.addOutput("out", -"number")}var D=y.LiteGraph;c.title="mat4";c.temp_quat=new Float32Array([0,0,0,1]);c.temp_mat4=new Float32Array(16);c.temp_vec3=new Float32Array(3);c.prototype.onPropertyChanged=function(c,e){this._must_update=!0};c.prototype.onExecute=function(){var e=this._result,l=c.temp_quat,m=c.temp_mat4,h=c.temp_vec3,k=this.getInputData(0),a=this.getInputData(1),b=this.getInputData(2);if(this._must_update||k||a||b)k=k||this.properties.T,a=a||this.properties.R,b=b||this.properties.S,mat4.identity(e),mat4.translate(e, -e,k),this.properties.R_in_degrees?(h.set(a),vec3.scale(h,h,DEG2RAD),quat.fromEuler(l,h)):quat.fromEuler(l,a),mat4.fromQuat(m,l),mat4.multiply(e,e,m),mat4.scale(e,e,b);this.setOutputData(0,e)};D.registerNodeType("math3d/mat4",c);k.values="+ - * / % ^ max min dot cross".split(" ");D.registerSearchboxExtra("math3d/operation","CROSS()",{properties:{OP:"cross"},title:"CROSS()"});D.registerSearchboxExtra("math3d/operation","DOT()",{properties:{OP:"dot"},title:"DOT()"});k.title="Operation";k.desc="Easy math 3D operators"; -k["@OP"]={type:"enum",title:"operation",values:k.values};k.size=[100,60];k.prototype.getTitle=function(){return"max"==this.properties.OP||"min"==this.properties.OP?this.properties.OP+"(A,B)":"A "+this.properties.OP+" B"};k.prototype.onExecute=function(){var c=this.getInputData(0),e=this.getInputData(1);if(null!=c&&null!=e){c.constructor===Number&&(c=[c,c,c]);e.constructor===Number&&(e=[e,e,e]);var m=this._result;switch(this.properties.OP){case "+":m=vec3.add(m,c,e);break;case "-":m=vec3.sub(m,c,e); -break;case "x":case "X":case "*":m=vec3.mul(m,c,e);break;case "/":m=vec3.div(m,c,e);break;case "%":m[0]=c[0]%e[0];m[1]=c[1]%e[1];m[2]=c[2]%e[2];break;case "^":m[0]=Math.pow(c[0],e[0]);m[1]=Math.pow(c[1],e[1]);m[2]=Math.pow(c[2],e[2]);break;case "max":m[0]=Math.max(c[0],e[0]);m[1]=Math.max(c[1],e[1]);m[2]=Math.max(c[2],e[2]);break;case "min":m[0]=Math.min(c[0],e[0]),m[1]=Math.min(c[1],e[1]),m[2]=Math.min(c[2],e[2]);case "dot":m=vec3.dot(c,e);break;case "cross":vec3.cross(m,c,e);break;default:console.warn("Unknown operation: "+ -this.properties.OP)}this.setOutputData(0,m)}};k.prototype.onDrawBackground=function(c){this.flags.collapsed||(c.font="40px Arial",c.fillStyle="#666",c.textAlign="center",c.fillText(this.properties.OP,.5*this.size[0],.5*(this.size[1]+D.NODE_TITLE_HEIGHT)),c.textAlign="left")};D.registerNodeType("math3d/operation",k);n.title="vec3_scale";n.desc="scales the components of a vec3";n.prototype.onExecute=function(){var c=this.getInputData(0);if(null!=c){var e=this.getInputData(1);null==e&&(e=this.properties.f); -var m=this._data;m[0]=c[0]*e;m[1]=c[1]*e;m[2]=c[2]*e;this.setOutputData(0,m)}};D.registerNodeType("math3d/vec3-scale",n);r.title="vec3_length";r.desc="returns the module of a vector";r.prototype.onExecute=function(){var c=this.getInputData(0);null!=c&&this.setOutputData(0,Math.sqrt(c[0]*c[0]+c[1]*c[1]+c[2]*c[2]))};D.registerNodeType("math3d/vec3-length",r);u.title="vec3_normalize";u.desc="returns the vector normalized";u.prototype.onExecute=function(){var c=this.getInputData(0);if(null!=c){var e= -Math.sqrt(c[0]*c[0]+c[1]*c[1]+c[2]*c[2]),m=this._data;m[0]=c[0]/e;m[1]=c[1]/e;m[2]=c[2]/e;this.setOutputData(0,m)}};D.registerNodeType("math3d/vec3-normalize",u);h.title="vec3_lerp";h.desc="returns the interpolated vector";h.prototype.onExecute=function(){var c=this.getInputData(0);if(null!=c){var e=this.getInputData(1);if(null!=e){var m=this.getInputOrProperty("f"),h=this._data;h[0]=c[0]*(1-m)+e[0]*m;h[1]=c[1]*(1-m)+e[1]*m;h[2]=c[2]*(1-m)+e[2]*m;this.setOutputData(0,h)}}};D.registerNodeType("math3d/vec3-lerp", -h);E.title="vec3_dot";E.desc="returns the dot product";E.prototype.onExecute=function(){var c=this.getInputData(0);if(null!=c){var e=this.getInputData(1);null!=e&&this.setOutputData(0,c[0]*e[0]+c[1]*e[1]+c[2]*e[2])}};D.registerNodeType("math3d/vec3-dot",E);if(y.glMatrix){y=function(){this.addInput("vec3","vec3");this.addOutput("remap","vec3");this.addOutput("clamped","vec3");this.properties={clamp:!0,range_min:[-1,-1,0],range_max:[1,1,0],target_min:[-1,-1,0],target_max:[1,1,0]};this._value=vec3.create(); -this._clamped=vec3.create()};var J=function(){this.addInputs([["A","quat"],["B","quat"],["factor","number"]]);this.addOutput("slerp","quat");this.addProperty("factor",.5);this._value=quat.create()},L=function(){this.addInputs([["A","quat"],["B","quat"]]);this.addOutput("A*B","quat");this._value=quat.create()},H=function(){this.addInputs([["vec3","vec3"],["quat","quat"]]);this.addOutput("result","vec3");this.properties={vec:[0,0,1]}},F=function(){this.addInput(["quat","quat"]);this.addOutput("euler", -"vec3");this._value=vec3.create()},e=function(){this.addInput("euler","vec3");this.addOutput("quat","quat");this.properties={euler:[0,0,0],use_yaw_pitch_roll:!1};this._degs=vec3.create();this._value=quat.create()},K=function(){this.addInputs([["degrees","number"],["axis","vec3"]]);this.addOutput("quat","quat");this.properties={angle:90,axis:vec3.fromValues(0,1,0)};this._value=quat.create()},B=function(){this.addOutput("quat","quat");this.properties={x:0,y:0,z:0,w:1,normalize:!1};this._value=quat.create()}; -B.title="Quaternion";B.desc="quaternion";B.prototype.onExecute=function(){this._value[0]=this.getInputOrProperty("x");this._value[1]=this.getInputOrProperty("y");this._value[2]=this.getInputOrProperty("z");this._value[3]=this.getInputOrProperty("w");this.properties.normalize&&quat.normalize(this._value,this._value);this.setOutputData(0,this._value)};B.prototype.onGetInputs=function(){return[["x","number"],["y","number"],["z","number"],["w","number"]]};D.registerNodeType("math3d/quaternion",B);K.title= -"Rotation";K.desc="quaternion rotation";K.prototype.onExecute=function(){var c=this.getInputData(0);null==c&&(c=this.properties.angle);var e=this.getInputData(1);null==e&&(e=this.properties.axis);c=quat.setAxisAngle(this._value,e,.0174532925*c);this.setOutputData(0,c)};D.registerNodeType("math3d/rotation",K);e.title="Euler->Quat";e.desc="Converts euler angles (in degrees) to quaternion";e.prototype.onExecute=function(){var c=this.getInputData(0);null==c&&(c=this.properties.euler);vec3.scale(this._degs, -c,DEG2RAD);this.properties.use_yaw_pitch_roll&&(this._degs=[this._degs[2],this._degs[0],this._degs[1]]);c=quat.fromEuler(this._value,this._degs);this.setOutputData(0,c)};D.registerNodeType("math3d/euler_to_quat",e);F.title="Euler->Quat";F.desc="Converts rotX,rotY,rotZ in degrees to quat";F.prototype.onExecute=function(){var c=this.getInputData(0);c&&(quat.toEuler(this._value,c),vec3.scale(this._value,this._value,DEG2RAD),this.setOutputData(0,this._value))};D.registerNodeType("math3d/quat_to_euler", -F);H.title="Rot. Vec3";H.desc="rotate a point";H.prototype.onExecute=function(){var c=this.getInputData(0);null==c&&(c=this.properties.vec);var e=this.getInputData(1);null==e?this.setOutputData(c):this.setOutputData(0,vec3.transformQuat(vec3.create(),c,e))};D.registerNodeType("math3d/rotate_vec3",H);L.title="Mult. Quat";L.desc="rotate quaternion";L.prototype.onExecute=function(){var c=this.getInputData(0);if(null!=c){var e=this.getInputData(1);null!=e&&(c=quat.multiply(this._value,c,e),this.setOutputData(0, -c))}};D.registerNodeType("math3d/mult-quat",L);J.title="Quat Slerp";J.desc="quaternion spherical interpolation";J.prototype.onExecute=function(){var c=this.getInputData(0);if(null!=c){var e=this.getInputData(1);if(null!=e){var m=this.properties.factor;null!=this.getInputData(2)&&(m=this.getInputData(2));c=quat.slerp(this._value,c,e,m);this.setOutputData(0,c)}}};D.registerNodeType("math3d/quat-slerp",J);y.title="Remap Range";y.desc="remap a 3D range";y.prototype.onExecute=function(){var c=this.getInputData(0); -c&&this._value.set(c);c=this.properties.range_min;for(var e=this.properties.range_max,m=this.properties.target_min,h=this.properties.target_max,k=0;3>k;++k){var a=e[k]-c[k];this._clamped[k]=clamp(this._value[k],c[k],e[k]);0==a?this._value[k]=.5*(m[k]+h[k]):(a=(this._value[k]-c[k])/a,this.properties.clamp&&(a=clamp(a,0,1)),this._value[k]=m[k]+a*(h[k]-m[k]))}this.setOutputData(0,this._value);this.setOutputData(1,this._clamped)};D.registerNodeType("math3d/remap_range",y)}else D.debug&&console.warn("No glmatrix found, some Math3D nodes may not work")})(this); -(function(y){function c(){this.addInput("","string");this.addOutput("table","table");this.addOutput("rows","number");this.addProperty("value","");this.addProperty("separator",",");this._table=null}y=y.LiteGraph;y.wrapFunctionAsNode("string/toString",function(c){if(c&&c.constructor===Object)try{return JSON.stringify(c)}catch(n){}return String(c)},[""],"string");y.wrapFunctionAsNode("string/compare",function(c,n){return c==n},["string","string"],"boolean");y.wrapFunctionAsNode("string/concatenate", -function(c,n){return void 0===c?n:void 0===n?c:c+n},["string","string"],"string");y.wrapFunctionAsNode("string/contains",function(c,n){return void 0===c||void 0===n?!1:-1!=c.indexOf(n)},["string","string"],"boolean");y.wrapFunctionAsNode("string/toUpperCase",function(c){return null!=c&&c.constructor===String?c.toUpperCase():c},["string"],"string");y.wrapFunctionAsNode("string/split",function(c,n){null==n&&(n=this.properties.separator);if(null==c)return[];if(c.constructor===String)return c.split(n|| -" ");if(c.constructor===Array){for(var k=[],u=0;ue;++e){var h=this.getInputData(e);if(null!=h){var k=this.values[e];k.push(h);k.length>c[0]&&k.shift()}}}};c.prototype.onDrawBackground=function(e){if(!this.flags.collapsed){var h= -this.size,k=.5*h[1]/this.properties.scale,n=c.colors,l=.5*h[1];e.fillStyle="#000";e.fillRect(0,0,h[0],h[1]);e.strokeStyle="#555";e.beginPath();e.moveTo(0,l);e.lineTo(h[0],l);e.stroke();if(this.inputs)for(var m=0;4>m;++m){var w=this.values[m];if(this.inputs[m]&&this.inputs[m].link){e.strokeStyle=n[m];e.beginPath();var r=w[0]*k*-1+l;e.moveTo(0,clamp(r,0,h[1]));for(var a=1;ah&&(h=0);if(0!=c.length){var k=[0,0,0];if(0==h)k=c[0];else if(1==h)k=c[c.length-1];else{var n= -(c.length-1)*h;h=c[Math.floor(n)];c=c[Math.floor(n)+1];n-=Math.floor(n);k[0]=h[0]*(1-n)+c[0]*n;k[1]=h[1]*(1-n)+c[1]*n;k[2]=h[2]*(1-n)+c[2]*n}for(h=0;h=c&&(this._video.currentTime=c*this._video.duration,this._video.pause()); -this._video.dirty=!0;this.setOutputData(0,this._video);this.setOutputData(1,this._video.currentTime);this.setOutputData(2,this._video.duration);this.setDirtyCanvas(!0)}};L.prototype.onStart=function(){this.play()};L.prototype.onStop=function(){this.stop()};L.prototype.loadVideo=function(c){this._video_url=c;var e=c.substr(0,10).indexOf(":"),h="";-1!=e&&(h=c.substr(0,e));e="";h&&(e=c.substr(0,c.indexOf("/",h.length+3)),e=e.substr(h.length+3));this.properties.use_proxy&&h&&F.proxy&&e!=location.host&& -(c=F.proxy+c.substr(c.indexOf(":")+3));this._video=document.createElement("video");this._video.src=c;this._video.type="type=video/mp4";this._video.muted=!0;this._video.autoplay=!0;var k=this;this._video.addEventListener("loadedmetadata",function(c){console.log("Duration: "+this.duration+" seconds");console.log("Size: "+this.videoWidth+","+this.videoHeight);k.setDirtyCanvas(!0);this.width=this.videoWidth;this.height=this.videoHeight});this._video.addEventListener("progress",function(c){console.log("video loading...")}); -this._video.addEventListener("error",function(c){console.error("Error loading video: "+this.src);if(this.error)switch(this.error.code){case this.error.MEDIA_ERR_ABORTED:console.error("You stopped the video.");break;case this.error.MEDIA_ERR_NETWORK:console.error("Network error - please try again later.");break;case this.error.MEDIA_ERR_DECODE:console.error("Video is broken..");break;case this.error.MEDIA_ERR_SRC_NOT_SUPPORTED:console.error("Sorry, your browser can't play this video.")}});this._video.addEventListener("ended", -function(c){console.log("Video Ended.");this.play()})};L.prototype.onPropertyChanged=function(c,h){this.properties[c]=h;"url"==c&&""!=h&&this.loadVideo(h);return!0};L.prototype.play=function(){this._video&&this._video.videoWidth&&this._video.play()};L.prototype.playPause=function(){this._video&&(this._video.paused?this.play():this.pause())};L.prototype.stop=function(){this._video&&(this._video.pause(),this._video.currentTime=0)};L.prototype.pause=function(){this._video&&(console.log("Video paused"), -this._video.pause())};L.prototype.onWidget=function(c,h){};F.registerNodeType("graphics/video",L);H.title="Webcam";H.desc="Webcam image";H.is_webcam_open=!1;H.prototype.openStream=function(){if(navigator.mediaDevices.getUserMedia){this._waiting_confirmation=!0;navigator.mediaDevices.getUserMedia({audio:!1,video:this.properties.filterFacingMode?{facingMode:this.properties.facingMode}:!0}).then(this.streamReady.bind(this)).catch(function(e){console.log("Webcam rejected",e);c._webcam_stream=!1;H.is_webcam_open= -!1;c.boxcolor="red";c.trigger("stream_error")});var c=this}else console.log("getUserMedia() is not supported in your browser, use chrome and enable WebRTC from about://flags")};H.prototype.closeStream=function(){if(this._webcam_stream){var c=this._webcam_stream.getTracks();if(c.length)for(var h=0;h=this.size[1]||!this.properties.show||!this._video||(c.save(),c.drawImage(this._video,0,0,this.size[0],this.size[1]),c.restore())};H.prototype.onGetOutputs=function(){return[["width","number"],["height","number"],["stream_ready",F.EVENT],["stream_closed",F.EVENT],["stream_error",F.EVENT]]};F.registerNodeType("graphics/webcam",H)})(this); -(function(y){function c(){this.addOutput("tex","Texture");this.addOutput("name","string");this.properties={name:"",filter:!0};this.size=[c.image_preview_size,c.image_preview_size]}function k(){this.addInput("Texture","Texture");this.properties={flipY:!1};this.size=[c.image_preview_size,c.image_preview_size]}function n(){this.addInput("Texture","Texture");this.addOutput("tex","Texture");this.addOutput("name","string");this.properties={name:"",generate_mipmaps:!1}}function r(){this.addInput("Texture", -"Texture");this.addInput("TextureB","Texture");this.addInput("value","number");this.addOutput("Texture","Texture");this.help="

pixelcode must be vec3, uvcode must be vec2, is optional

\t\t

uv: tex. coords

color: texture colorB: textureB

time: scene time value: input value

For multiline you must type: result = ...

";this.properties={value:1,pixelcode:"color + colorB * value",uvcode:"",precision:c.DEFAULT}; -this.has_error=!1}function u(){this.addOutput("out","Texture");this.properties={code:"",u_value:1,u_color:[1,1,1,1],width:512,height:512,precision:c.DEFAULT};this.properties.code=u.pixel_shader;this._uniforms={u_value:1,u_color:vec4.create(),in_texture:0,texSize:vec4.create(),time:0}}function h(){this.addInput("in","Texture");this.addInput("scale","vec2");this.addInput("offset","vec2");this.addOutput("out","Texture");this.properties={offset:vec2.fromValues(0,0),scale:vec2.fromValues(1,1),precision:c.DEFAULT}} -function E(){this.addInput("in","Texture");this.addInput("warp","Texture");this.addInput("factor","number");this.addOutput("out","Texture");this.properties={factor:.01,scale:[1,1],offset:[0,0],precision:c.DEFAULT};this._uniforms={u_texture:0,u_textureB:1,u_factor:1,u_scale:vec2.create(),u_offset:vec2.create()}}function D(){this.addInput("Texture","Texture");this.properties={additive:!1,antialiasing:!1,filter:!0,disable_alpha:!1,gamma:1,viewport:[0,0,1,1]};this.size[0]=130}function J(){this.addInput("Texture", -"Texture");this.addOutput("","Texture");this.properties={size:0,generate_mipmaps:!1,precision:c.DEFAULT}}function L(){this.addInput("Texture","Texture");this.addOutput("","Texture");this.properties={iterations:1,generate_mipmaps:!1,precision:c.DEFAULT}}function H(){this.addInput("Texture","Texture");this.addOutput("","Texture");this.properties={size:[512,512],generate_mipmaps:!1,precision:c.DEFAULT}}function F(){this.addInput("Texture","Texture");this.addOutput("tex","Texture");this.addOutput("avg", -"vec4");this.addOutput("lum","number");this.properties={use_previous_frame:!0,high_quality:!1};this._uniforms={u_texture:0,u_mipmap_offset:0};this._luminance=new Float32Array(4)}function e(){this.addInput("in","Texture");this.addInput("factor","Number");this.addOutput("out","Texture");this.properties={factor:.5};this._uniforms={u_texture:0,u_textureB:1,u_factor:this.properties.factor}}function K(){this.addInput("in","Texture");this.addOutput("avg","Texture");this.addOutput("array","Texture");this.properties= -{samples:64,frames_interval:1};this._uniforms={u_texture:0,u_textureB:1,u_samples:this.properties.samples,u_isamples:1/this.properties.samples};this.frame=0}function B(){this.addInput("Image","image");this.addOutput("","Texture");this.properties={}}function M(){this.addInput("Texture","Texture");this.addInput("LUT","Texture");this.addInput("Intensity","number");this.addOutput("","Texture");this.properties={enabled:!0,intensity:1,precision:c.DEFAULT,texture:null};M._shader||(M._shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER, -M.pixel_shader))}function l(){this.addInput("Texture","Texture");this.addInput("Atlas","Texture");this.addOutput("","Texture");this.properties={enabled:!0,num_row_symbols:4,symbol_size:16,brightness:1,colorize:!1,filter:!1,invert:!1,precision:c.DEFAULT,generate_mipmaps:!1,texture:null};l._shader||(l._shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,l.pixel_shader));this._uniforms={u_texture:0,u_textureB:1,u_row_simbols:4,u_simbol_size:16,u_res:vec2.create()}}function m(){this.addInput("Texture","Texture"); -this.addOutput("R","Texture");this.addOutput("G","Texture");this.addOutput("B","Texture");this.addOutput("A","Texture");m._shader||(m._shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,m.pixel_shader))}function w(){this.addInput("R","Texture");this.addInput("G","Texture");this.addInput("B","Texture");this.addInput("A","Texture");this.addOutput("Texture","Texture");this.properties={precision:c.DEFAULT,R:1,G:1,B:1,A:1};this._color=vec4.create();this._uniforms={u_textureR:0,u_textureG:1,u_textureB:2, -u_textureA:3,u_color:this._color}}function N(){this.addOutput("Texture","Texture");this._tex_color=vec4.create();this.properties={color:vec4.create(),precision:c.DEFAULT}}function a(){this.addInput("A","color");this.addInput("B","color");this.addOutput("Texture","Texture");this.properties={angle:0,scale:1,A:[0,0,0],B:[1,1,1],texture_size:32};a._shader||(a._shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,a.pixel_shader));this._uniforms={u_angle:0,u_colorA:vec3.create(),u_colorB:vec3.create()}}function b(){this.addInput("A", -"Texture");this.addInput("B","Texture");this.addInput("Mixer","Texture");this.addOutput("Texture","Texture");this.properties={factor:.5,size_from_biggest:!0,invert:!1,precision:c.DEFAULT};this._uniforms={u_textureA:0,u_textureB:1,u_textureMix:2,u_mix:vec4.create()}}function d(){this.addInput("Tex.","Texture");this.addOutput("Edges","Texture");this.properties={invert:!0,threshold:!1,factor:1,precision:c.DEFAULT};d._shader||(d._shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,d.pixel_shader))}function f(){this.addInput("Texture", -"Texture");this.addInput("Distance","number");this.addInput("Range","number");this.addOutput("Texture","Texture");this.properties={distance:100,range:50,only_depth:!1,high_precision:!1};this._uniforms={u_texture:0,u_distance:100,u_range:50,u_camera_planes:null}}function g(){this.addInput("Texture","Texture");this.addOutput("Texture","Texture");this.properties={precision:c.DEFAULT,invert:!1};this._uniforms={u_texture:0,u_camera_planes:null,u_ires:vec2.create()}}function q(){this.addInput("Texture", -"Texture");this.addInput("Iterations","number");this.addInput("Intensity","number");this.addOutput("Blurred","Texture");this.properties={intensity:1,iterations:1,preserve_aspect:!1,scale:[1,1],precision:c.DEFAULT}}function z(){this.intensity=.5;this.persistence=.6;this.iterations=8;this.threshold=.8;this.scale=1;this.dirt_texture=null;this.dirt_factor=.5;this._textures=[];this._uniforms={u_intensity:1,u_texture:0,u_glow_texture:1,u_threshold:0,u_texel_size:vec2.create()}}function P(){this.addInput("in", -"Texture");this.addInput("dirt","Texture");this.addOutput("out","Texture");this.addOutput("glow","Texture");this.properties={enabled:!0,intensity:1,persistence:.99,iterations:16,threshold:0,scale:1,dirt_factor:.5,precision:c.DEFAULT};this.fx=new z}function I(){this.addInput("Texture","Texture");this.addOutput("Filtered","Texture");this.properties={intensity:1,radius:5}}function t(){this.addInput("Texture","Texture");this.addOutput("Filtered","Texture");this.properties={sigma:1.4,k:1.6,p:21.7,epsilon:79, -phi:.017}}function v(){this.addOutput("Webcam","Texture");this.properties={texture_name:"",facingMode:"user"};this.boxcolor="black";this.version=0}function C(){this.addInput("in","Texture");this.addInput("f","number");this.addOutput("out","Texture");this.properties={enabled:!0,factor:1,precision:c.LOW};this._uniforms={u_texture:0,u_factor:1}}function x(){this.addInput("in","");this.properties={precision:c.LOW,width:0,height:0,channels:1};this.addOutput("out","Texture")}function G(){this.addInput("in", -"Texture");this.addOutput("out","Texture");this.properties={precision:c.LOW,split_channels:!1};this._values=new Uint8Array(1024);this._values.fill(255);this._curve_texture=null;this._uniforms={u_texture:0,u_curve:1,u_range:1};this._must_update=!0;this._points={RGB:[[0,0],[1,1]],R:[[0,0],[1,1]],G:[[0,0],[1,1]],B:[[0,0],[1,1]]};this.curve_editor=null;this.addWidget("toggle","Split Channels",!1,"split_channels");this.addWidget("combo","Channel","RGB",{values:["RGB","R","G","B"]});this.curve_offset=68; -this.size=[240,160]}function A(){this.addInput("in","Texture");this.addInput("exp","number");this.addOutput("out","Texture");this.properties={exposition:1,precision:c.LOW};this._uniforms={u_texture:0,u_exposition:1}}function R(){this.addInput("in","Texture");this.addInput("avg","number,Texture");this.addOutput("out","Texture");this.properties={enabled:!0,scale:1,gamma:1,average_lum:1,lum_white:1,precision:c.LOW};this._uniforms={u_texture:0,u_lumwhite2:1,u_igamma:1,u_scale:1,u_average_lum:1}}function T(){this.addOutput("out", -"Texture");this.properties={width:512,height:512,seed:0,persistence:.1,octaves:8,scale:1,offset:[0,0],amplitude:1,precision:c.DEFAULT};this._key=0;this._texture=null;this._uniforms={u_persistence:.1,u_seed:0,u_offset:vec2.create(),u_scale:1,u_viewport:vec2.create()}}function S(){this.addInput("v");this.addOutput("out","Texture");this.properties={code:S.default_code,width:512,height:512,clear:!0,precision:c.DEFAULT,use_html_canvas:!1};this._temp_texture=this._func=null;this.compileCode()}function U(){this.addInput("in", -"Texture");this.addOutput("out","Texture");this.properties={key_color:vec3.fromValues(0,1,0),threshold:.8,slope:.2,precision:c.DEFAULT}}function V(){this.addInput("in","texture");this.addInput("yaw","number");this.addOutput("out","texture");this.properties={yaw:0}}var O=y.LiteGraph,aa=y.LGraphCanvas;y.LGraphTexture=null;"undefined"!=typeof GL&&(aa.link_type_colors.Texture="#987",y.LGraphTexture=c,c.title="Texture",c.desc="Texture",c.widgets_info={name:{widget:"texture"},filter:{widget:"checkbox"}}, -c.loadTextureCallback=null,c.image_preview_size=256,c.UNDEFINED=0,c.PASS_THROUGH=1,c.COPY=2,c.LOW=3,c.HIGH=4,c.REUSE=5,c.DEFAULT=2,c.MODE_VALUES={undefined:c.UNDEFINED,"pass through":c.PASS_THROUGH,copy:c.COPY,low:c.LOW,high:c.HIGH,reuse:c.REUSE,default:c.DEFAULT},c.getTexturesContainer=function(){return gl.textures},c.loadTexture=function(a,b){b=b||{};var d=a;"http://"==d.substr(0,7)&&O.proxy&&(d=O.proxy+d.substr(7));return c.getTexturesContainer()[a]=GL.Texture.fromURL(d,b)},c.getTexture=function(a){var b= -this.getTexturesContainer();if(!b)throw"Cannot load texture, container of textures not found";b=b[a];return!b&&a&&":"!=a[0]?this.loadTexture(a):b},c.getTargetTexture=function(a,b,d){if(!a)throw"LGraphTexture.getTargetTexture expects a reference texture";switch(d){case c.LOW:d=gl.UNSIGNED_BYTE;break;case c.HIGH:d=gl.HIGH_PRECISION_FORMAT;break;case c.REUSE:return a;default:d=a?a.type:gl.UNSIGNED_BYTE}b&&b.width==a.width&&b.height==a.height&&b.type==d&&b.format==a.format||(b=new GL.Texture(a.width, -a.height,{type:d,format:a.format,filter:gl.LINEAR}));return b},c.getTextureType=function(a,b){b=b?b.type:gl.UNSIGNED_BYTE;switch(a){case c.HIGH:b=gl.HIGH_PRECISION_FORMAT;break;case c.LOW:b=gl.UNSIGNED_BYTE}return b},c.getWhiteTexture=function(){return this._white_texture?this._white_texture:this._white_texture=GL.Texture.fromMemory(1,1,[255,255,255,255],{format:gl.RGBA,wrap:gl.REPEAT,filter:gl.NEAREST})},c.getNoiseTexture=function(){if(this._noise_texture)return this._noise_texture;for(var a=new Uint8Array(1048576), -b=0;1048576>b;++b)a[b]=255*Math.random();return this._noise_texture=a=GL.Texture.fromMemory(512,512,a,{format:gl.RGBA,wrap:gl.REPEAT,filter:gl.NEAREST})},c.prototype.onDropFile=function(a,b,c){a?("string"==typeof a?a=GL.Texture.fromURL(a):-1!=b.toLowerCase().indexOf(".dds")?a=GL.Texture.fromDDSInMemory(a):(a=new Blob([c]),a=URL.createObjectURL(a),a=GL.Texture.fromURL(a)),this._drop_texture=a,this.properties.name=b):(this._drop_texture=null,this.properties.name="")},c.prototype.getExtraMenuOptions= -function(a){var b=this;if(this._drop_texture)return[{content:"Clear",callback:function(){b._drop_texture=null;b.properties.name=""}}]},c.prototype.onExecute=function(){var a=null;this.isOutputConnected(1)&&(a=this.getInputData(0));!a&&this._drop_texture&&(a=this._drop_texture);!a&&this.properties.name&&(a=c.getTexture(this.properties.name));if(a){this._last_tex=a;!1===this.properties.filter?a.setParameter(gl.TEXTURE_MAG_FILTER,gl.NEAREST):a.setParameter(gl.TEXTURE_MAG_FILTER,gl.LINEAR);this.setOutputData(0, -a);this.setOutputData(1,a.fullpath||a.filename);for(var b=2;b=this.size[1]))if(this._drop_texture&& -a.webgl)a.drawImage(this._drop_texture,0,0,this.size[0],this.size[1]);else{if(this._last_preview_tex!=this._last_tex)if(a.webgl)this._canvas=this._last_tex;else{var b=c.generateLowResTexturePreview(this._last_tex);if(!b)return;this._last_preview_tex=this._last_tex;this._canvas=cloneCanvas(b)}this._canvas&&(a.save(),a.webgl||(a.translate(0,this.size[1]),a.scale(1,-1)),a.drawImage(this._canvas,0,0,this.size[0],this.size[1]),a.restore())}},c.generateLowResTexturePreview=function(a){if(!a)return null; -var b=c.image_preview_size,d=a;if(a.format==gl.DEPTH_COMPONENT)return null;if(a.width>b||a.height>b)d=this._preview_temp_tex,this._preview_temp_tex||(this._preview_temp_tex=d=new GL.Texture(b,b,{minFilter:gl.NEAREST})),a.copyTo(d);a=this._preview_canvas;a||(this._preview_canvas=a=createCanvas(b,b));d&&d.toCanvas(a);return a},c.prototype.getResources=function(a){this.properties.name&&(a[this.properties.name]=GL.Texture);return a},c.prototype.onGetInputs=function(){return[["in","Texture"]]},c.prototype.onGetOutputs= -function(){return[["width","number"],["height","number"],["aspect","number"]]},c.replaceCode=function(a,b){return a.replace(/\{\{[a-zA-Z0-9_]*\}\}/g,function(a){a=a.replace(/[\{\}]/g,"");return b[a]||""})},O.registerNodeType("texture/texture",c),k.title="Preview",k.desc="Show a texture in the graph canvas",k.allow_preview=!1,k.prototype.onDrawBackground=function(a){if(!this.flags.collapsed&&(a.webgl||k.allow_preview)){var b=this.getInputData(0);b&&(b=!b.handle&&a.webgl?b:c.generateLowResTexturePreview(b), -a.save(),this.properties.flipY&&(a.translate(0,this.size[1]),a.scale(1,-1)),a.drawImage(b,0,0,this.size[0],this.size[1]),a.restore())}},O.registerNodeType("texture/preview",k),n.title="Save",n.desc="Save a texture in the repository",n.prototype.getPreviewTexture=function(){return this._texture},n.prototype.onExecute=function(){var a=this.getInputData(0);a&&(this.properties.generate_mipmaps&&(a.bind(0),a.setParameter(gl.TEXTURE_MIN_FILTER,gl.LINEAR_MIPMAP_LINEAR),gl.generateMipmap(a.texture_type), -a.unbind(0)),this.properties.name&&(c.storeTexture?c.storeTexture(this.properties.name,a):c.getTexturesContainer()[this.properties.name]=a),this._texture=a,this.setOutputData(0,a),this.setOutputData(1,this.properties.name))},O.registerNodeType("texture/save",n),r.widgets_info={uvcode:{widget:"code"},pixelcode:{widget:"code"},precision:{widget:"combo",values:c.MODE_VALUES}},r.title="Operation",r.desc="Texture shader operation",r.presets={},r.prototype.getExtraMenuOptions=function(a){var b=this;return[{content:b.properties.show? -"Hide Texture":"Show Texture",callback:function(){b.properties.show=!b.properties.show}}]},r.prototype.onPropertyChanged=function(){this.has_error=!1},r.prototype.onDrawBackground=function(a){this.flags.collapsed||20>=this.size[1]||!this.properties.show||!this._tex||this._tex.gl!=a||(a.save(),a.drawImage(this._tex,0,0,this.size[0],this.size[1]),a.restore())},r.prototype.onExecute=function(){var a=this.getInputData(0);if(this.isOutputConnected(0))if(this.properties.precision===c.PASS_THROUGH)this.setOutputData(0, -a);else{var b=this.getInputData(1);if(this.properties.uvcode||this.properties.pixelcode){var d=512,f=512;a?(d=a.width,f=a.height):b&&(d=b.width,f=b.height);b||(b=GL.Texture.getWhiteTexture());var g=c.getTextureType(this.properties.precision,a);this._tex=a||this._tex?c.getTargetTexture(a||this._tex,this._tex,this.properties.precision):new GL.Texture(d,f,{type:g,format:gl.RGBA,filter:gl.LINEAR});g="";this.properties.uvcode&&(g="uv = "+this.properties.uvcode,-1!=this.properties.uvcode.indexOf(";")&& -(g=this.properties.uvcode));var e="";this.properties.pixelcode&&(e="result = "+this.properties.pixelcode,-1!=this.properties.pixelcode.indexOf(";")&&(e=this.properties.pixelcode));var l=this._shader;if(!(this.has_error||l&&this._shader_code==g+"|"+e)){var h=c.replaceCode(r.pixel_shader,{UV_CODE:g,PIXEL_CODE:e});try{l=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,h),this.boxcolor="#00FF00"}catch(Z){GL.Shader.dumpErrorToConsole(Z,Shader.SCREEN_VERTEX_SHADER,h);this.boxcolor="#FF0000";this.has_error=!0; -return}this._shader=l;this._shader_code=g+"|"+e}if(this._shader){var m=this.getInputData(2);null!=m?this.properties.value=m:m=parseFloat(this.properties.value);var k=this.graph.getTime();this._tex.drawTo(function(){gl.disable(gl.DEPTH_TEST);gl.disable(gl.CULL_FACE);gl.disable(gl.BLEND);a&&a.bind(0);b&&b.bind(1);var c=Mesh.getScreenQuad();l.uniforms({u_texture:0,u_textureB:1,value:m,texSize:[d,f,1/d,1/f],time:k}).draw(c)});this.setOutputData(0,this._tex)}}}},r.pixel_shader="precision highp float;\n\t\t\n\t\tuniform sampler2D u_texture;\n\t\tuniform sampler2D u_textureB;\n\t\tvarying vec2 v_coord;\n\t\tuniform vec4 texSize;\n\t\tuniform float time;\n\t\tuniform float value;\n\t\t\n\t\tvoid main() {\n\t\t\tvec2 uv = v_coord;\n\t\t\t{{UV_CODE}};\n\t\t\tvec4 color4 = texture2D(u_texture, uv);\n\t\t\tvec3 color = color4.rgb;\n\t\t\tvec4 color4B = texture2D(u_textureB, uv);\n\t\t\tvec3 colorB = color4B.rgb;\n\t\t\tvec3 result = color;\n\t\t\tfloat alpha = 1.0;\n\t\t\t{{PIXEL_CODE}};\n\t\t\tgl_FragColor = vec4(result, alpha);\n\t\t}\n\t\t", -r.registerPreset=function(a,b){r.presets[a]=b},r.registerPreset("",""),r.registerPreset("bypass","color"),r.registerPreset("add","color + colorB * value"),r.registerPreset("substract","(color - colorB) * value"),r.registerPreset("mate","mix( color, colorB, color4B.a * value)"),r.registerPreset("invert","vec3(1.0) - color"),r.registerPreset("multiply","color * colorB * value"),r.registerPreset("divide","(color / colorB) / value"),r.registerPreset("difference","abs(color - colorB) * value"),r.registerPreset("max", -"max(color, colorB) * value"),r.registerPreset("min","min(color, colorB) * value"),r.registerPreset("displace","texture2D(u_texture, uv + (colorB.xy - vec2(0.5)) * value).xyz"),r.registerPreset("grayscale","vec3(color.x + color.y + color.z) * value / 3.0"),r.registerPreset("saturation","mix( vec3(color.x + color.y + color.z) / 3.0, color, value )"),r.registerPreset("normalmap","\n\t\tfloat z0 = texture2D(u_texture, uv + vec2(-texSize.z, -texSize.w) ).x;\n\t\tfloat z1 = texture2D(u_texture, uv + vec2(0.0, -texSize.w) ).x;\n\t\tfloat z2 = texture2D(u_texture, uv + vec2(texSize.z, -texSize.w) ).x;\n\t\tfloat z3 = texture2D(u_texture, uv + vec2(-texSize.z, 0.0) ).x;\n\t\tfloat z4 = color.x;\n\t\tfloat z5 = texture2D(u_texture, uv + vec2(texSize.z, 0.0) ).x;\n\t\tfloat z6 = texture2D(u_texture, uv + vec2(-texSize.z, texSize.w) ).x;\n\t\tfloat z7 = texture2D(u_texture, uv + vec2(0.0, texSize.w) ).x;\n\t\tfloat z8 = texture2D(u_texture, uv + vec2(texSize.z, texSize.w) ).x;\n\t\tvec3 normal = vec3( z2 + 2.0*z4 + z7 - z0 - 2.0*z3 - z5, z5 + 2.0*z6 + z7 -z0 - 2.0*z1 - z2, 1.0 );\n\t\tnormal.xy *= value;\n\t\tresult.xyz = normalize(normal) * 0.5 + vec3(0.5);\n\t"), -r.registerPreset("threshold","vec3(color.x > colorB.x * value ? 1.0 : 0.0,color.y > colorB.y * value ? 1.0 : 0.0,color.z > colorB.z * value ? 1.0 : 0.0)"),r.prototype.onInspect=function(a){var b=this;a.addCombo("Presets","",{values:Object.keys(r.presets),callback:function(d){var c=r.presets[d];c&&(b.setProperty("pixelcode",c),b.title=d,a.refresh())}})},O.registerNodeType("texture/operation",r),u.title="Shader",u.desc="Texture shader",u.widgets_info={code:{type:"code",lang:"glsl"},precision:{widget:"combo", -values:c.MODE_VALUES}},u.prototype.onPropertyChanged=function(a,b){if("code"==a&&(a=this.getShader())){b=a.uniformInfo;if(this.inputs)for(var d={},c=0;c=this.size[1])){var b=this.getInputData(0);b&&a.drawImage(a==gl?b:gl.canvas,10,30,this.size[0]-20,this.size[1]-40)}},D.prototype.onExecute=function(){var a=this.getInputData(0);if(a){this.properties.disable_alpha?gl.disable(gl.BLEND):(gl.enable(gl.BLEND),this.properties.additive?gl.blendFunc(gl.SRC_ALPHA, -gl.ONE):gl.blendFunc(gl.SRC_ALPHA,gl.ONE_MINUS_SRC_ALPHA));gl.disable(gl.DEPTH_TEST);var b=this.properties.gamma||1;this.isInputConnected(1)&&(b=this.getInputData(1));a.setParameter(gl.TEXTURE_MAG_FILTER,this.properties.filter?gl.LINEAR:gl.NEAREST);var d=D._prev_viewport;d.set(gl.viewport_data);var c=this.properties.viewport;gl.viewport(d[0]+d[2]*c[0],d[1]+d[3]*c[1],d[2]*c[2],d[3]*c[3]);gl.getViewport();this.properties.antialiasing?(D._shader||(D._shader=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER, -D.aa_pixel_shader)),c=Mesh.getScreenQuad(),a.bind(0),D._shader.uniforms({u_texture:0,uViewportSize:[a.width,a.height],u_igamma:1/b,inverseVP:[1/a.width,1/a.height]}).draw(c)):1!=b?(D._gamma_shader||(D._gamma_shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,D.gamma_pixel_shader)),a.toViewport(D._gamma_shader,{u_texture:0,u_igamma:1/b})):a.toViewport();gl.viewport(d[0],d[1],d[2],d[3])}},D.prototype.onGetInputs=function(){return[["gamma","number"]]},D.aa_pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_texture;\n\t\tuniform vec2 uViewportSize;\n\t\tuniform vec2 inverseVP;\n\t\tuniform float u_igamma;\n\t\t#define FXAA_REDUCE_MIN (1.0/ 128.0)\n\t\t#define FXAA_REDUCE_MUL (1.0 / 8.0)\n\t\t#define FXAA_SPAN_MAX 8.0\n\t\t\n\t\t/* from mitsuhiko/webgl-meincraft based on the code on geeks3d.com */\n\t\tvec4 applyFXAA(sampler2D tex, vec2 fragCoord)\n\t\t{\n\t\t\tvec4 color = vec4(0.0);\n\t\t\t/*vec2 inverseVP = vec2(1.0 / uViewportSize.x, 1.0 / uViewportSize.y);*/\n\t\t\tvec3 rgbNW = texture2D(tex, (fragCoord + vec2(-1.0, -1.0)) * inverseVP).xyz;\n\t\t\tvec3 rgbNE = texture2D(tex, (fragCoord + vec2(1.0, -1.0)) * inverseVP).xyz;\n\t\t\tvec3 rgbSW = texture2D(tex, (fragCoord + vec2(-1.0, 1.0)) * inverseVP).xyz;\n\t\t\tvec3 rgbSE = texture2D(tex, (fragCoord + vec2(1.0, 1.0)) * inverseVP).xyz;\n\t\t\tvec3 rgbM = texture2D(tex, fragCoord * inverseVP).xyz;\n\t\t\tvec3 luma = vec3(0.299, 0.587, 0.114);\n\t\t\tfloat lumaNW = dot(rgbNW, luma);\n\t\t\tfloat lumaNE = dot(rgbNE, luma);\n\t\t\tfloat lumaSW = dot(rgbSW, luma);\n\t\t\tfloat lumaSE = dot(rgbSE, luma);\n\t\t\tfloat lumaM = dot(rgbM, luma);\n\t\t\tfloat lumaMin = min(lumaM, min(min(lumaNW, lumaNE), min(lumaSW, lumaSE)));\n\t\t\tfloat lumaMax = max(lumaM, max(max(lumaNW, lumaNE), max(lumaSW, lumaSE)));\n\t\t\t\n\t\t\tvec2 dir;\n\t\t\tdir.x = -((lumaNW + lumaNE) - (lumaSW + lumaSE));\n\t\t\tdir.y = ((lumaNW + lumaSW) - (lumaNE + lumaSE));\n\t\t\t\n\t\t\tfloat dirReduce = max((lumaNW + lumaNE + lumaSW + lumaSE) * (0.25 * FXAA_REDUCE_MUL), FXAA_REDUCE_MIN);\n\t\t\t\n\t\t\tfloat rcpDirMin = 1.0 / (min(abs(dir.x), abs(dir.y)) + dirReduce);\n\t\t\tdir = min(vec2(FXAA_SPAN_MAX, FXAA_SPAN_MAX), max(vec2(-FXAA_SPAN_MAX, -FXAA_SPAN_MAX), dir * rcpDirMin)) * inverseVP;\n\t\t\t\n\t\t\tvec3 rgbA = 0.5 * (texture2D(tex, fragCoord * inverseVP + dir * (1.0 / 3.0 - 0.5)).xyz + \n\t\t\t\ttexture2D(tex, fragCoord * inverseVP + dir * (2.0 / 3.0 - 0.5)).xyz);\n\t\t\tvec3 rgbB = rgbA * 0.5 + 0.25 * (texture2D(tex, fragCoord * inverseVP + dir * -0.5).xyz + \n\t\t\t\ttexture2D(tex, fragCoord * inverseVP + dir * 0.5).xyz);\n\t\t\t\n\t\t\t//return vec4(rgbA,1.0);\n\t\t\tfloat lumaB = dot(rgbB, luma);\n\t\t\tif ((lumaB < lumaMin) || (lumaB > lumaMax))\n\t\t\t\tcolor = vec4(rgbA, 1.0);\n\t\t\telse\n\t\t\t\tcolor = vec4(rgbB, 1.0);\n\t\t\tif(u_igamma != 1.0)\n\t\t\t\tcolor.xyz = pow( color.xyz, vec3(u_igamma) );\n\t\t\treturn color;\n\t\t}\n\t\t\n\t\tvoid main() {\n\t\t gl_FragColor = applyFXAA( u_texture, v_coord * uViewportSize) ;\n\t\t}\n\t\t", -D.gamma_pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_texture;\n\t\tuniform float u_igamma;\n\t\tvoid main() {\n\t\t\tvec4 color = texture2D( u_texture, v_coord);\n\t\t\tcolor.xyz = pow(color.xyz, vec3(u_igamma) );\n\t\t gl_FragColor = color;\n\t\t}\n\t\t",O.registerNodeType("texture/toviewport",D),J.title="Copy",J.desc="Copy Texture",J.widgets_info={size:{widget:"combo",values:[0,32,64,128,256,512,1024,2048]},precision:{widget:"combo", -values:c.MODE_VALUES}},J.prototype.onExecute=function(){var a=this.getInputData(0);if((a||this._temp_texture)&&this.isOutputConnected(0)){if(a){var b=a.width,d=a.height;0!=this.properties.size&&(d=b=this.properties.size);var f=this._temp_texture,g=a.type;this.properties.precision===c.LOW?g=gl.UNSIGNED_BYTE:this.properties.precision===c.HIGH&&(g=gl.HIGH_PRECISION_FORMAT);f&&f.width==b&&f.height==d&&f.type==g||(f=gl.LINEAR,this.properties.generate_mipmaps&&isPowerOfTwo(b)&&isPowerOfTwo(d)&&(f=gl.LINEAR_MIPMAP_LINEAR), -this._temp_texture=new GL.Texture(b,d,{type:g,format:gl.RGBA,minFilter:f,magFilter:gl.LINEAR}));a.copyTo(this._temp_texture);this.properties.generate_mipmaps&&(this._temp_texture.bind(0),gl.generateMipmap(this._temp_texture.texture_type),this._temp_texture.unbind(0))}this.setOutputData(0,this._temp_texture)}},O.registerNodeType("texture/copy",J),L.title="Downsample",L.desc="Downsample Texture",L.widgets_info={iterations:{type:"number",step:1,precision:0,min:0},precision:{widget:"combo",values:c.MODE_VALUES}}, -L.prototype.onExecute=function(){var a=this.getInputData(0);if((a||this._temp_texture)&&this.isOutputConnected(0)&&a&&a.texture_type===GL.TEXTURE_2D)if(1>this.properties.iterations)this.setOutputData(0,a);else{var b=L._shader;b||(L._shader=b=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,L.pixel_shader));var d=a.width|0,f=a.height|0,g=a.type;this.properties.precision===c.LOW?g=gl.UNSIGNED_BYTE:this.properties.precision===c.HIGH&&(g=gl.HIGH_PRECISION_FORMAT);var e=this.properties.iterations||1,l=a,h= -[];g={type:g,format:a.format};var m=vec2.create(),k={u_offset:m};this._texture&&GL.Texture.releaseTemporary(this._texture);for(var w=0;w>1||0;f=f>>1||0;a=GL.Texture.getTemporary(d,f,g);h.push(a);l.setParameter(GL.TEXTURE_MAG_FILTER,GL.NEAREST);l.copyTo(a,b,k);if(1==d&&1==f)break;l=a}this._texture=h.pop();for(w=0;wc;c++)this.isOutputConnected(c)?(this._channels[c]&&this._channels[c].width==a.width&&this._channels[c].height==a.height&&this._channels[c].type==a.type&&this._channels[c].format==b||(this._channels[c]=new GL.Texture(a.width,a.height,{type:a.type,format:b,filter:gl.LINEAR})), -d++):this._channels[c]=null;if(d){gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);var f=Mesh.getScreenQuad(),g=m._shader,e=[[1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1]];for(c=0;4>c;c++)this._channels[c]&&(this._channels[c].drawTo(function(){a.bind(0);g.uniforms({u_texture:0,u_mask:e[c]}).draw(f)}),this.setOutputData(c,this._channels[c]))}}},m.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_texture;\n\t\tuniform vec4 u_mask;\n\t\t\n\t\tvoid main() {\n\t\t gl_FragColor = vec4( vec3( length( texture2D(u_texture, v_coord) * u_mask )), 1.0 );\n\t\t}\n\t\t", -O.registerNodeType("texture/textureChannels",m),w.title="Channels to Texture",w.desc="Split texture channels",w.widgets_info={precision:{widget:"combo",values:c.MODE_VALUES}},w.prototype.onExecute=function(){var a=c.getWhiteTexture(),b=this.getInputData(0)||a,d=this.getInputData(1)||a,f=this.getInputData(2)||a,g=this.getInputData(3)||a;gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);var e=Mesh.getScreenQuad();w._shader||(w._shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,w.pixel_shader));var l=w._shader; -a=Math.max(b.width,d.width,f.width,g.width);var h=Math.max(b.height,d.height,f.height,g.height),m=this.properties.precision==c.HIGH?c.HIGH_PRECISION_FORMAT:gl.UNSIGNED_BYTE;this._texture&&this._texture.width==a&&this._texture.height==h&&this._texture.type==m||(this._texture=new GL.Texture(a,h,{type:m,format:gl.RGBA,filter:gl.LINEAR}));a=this._color;a[0]=this.properties.R;a[1]=this.properties.G;a[2]=this.properties.B;a[3]=this.properties.A;var k=this._uniforms;this._texture.drawTo(function(){b.bind(0); -d.bind(1);f.bind(2);g.bind(3);l.uniforms(k).draw(e)});this.setOutputData(0,this._texture)},w.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_textureR;\n\t\tuniform sampler2D u_textureG;\n\t\tuniform sampler2D u_textureB;\n\t\tuniform sampler2D u_textureA;\n\t\tuniform vec4 u_color;\n\t\t\n\t\tvoid main() {\n\t\t gl_FragColor = u_color * vec4( \t\t\t\t\ttexture2D(u_textureR, v_coord).r,\t\t\t\t\ttexture2D(u_textureG, v_coord).r,\t\t\t\t\ttexture2D(u_textureB, v_coord).r,\t\t\t\t\ttexture2D(u_textureA, v_coord).r);\n\t\t}\n\t\t", -O.registerNodeType("texture/channelsTexture",w),N.title="Color",N.desc="Generates a 1x1 texture with a constant color",N.widgets_info={precision:{widget:"combo",values:c.MODE_VALUES}},N.prototype.onDrawBackground=function(a){var b=this.properties.color;a.fillStyle="rgb("+Math.floor(255*clamp(b[0],0,1))+","+Math.floor(255*clamp(b[1],0,1))+","+Math.floor(255*clamp(b[2],0,1))+")";this.flags.collapsed?this.boxcolor=a.fillStyle:a.fillRect(0,0,this.size[0],this.size[1])},N.prototype.onExecute=function(){var a= -this.properties.precision==c.HIGH?c.HIGH_PRECISION_FORMAT:gl.UNSIGNED_BYTE;this._tex&&this._tex.type==a||(this._tex=new GL.Texture(1,1,{format:gl.RGBA,type:a,minFilter:gl.NEAREST}));a=this.properties.color;if(this.inputs)for(var b=0;ba.width?d: -a,this._tex,this.properties.precision);gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);var e=Mesh.getScreenQuad(),l=null,h=this._uniforms;f?(l=b._shader_tex,l||(l=b._shader_tex=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,b.pixel_shader,{MIX_TEX:""}))):(l=b._shader_factor,l||(l=b._shader_factor=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,b.pixel_shader)),g=null==g?this.properties.factor:g,h.u_mix.set([g,g,g,g]));var m=this.properties.invert;this._tex.drawTo(function(){a.bind(m?1:0);d.bind(m?0:1);f&&f.bind(2); -l.uniforms(h).draw(e)});this.setOutputData(0,this._tex)}}},b.prototype.onGetInputs=function(){return[["factor","number"]]},b.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_textureA;\n\t\tuniform sampler2D u_textureB;\n\t\t#ifdef MIX_TEX\n\t\t\tuniform sampler2D u_textureMix;\n\t\t#else\n\t\t\tuniform vec4 u_mix;\n\t\t#endif\n\t\t\n\t\tvoid main() {\n\t\t\t#ifdef MIX_TEX\n\t\t\t vec4 f = texture2D(u_textureMix, v_coord);\n\t\t\t#else\n\t\t\t vec4 f = u_mix;\n\t\t\t#endif\n\t\t gl_FragColor = mix( texture2D(u_textureA, v_coord), texture2D(u_textureB, v_coord), f );\n\t\t}\n\t\t", -O.registerNodeType("texture/mix",b),d.title="Edges",d.desc="Detects edges",d.widgets_info={precision:{widget:"combo",values:c.MODE_VALUES}},d.prototype.onExecute=function(){if(this.isOutputConnected(0)){var a=this.getInputData(0);if(this.properties.precision===c.PASS_THROUGH)this.setOutputData(0,a);else if(a){this._tex=c.getTargetTexture(a,this._tex,this.properties.precision);gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);var b=Mesh.getScreenQuad(),f=d._shader,g=this.properties.invert,e=this.properties.factor, -l=this.properties.threshold?1:0;this._tex.drawTo(function(){a.bind(0);f.uniforms({u_texture:0,u_isize:[1/a.width,1/a.height],u_factor:e,u_threshold:l,u_invert:g?1:0}).draw(b)});this.setOutputData(0,this._tex)}}},d.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_texture;\n\t\tuniform vec2 u_isize;\n\t\tuniform int u_invert;\n\t\tuniform float u_factor;\n\t\tuniform float u_threshold;\n\t\t\n\t\tvoid main() {\n\t\t\tvec4 center = texture2D(u_texture, v_coord);\n\t\t\tvec4 up = texture2D(u_texture, v_coord + u_isize * vec2(0.0,1.0) );\n\t\t\tvec4 down = texture2D(u_texture, v_coord + u_isize * vec2(0.0,-1.0) );\n\t\t\tvec4 left = texture2D(u_texture, v_coord + u_isize * vec2(1.0,0.0) );\n\t\t\tvec4 right = texture2D(u_texture, v_coord + u_isize * vec2(-1.0,0.0) );\n\t\t\tvec4 diff = abs(center - up) + abs(center - down) + abs(center - left) + abs(center - right);\n\t\t\tdiff *= u_factor;\n\t\t\tif(u_invert == 1)\n\t\t\t\tdiff.xyz = vec3(1.0) - diff.xyz;\n\t\t\tif( u_threshold == 0.0 )\n\t\t\t\tgl_FragColor = vec4( diff.xyz, center.a );\n\t\t\telse\n\t\t\t\tgl_FragColor = vec4( diff.x > 0.5 ? 1.0 : 0.0, diff.y > 0.5 ? 1.0 : 0.0, diff.z > 0.5 ? 1.0 : 0.0, center.a );\n\t\t}\n\t\t", -O.registerNodeType("texture/edges",d),f.title="Depth Range",f.desc="Generates a texture with a depth range",f.prototype.onExecute=function(){if(this.isOutputConnected(0)){var a=this.getInputData(0);if(a){var b=gl.UNSIGNED_BYTE;this.properties.high_precision&&(b=gl.half_float_ext?gl.HALF_FLOAT_OES:gl.FLOAT);this._temp_texture&&this._temp_texture.type==b&&this._temp_texture.width==a.width&&this._temp_texture.height==a.height||(this._temp_texture=new GL.Texture(a.width,a.height,{type:b,format:gl.RGBA, -filter:gl.LINEAR}));var d=this._uniforms;b=this.properties.distance;this.isInputConnected(1)&&(b=this.getInputData(1),this.properties.distance=b);var c=this.properties.range;this.isInputConnected(2)&&(c=this.getInputData(2),this.properties.range=c);d.u_distance=b;d.u_range=c;gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);var g=Mesh.getScreenQuad();f._shader||(f._shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,f.pixel_shader),f._shader_onlydepth=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,f.pixel_shader, -{ONLY_DEPTH:""}));var e=this.properties.only_depth?f._shader_onlydepth:f._shader;b=null;b=a.near_far_planes?a.near_far_planes:window.LS&&LS.Renderer._main_camera?LS.Renderer._main_camera._uniforms.u_camera_planes:[.1,1E3];d.u_camera_planes=b;this._temp_texture.drawTo(function(){a.bind(0);e.uniforms(d).draw(g)});this._temp_texture.near_far_planes=b;this.setOutputData(0,this._temp_texture)}}},f.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_texture;\n\t\tuniform vec2 u_camera_planes;\n\t\tuniform float u_distance;\n\t\tuniform float u_range;\n\t\t\n\t\tfloat LinearDepth()\n\t\t{\n\t\t\tfloat zNear = u_camera_planes.x;\n\t\t\tfloat zFar = u_camera_planes.y;\n\t\t\tfloat depth = texture2D(u_texture, v_coord).x;\n\t\t\tdepth = depth * 2.0 - 1.0;\n\t\t\treturn zNear * (depth + 1.0) / (zFar + zNear - depth * (zFar - zNear));\n\t\t}\n\t\t\n\t\tvoid main() {\n\t\t\tfloat depth = LinearDepth();\n\t\t\t#ifdef ONLY_DEPTH\n\t\t\t gl_FragColor = vec4(depth);\n\t\t\t#else\n\t\t\t\tfloat diff = abs(depth * u_camera_planes.y - u_distance);\n\t\t\t\tfloat dof = 1.0;\n\t\t\t\tif(diff <= u_range)\n\t\t\t\t\tdof = diff / u_range;\n\t\t\t gl_FragColor = vec4(dof);\n\t\t\t#endif\n\t\t}\n\t\t", -O.registerNodeType("texture/depth_range",f),g.widgets_info={precision:{widget:"combo",values:c.MODE_VALUES}},g.title="Linear Depth",g.desc="Creates a color texture with linear depth",g.prototype.onExecute=function(){if(this.isOutputConnected(0)){var a=this.getInputData(0);if(a&&(a.format==gl.DEPTH_COMPONENT||a.format==gl.DEPTH_STENCIL)){var b=this.properties.precision==c.HIGH?gl.HIGH_PRECISION_FORMAT:gl.UNSIGNED_BYTE;this._temp_texture&&this._temp_texture.type==b&&this._temp_texture.width==a.width&& -this._temp_texture.height==a.height||(this._temp_texture=new GL.Texture(a.width,a.height,{type:b,format:gl.RGB,filter:gl.LINEAR}));var d=this._uniforms;d.u_invert=this.properties.invert?1:0;gl.disable(gl.BLEND);gl.disable(gl.DEPTH_TEST);var f=Mesh.getScreenQuad();g._shader||(g._shader=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,g.pixel_shader));var e=g._shader;b=null;b=a.near_far_planes?a.near_far_planes:window.LS&&LS.Renderer._main_camera?LS.Renderer._main_camera._uniforms.u_camera_planes:[.1,1E3]; -d.u_camera_planes=b;d.u_ires.set([0,0]);this._temp_texture.drawTo(function(){a.bind(0);e.uniforms(d).draw(f)});this._temp_texture.near_far_planes=b;this.setOutputData(0,this._temp_texture)}}},g.pixel_shader="precision highp float;\n\t\tprecision highp float;\n\t\tvarying vec2 v_coord;\n\t\tuniform sampler2D u_texture;\n\t\tuniform vec2 u_camera_planes;\n\t\tuniform int u_invert;\n\t\tuniform vec2 u_ires;\n\t\t\n\t\tvoid main() {\n\t\t\tfloat zNear = u_camera_planes.x;\n\t\t\tfloat zFar = u_camera_planes.y;\n\t\t\tfloat depth = texture2D(u_texture, v_coord + u_ires*0.5).x * 2.0 - 1.0;\n\t\t\tfloat f = zNear * (depth + 1.0) / (zFar + zNear - depth * (zFar - zNear));\n\t\t\tif( u_invert == 1 )\n\t\t\t\tf = 1.0 - f;\n\t\t\tgl_FragColor = vec4(vec3(f),1.0);\n\t\t}\n\t\t", -O.registerNodeType("texture/linear_depth",g),q.title="Blur",q.desc="Blur a texture",q.widgets_info={precision:{widget:"combo",values:c.MODE_VALUES}},q.max_iterations=20,q.prototype.onExecute=function(){var a=this.getInputData(0);if(a&&this.isOutputConnected(0)){var b=this._final_texture;b&&b.width==a.width&&b.height==a.height&&b.type==a.type||(b=this._final_texture=new GL.Texture(a.width,a.height,{type:a.type,format:gl.RGBA,filter:gl.LINEAR}));var d=this.properties.iterations;this.isInputConnected(1)&& -(d=this.getInputData(1),this.properties.iterations=d);d=Math.min(Math.floor(d),q.max_iterations);if(0==d)this.setOutputData(0,a);else{var c=this.properties.intensity;this.isInputConnected(2)&&(c=this.getInputData(2),this.properties.intensity=c);var f=O.camera_aspect;f||void 0===window.gl||(f=gl.canvas.height/gl.canvas.width);f||(f=1);f=this.properties.preserve_aspect?f:1;var g=this.properties.scale||[1,1];a.applyBlur(f*g[0],g[1],c,b);for(a=1;a>=1;1<(g|0)&&(g>>=1);if(2>f)break;k=h[A]=GL.Texture.getTemporary(f,g,e);q[0]=1/w.width;q[1]=1/w.height;w.blit(k,m.uniforms(l));w=k}c&&(q[0]=1/w.width,q[1]=1/w.height,l.u_intensity=G,l.u_delta=1,w.blit(c,m.uniforms(l)));gl.enable(gl.BLEND);gl.blendFunc(gl.ONE,gl.ONE);l.u_intensity=this.persistence; -l.u_delta=.5;for(A-=2;0<=A;A--)k=h[A],h[A]=null,q[0]=1/w.width,q[1]=1/w.height,w.blit(k,m.uniforms(l)),GL.Texture.releaseTemporary(w),w=k;gl.disable(gl.BLEND);d&&w.blit(d);if(b){var n=this.dirt_texture,t=this.dirt_factor;l.u_intensity=G;m=n?z._dirt_final_shader:z._final_shader;m||(m=n?z._dirt_final_shader=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,z.final_pixel_shader,{USE_DIRT:""}):z._final_shader=new GL.Shader(GL.Shader.SCREEN_VERTEX_SHADER,z.final_pixel_shader));b.drawTo(function(){a.bind(0); -w.bind(1);n&&(m.setUniform("u_dirt_factor",t),m.setUniform("u_dirt_texture",n.bind(2)));m.toViewport(l)})}GL.Texture.releaseTemporary(w)},z.cut_pixel_shader="precision highp float;\n\tvarying vec2 v_coord;\n\tuniform sampler2D u_texture;\n\tuniform float u_threshold;\n\tvoid main() {\n\t\tgl_FragColor = max( texture2D( u_texture, v_coord ) - vec4( u_threshold ), vec4(0.0) );\n\t}",z.scale_pixel_shader="precision highp float;\n\tvarying vec2 v_coord;\n\tuniform sampler2D u_texture;\n\tuniform vec2 u_texel_size;\n\tuniform float u_delta;\n\tuniform float u_intensity;\n\t\n\tvec4 sampleBox(vec2 uv) {\n\t\tvec4 o = u_texel_size.xyxy * vec2(-u_delta, u_delta).xxyy;\n\t\tvec4 s = texture2D( u_texture, uv + o.xy ) + texture2D( u_texture, uv + o.zy) + texture2D( u_texture, uv + o.xw) + texture2D( u_texture, uv + o.zw);\n\t\treturn s * 0.25;\n\t}\n\tvoid main() {\n\t\tgl_FragColor = u_intensity * sampleBox( v_coord );\n\t}", -z.final_pixel_shader="precision highp float;\n\tvarying vec2 v_coord;\n\tuniform sampler2D u_texture;\n\tuniform sampler2D u_glow_texture;\n\t#ifdef USE_DIRT\n\t\tuniform sampler2D u_dirt_texture;\n\t#endif\n\tuniform vec2 u_texel_size;\n\tuniform float u_delta;\n\tuniform float u_intensity;\n\tuniform float u_dirt_factor;\n\t\n\tvec4 sampleBox(vec2 uv) {\n\t\tvec4 o = u_texel_size.xyxy * vec2(-u_delta, u_delta).xxyy;\n\t\tvec4 s = texture2D( u_glow_texture, uv + o.xy ) + texture2D( u_glow_texture, uv + o.zy) + texture2D( u_glow_texture, uv + o.xw) + texture2D( u_glow_texture, uv + o.zw);\n\t\treturn s * 0.25;\n\t}\n\tvoid main() {\n\t\tvec4 glow = sampleBox( v_coord );\n\t\t#ifdef USE_DIRT\n\t\t\tglow = mix( glow, glow * texture2D( u_dirt_texture, v_coord ), u_dirt_factor );\n\t\t#endif\n\t\tgl_FragColor = texture2D( u_texture, v_coord ) + u_intensity * glow;\n\t}", -P.title="Glow",P.desc="Filters a texture giving it a glow effect",P.widgets_info={iterations:{type:"number",min:0,max:16,step:1,precision:0},threshold:{type:"number",min:0,max:10,step:.01,precision:2},precision:{widget:"combo",values:c.MODE_VALUES}},P.prototype.onGetInputs=function(){return[["enabled","boolean"],["threshold","number"],["intensity","number"],["persistence","number"],["iterations","number"],["dirt_factor","number"]]},P.prototype.onGetOutputs=function(){return[["average","Texture"]]}, -P.prototype.onExecute=function(){var a=this.getInputData(0);if(a&&this.isAnyOutputConnected())if(this.properties.precision===c.PASS_THROUGH||!1===this.getInputOrProperty("enabled"))this.setOutputData(0,a);else{var b=this.fx;b.threshold=this.getInputOrProperty("threshold");b.iterations=this.getInputOrProperty("iterations");b.intensity=this.getInputOrProperty("intensity");b.persistence=this.getInputOrProperty("persistence");b.dirt_texture=this.getInputData(1);b.dirt_factor=this.getInputOrProperty("dirt_factor"); -b.scale=this.properties.scale;var d=c.getTextureType(this.properties.precision,a),f=null;this.isOutputConnected(2)&&(f=this._average_texture,f&&f.type==a.type&&f.format==a.format||(f=this._average_texture=new GL.Texture(1,1,{type:a.type,format:a.format,filter:gl.LINEAR})));var g=null;this.isOutputConnected(1)&&(g=this._glow_texture,g&&g.width==a.width&&g.height==a.height&&g.type==d&&g.format==a.format||(g=this._glow_texture=new GL.Texture(a.width,a.height,{type:d,format:a.format,filter:gl.LINEAR}))); -var e=null;this.isOutputConnected(0)&&(e=this._final_texture,e&&e.width==a.width&&e.height==a.height&&e.type==d&&e.format==a.format||(e=this._final_texture=new GL.Texture(a.width,a.height,{type:d,format:a.format,filter:gl.LINEAR})));b.applyFX(a,e,g,f);this.isOutputConnected(0)&&this.setOutputData(0,e);this.isOutputConnected(1)&&this.setOutputData(1,f);this.isOutputConnected(2)&&this.setOutputData(2,g)}},O.registerNodeType("texture/glow",P),I.title="Kuwahara Filter",I.desc="Filters a texture giving an artistic oil canvas painting", -I.max_radius=10,I._shaders=[],I.prototype.onExecute=function(){var a=this.getInputData(0);if(a&&this.isOutputConnected(0)){var b=this._temp_texture;b&&b.width==a.width&&b.height==a.height&&b.type==a.type||(this._temp_texture=new GL.Texture(a.width,a.height,{type:a.type,format:gl.RGBA,filter:gl.LINEAR}));b=this.properties.radius;b=Math.min(Math.floor(b),I.max_radius);if(0==b)this.setOutputData(0,a);else{var d=this.properties.intensity,c=O.camera_aspect;c||void 0===window.gl||(c=gl.canvas.height/gl.canvas.width); -c||(c=1);c=this.properties.preserve_aspect?c:1;I._shaders[b]||(I._shaders[b]=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,I.pixel_shader,{RADIUS:b.toFixed(0)}));var f=I._shaders[b],g=GL.Mesh.getScreenQuad();a.bind(0);this._temp_texture.drawTo(function(){f.uniforms({u_texture:0,u_intensity:d,u_resolution:[a.width,a.height],u_iResolution:[1/a.width,1/a.height]}).draw(g)});this.setOutputData(0,this._temp_texture)}}},I.pixel_shader="\nprecision highp float;\nvarying vec2 v_coord;\nuniform sampler2D u_texture;\nuniform float u_intensity;\nuniform vec2 u_resolution;\nuniform vec2 u_iResolution;\n#ifndef RADIUS\n\t#define RADIUS 7\n#endif\nvoid main() {\n\n\tconst int radius = RADIUS;\n\tvec2 fragCoord = v_coord;\n\tvec2 src_size = u_iResolution;\n\tvec2 uv = v_coord;\n\tfloat n = float((radius + 1) * (radius + 1));\n\tint i;\n\tint j;\n\tvec3 m0 = vec3(0.0); vec3 m1 = vec3(0.0); vec3 m2 = vec3(0.0); vec3 m3 = vec3(0.0);\n\tvec3 s0 = vec3(0.0); vec3 s1 = vec3(0.0); vec3 s2 = vec3(0.0); vec3 s3 = vec3(0.0);\n\tvec3 c;\n\t\n\tfor (int j = -radius; j <= 0; ++j) {\n\t\tfor (int i = -radius; i <= 0; ++i) {\n\t\t\tc = texture2D(u_texture, uv + vec2(i,j) * src_size).rgb;\n\t\t\tm0 += c;\n\t\t\ts0 += c * c;\n\t\t}\n\t}\n\t\n\tfor (int j = -radius; j <= 0; ++j) {\n\t\tfor (int i = 0; i <= radius; ++i) {\n\t\t\tc = texture2D(u_texture, uv + vec2(i,j) * src_size).rgb;\n\t\t\tm1 += c;\n\t\t\ts1 += c * c;\n\t\t}\n\t}\n\t\n\tfor (int j = 0; j <= radius; ++j) {\n\t\tfor (int i = 0; i <= radius; ++i) {\n\t\t\tc = texture2D(u_texture, uv + vec2(i,j) * src_size).rgb;\n\t\t\tm2 += c;\n\t\t\ts2 += c * c;\n\t\t}\n\t}\n\t\n\tfor (int j = 0; j <= radius; ++j) {\n\t\tfor (int i = -radius; i <= 0; ++i) {\n\t\t\tc = texture2D(u_texture, uv + vec2(i,j) * src_size).rgb;\n\t\t\tm3 += c;\n\t\t\ts3 += c * c;\n\t\t}\n\t}\n\t\n\tfloat min_sigma2 = 1e+2;\n\tm0 /= n;\n\ts0 = abs(s0 / n - m0 * m0);\n\t\n\tfloat sigma2 = s0.r + s0.g + s0.b;\n\tif (sigma2 < min_sigma2) {\n\t\tmin_sigma2 = sigma2;\n\t\tgl_FragColor = vec4(m0, 1.0);\n\t}\n\t\n\tm1 /= n;\n\ts1 = abs(s1 / n - m1 * m1);\n\t\n\tsigma2 = s1.r + s1.g + s1.b;\n\tif (sigma2 < min_sigma2) {\n\t\tmin_sigma2 = sigma2;\n\t\tgl_FragColor = vec4(m1, 1.0);\n\t}\n\t\n\tm2 /= n;\n\ts2 = abs(s2 / n - m2 * m2);\n\t\n\tsigma2 = s2.r + s2.g + s2.b;\n\tif (sigma2 < min_sigma2) {\n\t\tmin_sigma2 = sigma2;\n\t\tgl_FragColor = vec4(m2, 1.0);\n\t}\n\t\n\tm3 /= n;\n\ts3 = abs(s3 / n - m3 * m3);\n\t\n\tsigma2 = s3.r + s3.g + s3.b;\n\tif (sigma2 < min_sigma2) {\n\t\tmin_sigma2 = sigma2;\n\t\tgl_FragColor = vec4(m3, 1.0);\n\t}\n}\n", -O.registerNodeType("texture/kuwahara",I),t.title="XDoG Filter",t.desc="Filters a texture giving an artistic ink style",t.max_radius=10,t._shaders=[],t.prototype.onExecute=function(){var a=this.getInputData(0);if(a&&this.isOutputConnected(0)){var b=this._temp_texture;b&&b.width==a.width&&b.height==a.height&&b.type==a.type||(this._temp_texture=new GL.Texture(a.width,a.height,{type:a.type,format:gl.RGBA,filter:gl.LINEAR}));t._xdog_shader||(t._xdog_shader=new GL.Shader(Shader.SCREEN_VERTEX_SHADER,t.xdog_pixel_shader)); -var d=t._xdog_shader,c=GL.Mesh.getScreenQuad(),f=this.properties.sigma,g=this.properties.k,e=this.properties.p,l=this.properties.epsilon,h=this.properties.phi;a.bind(0);this._temp_texture.drawTo(function(){d.uniforms({src:0,sigma:f,k:g,p:e,epsilon:l,phi:h,cvsWidth:a.width,cvsHeight:a.height}).draw(c)});this.setOutputData(0,this._temp_texture)}},t.xdog_pixel_shader="\nprecision highp float;\nuniform sampler2D src;\n\nuniform float cvsHeight;\nuniform float cvsWidth;\n\nuniform float sigma;\nuniform float k;\nuniform float p;\nuniform float epsilon;\nuniform float phi;\nvarying vec2 v_coord;\n\nfloat cosh(float val)\n{\n\tfloat tmp = exp(val);\n\tfloat cosH = (tmp + 1.0 / tmp) / 2.0;\n\treturn cosH;\n}\n\nfloat tanh(float val)\n{\n\tfloat tmp = exp(val);\n\tfloat tanH = (tmp - 1.0 / tmp) / (tmp + 1.0 / tmp);\n\treturn tanH;\n}\n\nfloat sinh(float val)\n{\n\tfloat tmp = exp(val);\n\tfloat sinH = (tmp - 1.0 / tmp) / 2.0;\n\treturn sinH;\n}\n\nvoid main(void){\n\tvec3 destColor = vec3(0.0);\n\tfloat tFrag = 1.0 / cvsHeight;\n\tfloat sFrag = 1.0 / cvsWidth;\n\tvec2 Frag = vec2(sFrag,tFrag);\n\tvec2 uv = gl_FragCoord.st;\n\tfloat twoSigmaESquared = 2.0 * sigma * sigma;\n\tfloat twoSigmaRSquared = twoSigmaESquared * k * k;\n\tint halfWidth = int(ceil( 1.0 * sigma * k ));\n\n\tconst int MAX_NUM_ITERATION = 99999;\n\tvec2 sum = vec2(0.0);\n\tvec2 norm = vec2(0.0);\n\n\tfor(int cnt=0;cnt (2*halfWidth+1)*(2*halfWidth+1)){break;}\n\t\tint i = int(cnt / (2*halfWidth+1)) - halfWidth;\n\t\tint j = cnt - halfWidth - int(cnt / (2*halfWidth+1)) * (2*halfWidth+1);\n\n\t\tfloat d = length(vec2(i,j));\n\t\tvec2 kernel = vec2( exp( -d * d / twoSigmaESquared ), \n\t\t\t\t\t\t\texp( -d * d / twoSigmaRSquared ));\n\n\t\tvec2 L = texture2D(src, (uv + vec2(i,j)) * Frag).xx;\n\n\t\tnorm += kernel;\n\t\tsum += kernel * L;\n\t}\n\n\tsum /= norm;\n\n\tfloat H = 100.0 * ((1.0 + p) * sum.x - p * sum.y);\n\tfloat edge = ( H > epsilon )? 1.0 : 1.0 + tanh( phi * (H - epsilon));\n\tdestColor = vec3(edge);\n\tgl_FragColor = vec4(destColor, 1.0);\n}", -O.registerNodeType("texture/xDoG",t),v.title="Webcam",v.desc="Webcam texture",v.is_webcam_open=!1,v.prototype.openStream=function(){if(navigator.getUserMedia){this._waiting_confirmation=!0;navigator.mediaDevices.getUserMedia({audio:!1,video:{facingMode:this.properties.facingMode}}).then(this.streamReady.bind(this)).catch(function(b){v.is_webcam_open=!1;console.log("Webcam rejected",b);a._webcam_stream=!1;a.boxcolor="red";a.trigger("stream_error")});var a=this}},v.prototype.closeStream=function(){if(this._webcam_stream){var a= -this._webcam_stream.getTracks();if(a.length)for(var b=0;b=this.size[1]||!this._video||(a.save(),a.webgl?this._video_texture&&a.drawImage(this._video_texture,0,0,this.size[0],this.size[1]):a.drawImage(this._video, -0,0,this.size[0],this.size[1]),a.restore())},v.prototype.onExecute=function(){null!=this._webcam_stream||this._waiting_confirmation||this.openStream();if(this._video&&this._video.videoWidth){var a=this._video.videoWidth,b=this._video.videoHeight,d=this._video_texture;d&&d.width==a&&d.height==b||(this._video_texture=new GL.Texture(a,b,{format:gl.RGB,filter:gl.LINEAR}));this._video_texture.uploadImage(this._video);this._video_texture.version=++this.version;this.properties.texture_name&&(c.getTexturesContainer()[this.properties.texture_name]= -this._video_texture);this.setOutputData(0,this._video_texture);for(a=1;aMath.abs(b))return c[1];a=(a-c[0])/b;return c[1]*(1-a)+f[1]*a}}return 0}},G.prototype.updateCurve=function(){for(var a=this._values,b=a.length/4,d=this.properties.split_channels,c=0;cd+f.NODE_TITLE_HEIGHT&&a.drawImage(b,10,g,this.size[0]-20,this.size[1]-d-f.NODE_TITLE_HEIGHT);var g=this.size[1]-f.NODE_TITLE_HEIGHT+.5;c=f.isInsideRectangle(c[0],c[1],this.pos[0],this.pos[1]+g,this.size[0],f.NODE_TITLE_HEIGHT);a.fillStyle=c?"#555":"#222";a.beginPath();this._shape==f.BOX_SHAPE?a.rect(0,g,this.size[0]+1,f.NODE_TITLE_HEIGHT):a.roundRect(0,g,this.size[0]+1,f.NODE_TITLE_HEIGHT,0,8);a.fill();a.textAlign="center";a.font="24px Arial";a.fillStyle= -c?"#DDD":"#999";a.fillText("+",.5*this.size[0],g+24)}};E.prototype.onMouseDown=function(a,b,d){b[1]>this.size[1]-f.NODE_TITLE_HEIGHT+.5&&d.showSubgraphPropertiesDialog(this)};E.prototype.onDrawSubgraphBackground=function(a){};E.prototype.getExtraMenuOptions=function(a){var b=this;return[{content:"Print Code",callback:function(){var a=b._context.computeShaderCode();console.log(a.vs_code,a.fs_code)}}]};f.registerNodeType("texture/shaderGraph",E);D.title="Uniform";D.desc="Input data for the shader"; -D.prototype.getTitle=function(){return this.properties.name&&this.flags.collapsed?this.properties.type+" "+this.properties.name:"Uniform"};D.prototype.onPropertyChanged=function(a,b){this.outputs[0].name=this.properties.type+" "+this.properties.name};D.prototype.onGetCode=function(a){if(this.shader_destination){var b=this.properties.type;if(!b){if(!a.onGetPropertyInfo)return;b=a.onGetPropertyInfo(this.property.name);if(!b)return;b=b.type}"number"==b?b="float":"texture"==b&&(b="sampler2D");-1!=g.GLSL_types.indexOf(b)&& -(a.addUniform("u_"+this.properties.name,b),this.setOutputData(0,b))}};D.prototype.getOutputVarName=function(a){return"u_"+this.properties.name};k("input/uniform",D);J.title="Attribute";J.desc="Input data from mesh attribute";J.prototype.getTitle=function(){return"att. "+this.properties.name};J.prototype.onGetCode=function(a){if(this.shader_destination){var b=this.properties.type;b&&-1!=g.GLSL_types.indexOf(b)&&("number"==b&&(b="float"),"coord"!=this.properties.name&&a.addCode("varying"," varying "+ -b+" v_"+this.properties.name+";"),this.setOutputData(0,b))}};J.prototype.getOutputVarName=function(a){return"v_"+this.properties.name};k("input/attribute",J);L.title="Sampler2D";L.desc="Reads a pixel from a texture";L.prototype.onGetCode=function(a){if(this.shader_destination){var b=r(this,0),d=n(this),c="vec4 "+d+" = vec4(0.0);\n";if(b){var f=r(this,1)||a.buffer_names.uvs;c+=d+" = texture2D("+b+","+f+");\n"}u(this,0)&&(c+="vec4 "+u(this,0)+" = "+d+";\n");u(this,1)&&(c+="vec3 "+u(this,1)+" = "+d+ -".xyz;\n");a.addCode("code",c,this.shader_destination);this.setOutputData(0,"vec4");this.setOutputData(1,"vec3")}};k("texture/sampler2D",L);H.title="const";H.prototype.getTitle=function(){return this.flags.collapsed?t(this.properties.value,this.properties.type,2):"Const"};H.prototype.onPropertyChanged=function(a,b){"type"==a&&(this.outputs[0].type!=b&&(this.disconnectOutput(0),this.outputs[0].type=b),this.widgets.length=1,this.updateWidgets());"value"==a&&(b.length?(this.widgets[1].value=b[1],2d;++d)b.push({name:r(this,d),type:this.getInputData(d)||"float"});var c=u(this,0);if(c){var f=b[0].type,g=this.properties.operation, -e=[];for(d=0;2>d;++d){var l=b[d].name;null==l&&(l=null!=p.value?p.value:"(1.0)",b[d].type="float");b[d].type!=f&&("float"!=b[d].type||"*"!=g&&"/"!=g)&&(l=C(l,b[d].type,f));e.push(l)}a.addCode("code",f+" "+c+" = "+e[0]+g+e[1]+";",this.shader_destination);this.setOutputData(0,f)}}};k("math/operation",M);l.title="Func";l.prototype.onPropertyChanged=function(a,b){this.graph&&this.graph._version++;if("func"==a&&(a=P[b])){for(b=a.params.length;bd;++d)b.push({name:r(this,d),type:this.getInputData(d)||"float"});var c=u(this,0);if(c){var f=P[this.properties.func];if(f){var g=b[0].type,e=f.return_type;"T"==e&&(e=g);var l=[];for(d= -0;d=c;){g=.5*(e+c)|0;d=a[g];if(d==b)break;if(c==e-1)return c;da&&(a=1);this.points&&this.points.length==3*a||(this.points=new Float32Array(3*a));this.properties.generate_normals?this.normals&&this.normals.length==this.points.length||(this.normals=new Float32Array(this.points.length)):this.normals=null;var b=this._last_radius||this.properties.radius,d=this.properties.mode,c=this.getInputData(0);this._old_obj_version=c?c._version:null;this.points=k.generatePoints(b,a,d,this.points,this.normals, -this.properties.regular,c);this.version++};k.generatePoints=function(a,b,d,c,g,e,l){var f=3*b;c&&c.length==f||(c=new Float32Array(f));var h=new Float32Array(3),m=new Float32Array([0,1,0]);if(e)if(d==k.RECTANGLE){f=Math.floor(Math.sqrt(b));for(b=0;bg||ue&&el))break}this.geometry.indices=this.indices=new Uint32Array(h)}this.indices&&this.indices.length?(this.geometry.indices=this.indices,this.setOutputData(0,this.geometry)):this.setOutputData(0,null)}};B.registerNodeType("geometry/connectPoints",J);"undefined"!=typeof GL&&(L.title="to geometry",L.desc="converts a mesh to geometry",L.prototype.onExecute=function(){var a=this.getInputData(0);if(a){if(a!=this.last_mesh){this.last_mesh=a;for(i in a.vertexBuffers)this.geometry[i]= -a.vertexBuffers[i].data;a.indexBuffers.triangles&&(this.geometry.indices=a.indexBuffers.triangles.data);this.geometry._id=c();this.geometry._version=0}this.setOutputData(0,this.geometry);this.geometry&&this.setOutputData(1,this.geometry.vertices)}},B.registerNodeType("geometry/toGeometry",L),H.title="Geo to Mesh",H.prototype.updateMesh=function(a){this.mesh||(this.mesh=new GL.Mesh);for(var b in a)if("_"!=b[0]){var d=a[b],c=GL.Mesh.common_buffers[b];if(c||"indices"==b){c=c?c.spacing:3;var g=this.mesh.vertexBuffers[b]; -g&&g.data.length==d.length?(g.data.set(d),g.upload(GL.DYNAMIC_DRAW)):g=new GL.Buffer("indices"==b?GL.ELEMENT_ARRAY_BUFFER:GL.ARRAY_BUFFER,d,c,GL.DYNAMIC_DRAW);this.mesh.addBuffer(b,g)}}if(this.mesh.vertexBuffers.normals&&this.mesh.vertexBuffers.normals.data.length!=this.mesh.vertexBuffers.vertices.data.length){d=new Float32Array([0,1,0]);c=new Float32Array(this.mesh.vertexBuffers.vertices.data.length);for(b=0;b=c.NOTEON||l<=c.NOTEOFF)this.channel=e&15};Object.defineProperty(c.prototype,"velocity",{get:function(){return this.cmd==c.NOTEON?this.data[2]:-1},set:function(c){this.data[2]=c},enumerable:!0});c.notes="A A# B C C# D D# E F F# G G#".split(" ");c.note_to_index={A:0,"A#":1,B:2,C:3,"C#":4,D:5,"D#":6,E:7,F:8,"F#":9,G:10,"G#":11};Object.defineProperty(c.prototype,"note",{get:function(){return this.cmd!= -c.NOTEON?-1:c.toNoteString(this.data[1],!0)},set:function(c){throw"notes cannot be assigned this way, must modify the data[1]";},enumerable:!0});Object.defineProperty(c.prototype,"octave",{get:function(){return this.cmd!=c.NOTEON?-1:Math.floor((this.data[1]-24)/12+1)},set:function(c){throw"octave cannot be assigned this way, must modify the data[1]";},enumerable:!0});c.prototype.getPitch=function(){return 440*Math.pow(2,(this.data[1]-69)/12)};c.computePitch=function(c){return 440*Math.pow(2,(c-69)/ -12)};c.prototype.getCC=function(){return this.data[1]};c.prototype.getCCValue=function(){return this.data[2]};c.prototype.getPitchBend=function(){return this.data[1]+(this.data[2]<<7)-8192};c.computePitchBend=function(c,e){return c+(e<<7)-8192};c.prototype.setCommandFromString=function(e){this.cmd=c.computeCommandFromString(e)};c.computeCommandFromString=function(e){if(!e)return 0;if(e&&e.constructor===Number)return e;e=e.toUpperCase();switch(e){case "NOTE ON":case "NOTEON":return c.NOTEON;case "NOTE OFF":case "NOTEOFF":return c.NOTEON; -case "KEY PRESSURE":case "KEYPRESSURE":return c.KEYPRESSURE;case "CONTROLLER CHANGE":case "CONTROLLERCHANGE":case "CC":return c.CONTROLLERCHANGE;case "PROGRAM CHANGE":case "PROGRAMCHANGE":case "PC":return c.PROGRAMCHANGE;case "CHANNEL PRESSURE":case "CHANNELPRESSURE":return c.CHANNELPRESSURE;case "PITCH BEND":case "PITCHBEND":return c.PITCHBEND;case "TIME TICK":case "TIMETICK":return c.TIMETICK;default:return Number(e)}};c.toNoteString=function(e,h){e=Math.round(e);var l=Math.floor((e-24)/12+1);e= -(e-21)%12;0>e&&(e=12+e);return c.notes[e]+(h?"":l)};c.NoteStringToPitch=function(e){e=e.toUpperCase();var l=e[0],h=4;"#"==e[1]?(l+="#",2this.properties.max_value)return;this.trigger("on_midi",h)}};B.registerNodeType("midi/filter",h);E.title="MIDIEvent";E.desc="Create a MIDI Event";E.color="#243";E.prototype.onAction=function(e,h){"assign"==e?(this.properties.channel=h.channel,this.properties.cmd=h.cmd,this.properties.value1=h.data[1],this.properties.value2=h.data[2],h.cmd== -c.NOTEON?this.gate=!0:h.cmd==c.NOTEOFF&&(this.gate=!1)):(h=this.midi_event,h.channel=this.properties.channel,this.properties.cmd&&this.properties.cmd.constructor===String?h.setCommandFromString(this.properties.cmd):h.cmd=this.properties.cmd,h.data[0]=h.cmd|h.channel,h.data[1]=Number(this.properties.value1),h.data[2]=Number(this.properties.value2),this.trigger("on_midi",h))};E.prototype.onExecute=function(){var e=this.properties;if(this.inputs)for(var h=0;hc;++c)this.valid_notes[c]=-1!=this.notes_pitches.indexOf(c);for(c=0;12>c;++c)if(this.valid_notes[c])this.offset_notes[c]=0;else for(var e=1;12>e;++e){if(this.valid_notes[(c-e)%12]){this.offset_notes[c]=-e;break}if(this.valid_notes[(c+e)%12]){this.offset_notes[c]=e;break}}};H.prototype.onAction=function(e,h){h&&h.constructor===c&&(h.data[0]==c.NOTEON||h.data[0]==c.NOTEOFF?(this.midi_event=new c,this.midi_event.setup(h.data),this.midi_event.data[1]+=this.offset_notes[c.note_to_index[h.note]], -this.trigger("out",this.midi_event)):this.trigger("out",h))};H.prototype.onExecute=function(){var c=this.getInputData(1);null!=c&&c!=this._current_scale&&this.processScale(c)};B.registerNodeType("midi/quantize",H);F.title="MIDI fromFile";F.desc="Plays a MIDI file";F.color="#243";F.prototype.onAction=function(c){"play"==c?this.play():"pause"==c&&(this._playing=!this._playing)};F.prototype.onPropertyChanged=function(c,e){"url"==c&&this.loadMIDIFile(e)};F.prototype.onExecute=function(){if(this._midi&& -this._playing){this._current_time+=this.graph.elapsed_time;for(var e=100*this._current_time,h=0;ha;a++)for(var b=0;bd+f||c[1]>b))return a}}return-1};K.prototype.onAction=function(e,h){if("reset"==e)for(h=0;hh[1]))return e=this.getKeyIndex(h),this.keys[e]=!0,this._last_key=e,e=12*(this.properties.start_octave-1)+29+e,h=new c,h.setup([c.NOTEON,e,100]),this.trigger("note",h),!0};K.prototype.onMouseMove=function(e,h){if(!(0>h[1]||-1==this._last_key)){this.setDirtyCanvas(!0); -e=this.getKeyIndex(h);if(this._last_key==e)return!0;this.keys[this._last_key]=!1;h=12*(this.properties.start_octave-1)+29+this._last_key;var k=new c;k.setup([c.NOTEOFF,h,100]);this.trigger("note",k);this.keys[e]=!0;h=12*(this.properties.start_octave-1)+29+e;k=new c;k.setup([c.NOTEON,h,100]);this.trigger("note",k);this._last_key=e;return!0}};K.prototype.onMouseUp=function(e,h){if(!(0>h[1]))return e=this.getKeyIndex(h),this.keys[e]=!1,this._last_key=-1,e=12*(this.properties.start_octave-1)+29+e,h=new c, -h.setup([c.NOTEOFF,e,100]),this.trigger("note",h),!0};B.registerNodeType("midi/keys",K)})(this); -(function(y){function c(){this.properties={src:"",gain:.5,loop:!0,autoplay:!0,playbackRate:1};this._loading_audio=!1;this._audiobuffer=null;this._audionodes=[];this._last_sourcenode=null;this.addOutput("out","audio");this.addInput("gain","number");this.audionode=m.getAudioContext().createGain();this.audionode.graphnode=this;this.audionode.gain.value=this.properties.gain;this.properties.src&&this.loadSound(this.properties.src)}function k(){this.properties={gain:.5};this._audionodes=[];this._media_stream= -null;this.addOutput("out","audio");this.addInput("gain","number");this.audionode=m.getAudioContext().createGain();this.audionode.graphnode=this;this.audionode.gain.value=this.properties.gain}function n(){this.properties={fftSize:2048,minDecibels:-100,maxDecibels:-10,smoothingTimeConstant:.5};this.audionode=m.getAudioContext().createAnalyser();this.audionode.graphnode=this;this.audionode.fftSize=this.properties.fftSize;this.audionode.minDecibels=this.properties.minDecibels;this.audionode.maxDecibels= -this.properties.maxDecibels;this.audionode.smoothingTimeConstant=this.properties.smoothingTimeConstant;this.addInput("in","audio");this.addOutput("freqs","array");this.addOutput("samples","array");this._time_bin=this._freq_bin=null}function r(){this.properties={gain:1};this.audionode=m.getAudioContext().createGain();this.addInput("in","audio");this.addInput("gain","number");this.addOutput("out","audio")}function u(){this.properties={impulse_src:"",normalize:!0};this.audionode=m.getAudioContext().createConvolver(); -this.addInput("in","audio");this.addOutput("out","audio")}function h(){this.properties={threshold:-50,knee:40,ratio:12,reduction:-20,attack:0,release:.25};this.audionode=m.getAudioContext().createDynamicsCompressor();this.addInput("in","audio");this.addOutput("out","audio")}function E(){this.properties={};this.audionode=m.getAudioContext().createWaveShaper();this.addInput("in","audio");this.addInput("shape","waveshape");this.addOutput("out","audio")}function D(){this.properties={gain1:.5,gain2:.5}; -this.audionode=m.getAudioContext().createGain();this.audionode1=m.getAudioContext().createGain();this.audionode1.gain.value=this.properties.gain1;this.audionode2=m.getAudioContext().createGain();this.audionode2.gain.value=this.properties.gain2;this.audionode1.connect(this.audionode);this.audionode2.connect(this.audionode);this.addInput("in1","audio");this.addInput("in1 gain","number");this.addInput("in2","audio");this.addInput("in2 gain","number");this.addOutput("out","audio")}function J(){this.properties= -{A:.1,D:.1,S:.1,R:.1};this.audionode=m.getAudioContext().createGain();this.audionode.gain.value=0;this.addInput("in","audio");this.addInput("gate","boolean");this.addOutput("out","audio");this.gate=!1}function L(){this.properties={delayTime:.5};this.audionode=m.getAudioContext().createDelay(10);this.audionode.delayTime.value=this.properties.delayTime;this.addInput("in","audio");this.addInput("time","number");this.addOutput("out","audio")}function H(){this.properties={frequency:350,detune:0,Q:1};this.addProperty("type", -"lowpass","enum",{values:"lowpass highpass bandpass lowshelf highshelf peaking notch allpass".split(" ")});this.audionode=m.getAudioContext().createBiquadFilter();this.addInput("in","audio");this.addOutput("out","audio")}function F(){this.properties={frequency:440,detune:0,type:"sine"};this.addProperty("type","sine","enum",{values:["sine","square","sawtooth","triangle","custom"]});this.audionode=m.getAudioContext().createOscillator();this.addOutput("out","audio")}function e(){this.properties={continuous:!0, -mark:-1};this.addInput("data","array");this.addInput("mark","number");this.size=[300,200];this._last_buffer=null}function K(){this.properties={band:440,amplitude:1};this.addInput("freqs","array");this.addOutput("signal","number")}function B(){if(!B.default_code){var c=B.default_function.toString(),e=c.indexOf("{")+1,a=c.lastIndexOf("}");B.default_code=c.substr(e,a-e)}this.properties={code:B.default_code};c=m.getAudioContext();c.createScriptProcessor?this.audionode=c.createScriptProcessor(4096,1,1): -(console.warn("ScriptProcessorNode deprecated"),this.audionode=c.createGain());this.processCode();B._bypass_function||(B._bypass_function=this.audionode.onaudioprocess);this.addInput("in","audio");this.addOutput("out","audio")}function M(){this.audionode=m.getAudioContext().destination;this.addInput("in","audio")}var l=y.LiteGraph,m={};y.LGAudio=m;m.getAudioContext=function(){if(!this._audio_context){window.AudioContext=window.AudioContext||window.webkitAudioContext;if(!window.AudioContext)return console.error("AudioContext not supported by browser"), -null;this._audio_context=new AudioContext;this._audio_context.onmessage=function(c){console.log("msg",c)};this._audio_context.onended=function(c){console.log("ended",c)};this._audio_context.oncomplete=function(c){console.log("complete",c)}}return this._audio_context};m.connect=function(c,e){try{c.connect(e)}catch(a){console.warn("LGraphAudio:",a)}};m.disconnect=function(c,e){try{c.disconnect(e)}catch(a){console.warn("LGraphAudio:",a)}};m.changeAllAudiosConnections=function(c,e){if(c.inputs)for(var a= -0;a=this.size[0]&&(d=this.size[0]-1),c.strokeStyle= -"red",c.beginPath(),c.moveTo(d,b),c.lineTo(d,0),c.stroke())}};e.title="Visualization";e.desc="Audio Visualization";l.registerNodeType("audio/visualization",e);K.prototype.onExecute=function(){if(this._freqs=this.getInputData(0)){var c=this.properties.band,e=this.getInputData(1);void 0!==e&&(c=e);e=m.getAudioContext().sampleRate/this._freqs.length;e=c/e*2;e>=this._freqs.length?e=this._freqs[this._freqs.length-1]:(c=e|0,e-=c,e=this._freqs[c]*(1-e)+this._freqs[c+1]*e);this.setOutputData(0,e/255*this.properties.amplitude)}}; -K.prototype.onGetInputs=function(){return[["band","number"]]};K.title="Signal";K.desc="extract the signal of some frequency";l.registerNodeType("audio/signal",K);B.prototype.onAdded=function(c){c.status==LGraph.STATUS_RUNNING&&(this.audionode.onaudioprocess=this._callback)};B["@code"]={widget:"code",type:"code"};B.prototype.onStart=function(){this.audionode.onaudioprocess=this._callback};B.prototype.onStop=function(){this.audionode.onaudioprocess=B._bypass_function};B.prototype.onPause=function(){this.audionode.onaudioprocess= -B._bypass_function};B.prototype.onUnpause=function(){this.audionode.onaudioprocess=this._callback};B.prototype.onExecute=function(){};B.prototype.onRemoved=function(){this.audionode.onaudioprocess=B._bypass_function};B.prototype.processCode=function(){try{this._script=new (new Function("properties",this.properties.code))(this.properties),this._old_code=this.properties.code,this._callback=this._script.onaudioprocess}catch(w){console.error("Error in onaudioprocess code",w),this._callback=B._bypass_function, -this.audionode.onaudioprocess=this._callback}};B.prototype.onPropertyChanged=function(c,e){"code"==c&&(this.properties.code=e,this.processCode(),this.graph&&this.graph.status==LGraph.STATUS_RUNNING&&(this.audionode.onaudioprocess=this._callback))};B.default_function=function(){this.onaudioprocess=function(c){var e=c.inputBuffer;c=c.outputBuffer;for(var a=0;a(a^Math.random()*16>>a/4).toString(16)); + return ([1e7]+-1e3+-4e3+-8e3+-1e11).replace(/[018]/g,(a) => (a^Math.random()*16>>a/4).toString(16)); }, /** @@ -684,12 +674,12 @@ * @return {Boolean} true if they can be connected */ isValidConnection: function(type_a, type_b) { - if (type_a=="" || type_a==="*") type_a = 0; - if (type_b=="" || type_b==="*") type_b = 0; + if (type_a=="" || type_a==="*") type_a = 0; + if (type_b=="" || type_b==="*") type_b = 0; if ( - !type_a //generic output + !type_a // generic output || !type_b // generic input - || type_a == type_b //same type (is valid for triggers) + || type_a == type_b // same type (is valid for triggers) || (type_a == LiteGraph.EVENT && type_b == LiteGraph.ACTION) ) { return true; @@ -711,8 +701,8 @@ var supported_types_b = type_b.split(","); for (var i = 0; i < supported_types_a.length; ++i) { for (var j = 0; j < supported_types_b.length; ++j) { - if(this.isValidConnection(supported_types_a[i],supported_types_b[j])){ - //if (supported_types_a[i] == supported_types_b[j]) { + if(this.isValidConnection(supported_types_a[i],supported_types_b[j])) { + // if (supported_types_a[i] == supported_types_b[j]) { return true; } } @@ -733,7 +723,7 @@ this.searchbox_extras[description.toLowerCase()] = { type: node_type, desc: description, - data: data + data: data, }; }, @@ -744,65 +734,61 @@ * @param {String} type an string to know how to fetch it: "text","arraybuffer","json","blob" * @param {Function} on_complete callback(data) * @param {Function} on_error in case of an error - * @return {FileReader|Promise} returns the object used to + * @return {FileReader|Promise} returns the object used to */ - fetchFile: function( url, type, on_complete, on_error ) { - var that = this; - if(!url) - return null; + fetchFile: function( url, type, on_complete, on_error ) { + var that = this; + if(!url) + return null; - type = type || "text"; - if( url.constructor === String ) - { - if (url.substr(0, 4) == "http" && LiteGraph.proxy) { - url = LiteGraph.proxy + url.substr(url.indexOf(":") + 3); - } - return fetch(url) - .then(function(response) { - if(!response.ok) - throw new Error("File not found"); //it will be catch below - if(type == "arraybuffer") - return response.arrayBuffer(); - else if(type == "text" || type == "string") - return response.text(); - else if(type == "json") - return response.json(); - else if(type == "blob") - return response.blob(); - }) - .then(function(data) { - if(on_complete) - on_complete(data); - }) - .catch(function(error) { - console.error("error fetching file:",url); - if(on_error) - on_error(error); - }); - } - else if( url.constructor === File || url.constructor === Blob) - { - var reader = new FileReader(); - reader.onload = function(e) - { - var v = e.target.result; - if( type == "json" ) - v = JSON.parse(v); - if(on_complete) - on_complete(v); - } - if(type == "arraybuffer") - return reader.readAsArrayBuffer(url); - else if(type == "text" || type == "json") - return reader.readAsText(url); - else if(type == "blob") - return reader.readAsBinaryString(url); - } - return null; - } + type = type || "text"; + if( url.constructor === String ) { + if (url.substr(0, 4) == "http" && LiteGraph.proxy) { + url = LiteGraph.proxy + url.substr(url.indexOf(":") + 3); + } + return fetch(url) + .then(function(response) { + if(!response.ok) + throw new Error("File not found"); // it will be catch below + if(type == "arraybuffer") + return response.arrayBuffer(); + else if(type == "text" || type == "string") + return response.text(); + else if(type == "json") + return response.json(); + else if(type == "blob") + return response.blob(); + }) + .then(function(data) { + if(on_complete) + on_complete(data); + }) + .catch(function(error) { + console.error("error fetching file:",url); + if(on_error) + on_error(error); + }); + } else if( url.constructor === File || url.constructor === Blob) { + var reader = new FileReader(); + reader.onload = function(e) { + var v = e.target.result; + if( type == "json" ) + v = JSON.parse(v); + if(on_complete) + on_complete(v); + } + if(type == "arraybuffer") + return reader.readAsArrayBuffer(url); + else if(type == "text" || type == "json") + return reader.readAsText(url); + else if(type == "blob") + return reader.readAsBinaryString(url); + } + return null; + }, }); - //timer that works everywhere + // timer that works everywhere if (typeof performance != "undefined") { LiteGraph.getTime = performance.now.bind(performance); } else if (typeof Date != "undefined" && Date.now) { @@ -818,9 +804,9 @@ }; } - //********************************************************************************* + //* ******************************************************************************** // LGraph CLASS - //********************************************************************************* + //* ******************************************************************************** /** * LGraph is the class that contain a full graph. We instantiate one and add nodes to it, and then we can run the execution loop. @@ -848,10 +834,10 @@ global.LGraph = LiteGraph.LGraph = LGraph; - //default supported types + // 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) + // 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; }; @@ -871,9 +857,9 @@ this.last_node_id = 0; this.last_link_id = 0; - this._version = -1; //used to detect changes + this._version = -1; // used to detect changes - //safe clear + // safe clear if (this._nodes) { for (var i = 0; i < this._nodes.length; ++i) { var node = this._nodes[i]; @@ -883,27 +869,27 @@ } } - //nodes + // nodes this._nodes = []; this._nodes_by_id = {}; - this._nodes_in_order = []; //nodes sorted in execution order - this._nodes_executable = null; //nodes that contain onExecute sorted in execution order + this._nodes_in_order = []; // nodes sorted in execution order + this._nodes_executable = null; // nodes that contain onExecute sorted in execution order - //other scene stuff + // other scene stuff this._groups = []; - //links - this.links = {}; //container with all the links + // links + this.links = {}; // container with all the links - //iterations + // iterations this.iteration = 0; - //custom data + // custom data this.config = {}; - this.vars = {}; - this.extra = {}; //to store custom data + this.vars = {}; + this.extra = {}; // to store custom data - //timing + // timing this.globaltime = 0; this.runningtime = 0; this.fixedtime = 0; @@ -917,12 +903,12 @@ this.nodes_executing = []; this.nodes_actioning = []; this.nodes_executedAction = []; - - //subgraph_data + + // subgraph_data this.inputs = {}; this.outputs = {}; - //notify canvas to redraw + // notify canvas to redraw this.change(); this.sendActionToCanvas("clear"); @@ -986,35 +972,35 @@ this.sendEventToAllNodes("onStart"); - //launch + // launch this.starttime = LiteGraph.getTime(); this.last_update_time = this.starttime; interval = interval || 0; var that = this; - //execute once per frame + // execute once per frame if ( interval == 0 && typeof window != "undefined" && window.requestAnimationFrame ) { function on_frame() { if (that.execution_timer_id != -1) { return; } window.requestAnimationFrame(on_frame); - if(that.onBeforeStep) - that.onBeforeStep(); + if(that.onBeforeStep) + that.onBeforeStep(); that.runStep(1, !that.catch_errors); - if(that.onAfterStep) - that.onAfterStep(); + if(that.onAfterStep) + that.onAfterStep(); } this.execution_timer_id = -1; on_frame(); - } else { //execute every 'interval' ms + } else { // execute every 'interval' ms this.execution_timer_id = setInterval(function() { - //execute - if(that.onBeforeStep) - that.onBeforeStep(); + // execute + if(that.onBeforeStep) + that.onBeforeStep(); that.runStep(1, !that.catch_errors); - if(that.onAfterStep) - that.onAfterStep(); + if(that.onAfterStep) + that.onAfterStep(); }, interval); } }; @@ -1049,7 +1035,7 @@ * Run N steps (cycles) of the graph * @method runStep * @param {number} num number of steps to run, default is 1 - * @param {Boolean} do_not_catch_errors [optional] if you want to try/catch errors + * @param {Boolean} do_not_catch_errors [optional] if you want to try/catch errors * @param {number} limit max number of nodes to execute (used to execute from start to a node) */ @@ -1059,10 +1045,10 @@ var start = LiteGraph.getTime(); this.globaltime = 0.001 * (start - this.starttime); - //not optimal: executes possible pending actions in node, problem is it is not optimized - //it is done here as if it was done in the later loop it wont be called in the node missed the onExecute - - //from now on it will iterate only on executable nodes which is faster + // not optimal: executes possible pending actions in node, problem is it is not optimized + // it is done here as if it was done in the later loop it wont be called in the node missed the onExecute + + // from now on it will iterate only on executable nodes which is faster var nodes = this._nodes_executable ? this._nodes_executable : this._nodes; @@ -1070,18 +1056,18 @@ return; } - limit = limit || nodes.length; + limit = limit || nodes.length; if (do_not_catch_errors) { - //iterations + // iterations for (var i = 0; i < num; i++) { for (var j = 0; j < limit; ++j) { var node = nodes[j]; if(LiteGraph.use_deferred_actions && node._waiting_actions && node._waiting_actions.length) node.executePendingActions(); if (node.mode == LiteGraph.ALWAYS && node.onExecute) { - //wrap node.onExecute(); - node.doExecute(); + // wrap node.onExecute(); + node.doExecute(); } } @@ -1094,9 +1080,9 @@ if (this.onAfterExecute) { this.onAfterExecute(); } - } else { //catch errors + } else { // catch errors try { - //iterations + // iterations for (var i = 0; i < num; i++) { for (var j = 0; j < limit; ++j) { var node = nodes[j]; @@ -1159,27 +1145,27 @@ } }; - //This is more internal, it computes the executable nodes in order and returns it + // This is more internal, it computes the executable nodes in order and returns it LGraph.prototype.computeExecutionOrder = function( only_onExecute, - set_level + set_level, ) { var L = []; var S = []; var M = {}; - var visited_links = {}; //to avoid repeating links - var remaining_links = {}; //to a + var visited_links = {}; // to avoid repeating links + var remaining_links = {}; // to a - //search for the nodes without inputs (starting nodes) + // search for the nodes without inputs (starting nodes) for (var i = 0, l = this._nodes.length; i < l; ++i) { var node = this._nodes[i]; if (only_onExecute && !node.onExecute) { continue; } - M[node.id] = node; //add to pending nodes + M[node.id] = node; // add to pending nodes - var num = 0; //num of input connections + var num = 0; // num of input connections if (node.inputs) { for (var j = 0, l2 = node.inputs.length; j < l2; j++) { if (node.inputs[j] && node.inputs[j].link != null) { @@ -1189,13 +1175,12 @@ } if (num == 0) { - //is a starting node + // is a starting node S.push(node); if (set_level) { node._level = 1; } - } //num of input links - else { + } else { // num of input links if (set_level) { node._level = 0; } @@ -1203,24 +1188,21 @@ } } - while (true) { - if (S.length == 0) { - break; - } + while (S.length != 0) { - //get an starting node + // get an starting node var node = S.shift(); - L.push(node); //add to ordered list - delete M[node.id]; //remove from the pending nodes + L.push(node); // add to ordered list + delete M[node.id]; // remove from the pending nodes if (!node.outputs) { continue; } - //for every output + // for every output for (var i = 0; i < node.outputs.length; i++) { var output = node.outputs[i]; - //not connected + // not connected if ( output == null || output.links == null || @@ -1229,7 +1211,7 @@ continue; } - //for every connection + // for every connection for (var j = 0; j < output.links.length; j++) { var link_id = output.links[j]; var link = this.links[link_id]; @@ -1237,7 +1219,7 @@ continue; } - //already visited link (ignore it) + // already visited link (ignore it) if (visited_links[link.id]) { continue; } @@ -1256,16 +1238,16 @@ target_node._level = node._level + 1; } - visited_links[link.id] = true; //mark as visited - remaining_links[target_node.id] -= 1; //reduce the number of links remaining + visited_links[link.id] = true; // mark as visited + remaining_links[target_node.id] -= 1; // reduce the number of links remaining if (remaining_links[target_node.id] == 0) { S.push(target_node); - } //if no more links, then add to starters array + } // if no more links, then add to starters array } } } - //the remaining ones (loops) + // the remaining ones (loops) for (var i in M) { L.push(M[i]); } @@ -1276,23 +1258,23 @@ var l = L.length; - //save order number in the node + // save order number in the node for (var i = 0; i < l; ++i) { L[i].order = i; } - //sort now by priority + // sort now by priority L = L.sort(function(A, B) { var Ap = A.constructor.priority || A.priority || 0; var Bp = B.constructor.priority || B.priority || 0; if (Ap == Bp) { - //if same priority, sort by order + // if same priority, sort by order return A.order - B.order; } - return Ap - Bp; //sort by priority + return Ap - Bp; // sort by priority }); - //save order number in the node, again... + // save order number in the node, again... for (var i = 0; i < l; ++i) { L[i].order = i; } @@ -1473,7 +1455,7 @@ return; } - //groups + // groups if (node.constructor === LGraphGroup) { this._groups.push(node); this.setDirtyCanvas(true); @@ -1483,15 +1465,12 @@ return; } - //nodes + // nodes if (node.id != -1 && this._nodes_by_id[node.id] != null) { - console.warn( - "LiteGraph: there is already a node with this ID, changing it" - ); + console.warn("LiteGraph: there is already a node with this ID, changing it"); if (LiteGraph.use_uuids) { node.id = LiteGraph.uuidv4(); - } - else { + } else { node.id = ++this.last_node_id; } } @@ -1500,12 +1479,11 @@ throw "LiteGraph: max number of nodes in a graph reached"; } - //give him an id + // give him an id if (LiteGraph.use_uuids) { if (node.id == null || node.id == -1) node.id = LiteGraph.uuidv4(); - } - else { + } else { if (node.id == null || node.id == -1) { node.id = ++this.last_node_id; } else if (this.last_node_id < node.id) { @@ -1538,7 +1516,7 @@ this.setDirtyCanvas(true); this.change(); - return node; //to chain actions + return node; // to chain actions }; /** @@ -1562,15 +1540,15 @@ if (this._nodes_by_id[node.id] == null) { return; - } //not found + } // not found if (node.ignore_remove) { return; - } //cannot be removed + } // cannot be removed - this.beforeChange(); //sure? - almost sure is wrong + this.beforeChange(); // sure? - almost sure is wrong - //disconnect inputs + // disconnect inputs if (node.inputs) { for (var i = 0; i < node.inputs.length; i++) { var slot = node.inputs[i]; @@ -1580,7 +1558,7 @@ } } - //disconnect outputs + // disconnect outputs if (node.outputs) { for (var i = 0; i < node.outputs.length; i++) { var slot = node.outputs[i]; @@ -1590,9 +1568,9 @@ } } - //node.id = -1; //why? + // node.id = -1; //why? - //callback + // callback if (node.onRemoved) { node.onRemoved(); } @@ -1600,7 +1578,7 @@ node.graph = null; this._version++; - //remove from canvas render + // remove from canvas render if (this.list_of_graphcanvas) { for (var i = 0; i < this.list_of_graphcanvas.length; ++i) { var canvas = this.list_of_graphcanvas[i]; @@ -1613,7 +1591,7 @@ } } - //remove from containers + // remove from containers var pos = this._nodes.indexOf(node); if (pos != -1) { this._nodes.splice(pos, 1); @@ -1624,11 +1602,11 @@ this.onNodeRemoved(node); } - //close panels - this.sendActionToCanvas("checkPanels"); + // close panels + this.sendActionToCanvas("checkPanels"); this.setDirtyCanvas(true, true); - this.afterChange(); //sure? - almost sure is wrong + this.afterChange(); // sure? - almost sure is wrong this.change(); this.updateExecutionOrder(); @@ -1723,16 +1701,16 @@ */ LGraph.prototype.getNodeOnPos = function(x, y, nodes_list, margin) { nodes_list = nodes_list || this._nodes; - var nRet = null; + var nRet = null; for (var i = nodes_list.length - 1; i >= 0; i--) { var n = nodes_list[i]; if (n.isPointInside(x, y, margin)) { // check for lesser interest nodes (TODO check for overlapping, use the top) - /*if (typeof n == "LGraphGroup"){ + /* if (typeof n == "LGraphGroup"){ nRet = n; }else{*/ - return n; - /*}*/ + return n; + /* }*/ } } return nRet; @@ -1790,14 +1768,14 @@ LGraph.prototype.onAction = function(action, param, options) { this._input_nodes = this.findNodesByClass( LiteGraph.GraphInput, - this._input_nodes + this._input_nodes, ); for (var i = 0; i < this._input_nodes.length; ++i) { var node = this._input_nodes[i]; if (node.properties.name != action) { continue; } - //wrap node.onAction(action, param); + // wrap node.onAction(action, param); node.actionDo(action, param, options); break; } @@ -1819,14 +1797,14 @@ LGraph.prototype.addInput = function(name, type, value) { var input = this.inputs[name]; if (input) { - //already exist + // already exist return; } - this.beforeChange(); + this.beforeChange(); this.inputs[name] = { name: name, type: type, value: value }; this._version++; - this.afterChange(); + this.afterChange(); if (this.onInputAdded) { this.onInputAdded(name, type); @@ -2087,7 +2065,7 @@ } }; - //used for undo, called before any change is made to the graph + // used for undo, called before any change is made to the graph LGraph.prototype.beforeChange = function(info) { if (this.onBeforeChange) { this.onBeforeChange(this,info); @@ -2095,7 +2073,7 @@ this.sendActionToCanvas("onBeforeChange", this); }; - //used to resend actions, called after any change is made to the graph + // used to resend actions, called after any change is made to the graph LGraph.prototype.afterChange = function(info) { if (this.onAfterChange) { this.onAfterChange(this,info); @@ -2178,7 +2156,7 @@ } }; - //save and recover app state *************************************** + // save and recover app state *************************************** /** * Creates a Object containing all the info about this graph, it can be serialized * @method serialize @@ -2190,18 +2168,16 @@ nodes_info.push(this._nodes[i].serialize()); } - //pack link info into a non-verbose format + // pack link info into a non-verbose format var links = []; for (var i in this.links) { - //links is an OBJECT + // links is an OBJECT var link = this.links[i]; if (!link.serialize) { - //weird bug I havent solved yet - console.warn( - "weird LLink bug, link info is not a LLink but a regular object" - ); + // weird bug I havent solved yet + console.warn("weird LLink bug, link info is not a LLink but a regular object"); var link2 = new LLink(); - for (var j in link) { + for (var j in link) { link2[j] = link[j]; } this.links[i] = link2; @@ -2223,12 +2199,12 @@ links: links, groups: groups_info, config: this.config, - extra: this.extra, - version: LiteGraph.VERSION + extra: this.extra, + version: LiteGraph.VERSION, }; - if(this.onSerialize) - this.onSerialize(data); + if(this.onSerialize) + this.onSerialize(data); return data; }; @@ -2250,16 +2226,15 @@ var nodes = data.nodes; - //decode links info (they are very verbose) + // decode links info (they are very verbose) if (data.links && data.links.constructor === Array) { var links = []; for (var i = 0; i < data.links.length; ++i) { var link_data = data.links[i]; - if(!link_data) //weird bug - { - console.warn("serialized graph link data contains errors, skipping."); - continue; - } + if(!link_data) { // weird bug + console.warn("serialized graph link data contains errors, skipping."); + continue; + } var link = new LLink(); link.configure(link_data); links[link.id] = link; @@ -2267,41 +2242,39 @@ data.links = links; } - //copy all stored fields + // copy all stored fields for (var i in data) { - if(i == "nodes" || i == "groups" ) //links must be accepted - continue; + if(i == "nodes" || i == "groups" ) // links must be accepted + continue; this[i] = data[i]; } var error = false; - //create nodes + // create nodes this._nodes = []; if (nodes) { for (var i = 0, l = nodes.length; i < l; ++i) { - var n_info = nodes[i]; //stored info + var n_info = nodes[i]; // stored info var node = LiteGraph.createNode(n_info.type, n_info.title); if (!node) { if (LiteGraph.debug) { - console.log( - "Node not found or has errors: " + n_info.type - ); + console.log("Node not found or has errors: " + n_info.type); } - //in case of error we create a replacement node to avoid losing info + // in case of error we create a replacement node to avoid losing info node = new LGraphNode(); node.last_serialization = n_info; node.has_errors = true; error = true; - //continue; + // continue; } - node.id = n_info.id; //id it or it will create a new id - this.add(node, true); //add before configure, otherwise configure cannot create links + node.id = n_info.id; // id it or it will create a new id + this.add(node, true); // add before configure, otherwise configure cannot create links } - //configure nodes afterwards so they can reach each other + // configure nodes afterwards so they can reach each other for (var i = 0, l = nodes.length; i < l; ++i) { var n_info = nodes[i]; var node = this.getNodeById(n_info.id); @@ -2311,7 +2284,7 @@ } } - //groups + // groups this._groups.length = 0; if (data.groups) { for (var i = 0; i < data.groups.length; ++i) { @@ -2323,10 +2296,10 @@ this.updateExecutionOrder(); - this.extra = data.extra || {}; + this.extra = data.extra || {}; - if(this.onConfigure) - this.onConfigure(data); + if(this.onConfigure) + this.onConfigure(data); this._version++; this.setDirtyCanvas(true, true); @@ -2336,22 +2309,21 @@ LGraph.prototype.load = function(url, callback) { var that = this; - //from file - if(url.constructor === File || url.constructor === Blob) - { - var reader = new FileReader(); - reader.addEventListener('load', function(event) { - var data = JSON.parse(event.target.result); - that.configure(data); - if(callback) - callback(); - }); - - reader.readAsText(url); - return; - } + // from file + if(url.constructor === File || url.constructor === Blob) { + var reader = new FileReader(); + reader.addEventListener('load', function(event) { + var data = JSON.parse(event.target.result); + that.configure(data); + if(callback) + callback(); + }); + + reader.readAsText(url); + return; + } - //is a string, then an URL + // is a string, then an URL var req = new XMLHttpRequest(); req.open("GET", url, true); req.send(null); @@ -2362,8 +2334,8 @@ } var data = JSON.parse( req.response ); that.configure(data); - if(callback) - callback(); + if(callback) + callback(); }; req.onerror = function(err) { console.error("Error loading graph:", err); @@ -2371,10 +2343,10 @@ }; LGraph.prototype.onNodeTrace = function(node, msg, color) { - //TODO + // TODO }; - //this is the class in charge of storing link information + // this is the class in charge of storing link information function LLink(id, type, origin_id, origin_slot, target_id, target_slot) { this.id = id; this.type = type; @@ -2384,7 +2356,7 @@ this.target_slot = target_slot; this._data = null; - this._pos = new Float32Array(2); //center + this._pos = new Float32Array(2); // center } LLink.prototype.configure = function(o) { @@ -2412,7 +2384,7 @@ this.origin_slot, this.target_id, this.target_slot, - this.type + this.type, ]; }; @@ -2437,7 +2409,7 @@ + resizable: if set to false it wont be resizable with the mouse + horizontal: slots are distributed horizontally + widgets_start_y: widgets start at y distance from the top of the node - + flags object: + collapsed: if it is collapsed @@ -2503,25 +2475,24 @@ get: function() { return this._pos; }, - enumerable: true + enumerable: true, }); if (LiteGraph.use_uuids) { this.id = LiteGraph.uuidv4(); - } - else { - this.id = -1; //not know till not added + } else { + this.id = -1; // not know till not added } this.type = null; - //inputs available: array of inputs + // inputs available: array of inputs this.inputs = []; this.outputs = []; this.connections = []; - //local data - this.properties = {}; //for the values - this.properties_info = []; //for the info + // local data + this.properties = {}; // for the values + this.properties_info = []; // for the info this.flags = {}; }; @@ -2536,7 +2507,7 @@ } for (var j in info) { if (j == "properties") { - //i don't want to clone properties, I want to reuse the old container + // i don't want to clone properties, I want to reuse the old container for (var k in info.properties) { this.properties[k] = info.properties[k]; if (this.onPropertyChanged) { @@ -2549,14 +2520,13 @@ if (info[j] == null) { continue; } else if (typeof info[j] == "object") { - //object + // object if (this[j] && this[j].configure) { this[j].configure(info[j]); } else { this[j] = LiteGraph.cloneObject(info[j], this[j]); } - } //value - else { + } else { // value this[j] = info[j]; } } @@ -2565,54 +2535,52 @@ this.title = this.constructor.title; } - if (this.inputs) { - for (var i = 0; i < this.inputs.length; ++i) { - var input = this.inputs[i]; - var link_info = this.graph ? this.graph.links[input.link] : null; - if (this.onConnectionsChange) - this.onConnectionsChange( LiteGraph.INPUT, i, true, link_info, input ); //link_info has been created now, so its updated + if (this.inputs) { + for (var i = 0; i < this.inputs.length; ++i) { + var input = this.inputs[i]; + var link_info = this.graph ? this.graph.links[input.link] : null; + if (this.onConnectionsChange) + this.onConnectionsChange( LiteGraph.INPUT, i, true, link_info, input ); // link_info has been created now, so its updated + + if( this.onInputAdded ) + this.onInputAdded(input); - if( this.onInputAdded ) - this.onInputAdded(input); + } + } - } - } + if (this.outputs) { + for (var i = 0; i < this.outputs.length; ++i) { + var output = this.outputs[i]; + if (!output.links) { + continue; + } + for (var j = 0; j < output.links.length; ++j) { + var link_info = this.graph ? this.graph.links[output.links[j]] : null; + if (this.onConnectionsChange) + this.onConnectionsChange( LiteGraph.OUTPUT, i, true, link_info, output ); // link_info has been created now, so its updated + } - if (this.outputs) { - for (var i = 0; i < this.outputs.length; ++i) { - var output = this.outputs[i]; - if (!output.links) { - continue; - } - for (var j = 0; j < output.links.length; ++j) { - var link_info = this.graph ? this.graph.links[output.links[j]] : null; - if (this.onConnectionsChange) - this.onConnectionsChange( LiteGraph.OUTPUT, i, true, link_info, output ); //link_info has been created now, so its updated - } - - if( this.onOutputAdded ) - this.onOutputAdded(output); - } + if( this.onOutputAdded ) + this.onOutputAdded(output); + } } - if( this.widgets ) - { - for (var i = 0; i < this.widgets.length; ++i) - { - var w = this.widgets[i]; - if(!w) - continue; - if(w.options && w.options.property && (this.properties[ w.options.property ] != undefined)) - w.value = JSON.parse( JSON.stringify( this.properties[ w.options.property ] ) ); - } - if (info.widgets_values) { - for (var i = 0; i < info.widgets_values.length; ++i) { - if (this.widgets[i]) { - this.widgets[i].value = info.widgets_values[i]; - } - } - } - } + if( this.widgets ) { + for (var i = 0; i < this.widgets.length; ++i) { + var w = this.widgets[i]; + if(!w) + continue; + if(w.options && w.options.property && (this.properties[w.options.property] != undefined)) + w.value = JSON.parse( JSON.stringify( this.properties[w.options.property] ) ); + } + if (info.widgets_values) { + for (var i = 0; i < info.widgets_values.length; ++i) { + if (this.widgets[i]) { + this.widgets[i].value = info.widgets_values[i]; + } + } + } + } if (this.onConfigure) { this.onConfigure(info); @@ -2625,18 +2593,18 @@ */ LGraphNode.prototype.serialize = function() { - //create serialization object + // create serialization object var o = { id: this.id, type: this.type, pos: this.pos, size: this.size, flags: LiteGraph.cloneObject(this.flags), - order: this.order, - mode: this.mode + order: this.order, + mode: this.mode, }; - //special case for when there were errors + // special case for when there were errors if (this.constructor === LGraphNode && this.last_serialization) { return this.last_serialization; } @@ -2646,7 +2614,7 @@ } if (this.outputs) { - //clear outputs last data (because data in connections is never serialized but stored inside the outputs info) + // clear outputs last data (because data in connections is never serialized but stored inside the outputs info) for (var i = 0; i < this.outputs.length; i++) { delete this.outputs[i]._data; } @@ -2664,10 +2632,10 @@ if (this.widgets && this.serialize_widgets) { o.widgets_values = []; for (var i = 0; i < this.widgets.length; ++i) { - if(this.widgets[i]) - o.widgets_values[i] = this.widgets[i].value; - else - o.widgets_values[i] = null; + if(this.widgets[i]) + o.widgets_values[i] = this.widgets[i].value; + else + o.widgets_values[i] = null; } } @@ -2690,9 +2658,7 @@ if (this.onSerialize) { if (this.onSerialize(o)) { - console.warn( - "node onSerialize shouldnt return anything, data should be stored in the object pass in the first parameter" - ); + console.warn("node onSerialize shouldnt return anything, data should be stored in the object pass in the first parameter"); } } @@ -2706,10 +2672,10 @@ return null; } - //we clone it because serialize returns shared containers + // we clone it because serialize returns shared containers var data = LiteGraph.cloneObject(this.serialize()); - //remove links + // remove links if (data.inputs) { for (var i = 0; i < data.inputs.length; ++i) { data.inputs[i].link = null; @@ -2730,7 +2696,7 @@ data["id"] = LiteGraph.uuidv4() } - //remove links + // remove links node.configure(data); return node; @@ -2744,7 +2710,7 @@ LGraphNode.prototype.toString = function() { return JSON.stringify(this.serialize()); }; - //LGraphNode.prototype.deserialize = function(info) {} //this cannot be done from within, must be done in LiteGraph + // LGraphNode.prototype.deserialize = function(info) {} //this cannot be done from within, must be done in LiteGraph /** * get the title string @@ -2765,26 +2731,24 @@ if (!this.properties) { this.properties = {}; } - if( value === this.properties[name] ) - return; - var prev_value = this.properties[name]; + if( value === this.properties[name] ) + return; + var prev_value = this.properties[name]; this.properties[name] = value; if (this.onPropertyChanged) { - if( this.onPropertyChanged(name, value, prev_value) === false ) //abort change - this.properties[name] = prev_value; + if( this.onPropertyChanged(name, value, prev_value) === false ) // abort change + this.properties[name] = prev_value; } - if(this.widgets) //widgets could be linked to properties - for(var i = 0; i < this.widgets.length; ++i) - { - var w = this.widgets[i]; - if(!w) - continue; - if(w.options.property == name) - { - w.value = value; - break; - } - } + if(this.widgets) // widgets could be linked to properties + for(var i = 0; i < this.widgets.length; ++i) { + var w = this.widgets[i]; + if(!w) + continue; + if(w.options.property == name) { + w.value = value; + break; + } + } }; // Execution ************************* @@ -2799,8 +2763,8 @@ return; } - //this maybe slow and a niche case - //if(slot && slot.constructor === String) + // this maybe slow and a niche case + // if(slot && slot.constructor === String) // slot = this.findOutputSlot(slot); if (slot == -1 || slot >= this.outputs.length) { @@ -2812,16 +2776,16 @@ return; } - //store data in the output itself in case we want to debug + // store data in the output itself in case we want to debug output_info._data = data; - //if there are connections, pass the data to the connections + // if there are connections, pass the data to the connections if (this.outputs[slot].links) { for (var i = 0; i < this.outputs[slot].links.length; i++) { var link_id = this.outputs[slot].links[i]; - var link = this.graph.links[link_id]; - if(link) - link.data = data; + var link = this.graph.links[link_id]; + if(link) + link.data = data; } } }; @@ -2843,10 +2807,10 @@ if (!output_info) { return; } - //store data in the output itself in case we want to debug + // store data in the output itself in case we want to debug output_info.type = type; - //if there are connections, pass the data to the connections + // if there are connections, pass the data to the connections if (this.outputs[slot].links) { for (var i = 0; i < this.outputs[slot].links.length; i++) { var link_id = this.outputs[slot].links[i]; @@ -2865,7 +2829,7 @@ LGraphNode.prototype.getInputData = function(slot, force_update) { if (!this.inputs) { return; - } //undefined; + } // undefined; if (slot >= this.inputs.length || this.inputs[slot].link == null) { return; @@ -2874,7 +2838,7 @@ var link_id = this.inputs[slot].link; var link = this.graph.links[link_id]; if (!link) { - //bug: weird case but it happens sometimes + // bug: weird case but it happens sometimes return null; } @@ -2882,7 +2846,7 @@ return link.data; } - //special case: used to extract data from the incoming connection before the graph has been executed + // special case: used to extract data from the incoming connection before the graph has been executed var node = this.graph.getNodeById(link.origin_id); if (!node) { return link.data; @@ -2906,7 +2870,7 @@ LGraphNode.prototype.getInputDataType = function(slot) { if (!this.inputs) { return null; - } //undefined; + } // undefined; if (slot >= this.inputs.length || this.inputs[slot].link == null) { return null; @@ -2914,7 +2878,7 @@ var link_id = this.inputs[slot].link; var link = this.graph.links[link_id]; if (!link) { - //bug: weird case but it happens sometimes + // bug: weird case but it happens sometimes return null; } var node = this.graph.getNodeById(link.origin_id); @@ -2937,7 +2901,7 @@ */ LGraphNode.prototype.getInputDataByName = function( slot_name, - force_update + force_update, ) { var slot = this.findInputSlot(slot_name); if (slot == -1) { @@ -2987,7 +2951,7 @@ } if (slot < this.inputs.length) { var slot_info = this.inputs[slot]; - return this.graph.links[ slot_info.link ]; + return this.graph.links[slot_info.link]; } return null; }; @@ -3141,59 +3105,58 @@ return r; }; - LGraphNode.prototype.addOnTriggerInput = function(){ + LGraphNode.prototype.addOnTriggerInput = function() { var trigS = this.findInputSlot("onTrigger"); - if (trigS == -1){ //!trigS || + if (trigS == -1) { // !trigS || var input = this.addInput("onTrigger", LiteGraph.EVENT, {optional: true, nameLocked: true}); return this.findInputSlot("onTrigger"); } return trigS; } - - LGraphNode.prototype.addOnExecutedOutput = function(){ + + LGraphNode.prototype.addOnExecutedOutput = function() { var trigS = this.findOutputSlot("onExecuted"); - if (trigS == -1){ //!trigS || + if (trigS == -1) { // !trigS || var output = this.addOutput("onExecuted", LiteGraph.ACTION, {optional: true, nameLocked: true}); return this.findOutputSlot("onExecuted"); } return trigS; } - - LGraphNode.prototype.onAfterExecuteNode = function(param, options){ + + LGraphNode.prototype.onAfterExecuteNode = function(param, options) { var trigS = this.findOutputSlot("onExecuted"); - if (trigS != -1){ - - //console.debug(this.id+":"+this.order+" triggering slot onAfterExecute"); - //console.debug(param); - //console.debug(options); + if (trigS != -1) { + + // console.debug(this.id+":"+this.order+" triggering slot onAfterExecute"); + // console.debug(param); + // console.debug(options); this.triggerSlot(trigS, param, null, options); - + } - } - - LGraphNode.prototype.changeMode = function(modeTo){ - switch(modeTo){ + } + + LGraphNode.prototype.changeMode = function(modeTo) { + switch(modeTo) { case LiteGraph.ON_EVENT: // this.addOnExecutedOutput(); break; - + case LiteGraph.ON_TRIGGER: this.addOnTriggerInput(); this.addOnExecutedOutput(); break; - + case LiteGraph.NEVER: break; - + case LiteGraph.ALWAYS: break; - + case LiteGraph.ON_REQUEST: break; - + default: return false; - break; } this.mode = modeTo; return true; @@ -3202,19 +3165,18 @@ /** * Triggers the execution of actions that were deferred when the action was triggered * @method executePendingActions - */ + */ LGraphNode.prototype.executePendingActions = function() { if(!this._waiting_actions || !this._waiting_actions.length) return; - for(var i = 0; i < this._waiting_actions.length;++i) - { + for(var i = 0; i < this._waiting_actions.length;++i) { var p = this._waiting_actions[i]; this.onAction(p[0],p[1],p[2],p[3],p[4]); - } + } this._waiting_actions.length = 0; } - + /** * Triggers the node code execution, place a boolean/counter to mark the node as being executed * @method doExecute @@ -3223,30 +3185,28 @@ */ LGraphNode.prototype.doExecute = function(param, options) { options = options || {}; - if (this.onExecute){ - + if (this.onExecute) { + // enable this to give the event an ID - if (!options.action_call) options.action_call = this.id+"_exec_"+Math.floor(Math.random()*9999); - - this.graph.nodes_executing[this.id] = true; //.push(this.id); + if (!options.action_call) options.action_call = this.id+"_exec_"+Math.floor(Math.random()*9999); + + this.graph.nodes_executing[this.id] = true; // .push(this.id); this.onExecute(param, options); - - this.graph.nodes_executing[this.id] = false; //.pop(); - + + this.graph.nodes_executing[this.id] = false; // .pop(); + // save execution/action ref this.exec_version = this.graph.iteration; - if(options && options.action_call){ + if(options && options.action_call) { this.action_call = options.action_call; // if (param) this.graph.nodes_executedAction[this.id] = options.action_call; } } - else { - } this.execute_triggered = 2; // the nFrames it will be used (-- each step), means "how old" is the event if(this.onAfterExecuteNode) this.onAfterExecuteNode(param, options); // callback }; - + /** * Triggers an action, wrapped by logics to control execution flow * @method actionDo @@ -3255,19 +3215,19 @@ */ LGraphNode.prototype.actionDo = function(action, param, options, action_slot ) { options = options || {}; - if (this.onAction){ - - // enable this to give the event an ID + if (this.onAction) { + + // enable this to give the event an ID if (!options.action_call) options.action_call = this.id+"_"+(action?action:"action")+"_"+Math.floor(Math.random()*9999); - - this.graph.nodes_actioning[this.id] = (action?action:"actioning"); //.push(this.id); - + + this.graph.nodes_actioning[this.id] = (action?action:"actioning"); // .push(this.id); + this.onAction(action, param, options, action_slot); - - this.graph.nodes_actioning[this.id] = false; //.pop(); - + + this.graph.nodes_actioning[this.id] = false; // .pop(); + // save execution/action ref - if(options && options.action_call){ + if(options && options.action_call) { this.action_call = options.action_call; // if (param) this.graph.nodes_executedAction[this.id] = options.action_call; } @@ -3275,7 +3235,7 @@ this.action_triggered = 2; // the nFrames it will be used (-- each step), means "how old" is the event if(this.onAfterExecuteNode) this.onAfterExecuteNode(param, options); }; - + /** * Triggers an event in this node, this will trigger any output with the same name * @method trigger @@ -3311,14 +3271,13 @@ return; } - if(slot == null) - { - console.error("slot must be a number"); - return; - } + if(slot == null) { + console.error("slot must be a number"); + return; + } - if(slot.constructor !== Number) - console.warn("slot must be a number, use node.trigger('name') if you want to use a string"); + if(slot.constructor !== Number) + console.warn("slot must be a number, use node.trigger('name') if you want to use a string"); var output = this.outputs[slot]; if (!output) { @@ -3334,52 +3293,47 @@ this.graph._last_trigger_time = LiteGraph.getTime(); } - //for every link attached here + // for every link attached here for (var k = 0; k < links.length; ++k) { var id = links[k]; if (link_id != null && link_id != id) { - //to skip links + // to skip links continue; } var link_info = this.graph.links[links[k]]; if (!link_info) { - //not connected + // not connected continue; } link_info._last_time = LiteGraph.getTime(); var node = this.graph.getNodeById(link_info.target_id); if (!node) { - //node not found? + // node not found? continue; } - //used to mark events in graph + // used to mark events in graph var target_connection = node.inputs[link_info.target_slot]; - if (node.mode === LiteGraph.ON_TRIGGER) - { - // generate unique trigger ID if not present - if (!options.action_call) options.action_call = this.id+"_trigg_"+Math.floor(Math.random()*9999); + if (node.mode === LiteGraph.ON_TRIGGER) { + // generate unique trigger ID if not present + if (!options.action_call) options.action_call = this.id+"_trigg_"+Math.floor(Math.random()*9999); if (node.onExecute) { // -- wrapping node.onExecute(param); -- node.doExecute(param, options); } - } - else if (node.onAction) { + } else if (node.onAction) { // generate unique action ID if not present - if (!options.action_call) options.action_call = this.id+"_act_"+Math.floor(Math.random()*9999); - //pass the action name + if (!options.action_call) options.action_call = this.id+"_act_"+Math.floor(Math.random()*9999); + // pass the action name var target_connection = node.inputs[link_info.target_slot]; - //instead of executing them now, it will be executed in the next graph loop, to ensure data flow - if(LiteGraph.use_deferred_actions && node.onExecute) - { + // instead of executing them now, it will be executed in the next graph loop, to ensure data flow + if(LiteGraph.use_deferred_actions && node.onExecute) { if(!node._waiting_actions) node._waiting_actions = []; node._waiting_actions.push([target_connection.name, param, options, link_info.target_slot]); - } - else - { + } else { // wrap node.onAction(target_connection.name, param); node.actionDo( target_connection.name, param, options, link_info.target_slot ); } @@ -3408,16 +3362,16 @@ return; } - //for every link attached here + // for every link attached here for (var k = 0; k < links.length; ++k) { var id = links[k]; if (link_id != null && link_id != id) { - //to skip links + // to skip links continue; } var link_info = this.graph.links[links[k]]; if (!link_info) { - //not connected + // not connected continue; } link_info._last_time = 0; @@ -3429,12 +3383,11 @@ * @method setSize * @param {vec2} size */ - LGraphNode.prototype.setSize = function(size) - { - this.size = size; - if(this.onResize) - this.onResize(this.size); - } + LGraphNode.prototype.setSize = function(size) { + this.size = size; + if(this.onResize) + this.onResize(this.size); + } /** * add a new property to this node @@ -3448,7 +3401,7 @@ name, default_value, type, - extra_info + extra_info, ) { var o = { name: name, type: type, default_value: default_value }; if (extra_info) { @@ -3467,7 +3420,7 @@ return o; }; - //connections + // connections /** * add a new output slot to use in this node @@ -3491,9 +3444,9 @@ if (this.onOutputAdded) { this.onOutputAdded(output); } - + if (LiteGraph.auto_load_slot_types) LiteGraph.registerNodeAndSlotType(this,type,true); - + this.setSize( this.computeSize() ); this.setDirtyCanvas(true, true); return output; @@ -3521,9 +3474,9 @@ if (this.onOutputAdded) { this.onOutputAdded(o); } - + if (LiteGraph.auto_load_slot_types) LiteGraph.registerNodeAndSlotType(this,info[1],true); - + } this.setSize( this.computeSize() ); @@ -3584,8 +3537,8 @@ if (this.onInputAdded) { this.onInputAdded(input); - } - + } + LiteGraph.registerNodeAndSlotType(this,type); this.setDirtyCanvas(true, true); @@ -3614,7 +3567,7 @@ if (this.onInputAdded) { this.onInputAdded(o); } - + LiteGraph.registerNodeAndSlotType(this,info[1]); } @@ -3661,7 +3614,7 @@ type: type, pos: pos, direction: direction, - links: null + links: null, }; this.connections.push(o); return o; @@ -3680,11 +3633,11 @@ var rows = Math.max( this.inputs ? this.inputs.length : 1, - this.outputs ? this.outputs.length : 1 + this.outputs ? this.outputs.length : 1, ); var size = out || new Float32Array([0, 0]); rows = Math.max(rows, 1); - var font_size = LiteGraph.NODE_TEXT_SIZE; //although it should be graphcanvas.inner_text_font size + var font_size = LiteGraph.NODE_TEXT_SIZE; // although it should be graphcanvas.inner_text_font size var title_width = compute_text_size(this.title); var input_width = 0; @@ -3731,7 +3684,7 @@ widgets_height += 8; } - //compute height using widgets height + // compute height using widgets height if( this.widgets_up ) size[1] = Math.max( size[1], widgets_height ); else if( this.widgets_start_y != null ) @@ -3753,7 +3706,7 @@ size[1] = this.constructor.min_height; } - size[1] += 6; //margin + size[1] += 6; // margin return size; }; @@ -3765,13 +3718,12 @@ * @param {String} property name of the property * @return {Object} the object with all the available info */ - LGraphNode.prototype.getPropertyInfo = function( property ) - { + LGraphNode.prototype.getPropertyInfo = function( property ) { var info = null; - //there are several ways to define info about a property - //legacy mode - if (this.properties_info) { + // there are several ways to define info about a property + // legacy mode + if (this.properties_info) { for (var i = 0; i < this.properties_info.length; ++i) { if (this.properties_info[i].name == property) { info = this.properties_info[i]; @@ -3779,27 +3731,27 @@ } } } - //litescene mode using the constructor - if(this.constructor["@" + property]) - info = this.constructor["@" + property]; + // litescene mode using the constructor + if(this.constructor["@" + property]) + info = this.constructor["@" + property]; - if(this.constructor.widgets_info && this.constructor.widgets_info[property]) - info = this.constructor.widgets_info[property]; + if(this.constructor.widgets_info && this.constructor.widgets_info[property]) + info = this.constructor.widgets_info[property]; - //litescene mode using the constructor - if (!info && this.onGetPropertyInfo) { + // litescene mode using the constructor + if (!info && this.onGetPropertyInfo) { info = this.onGetPropertyInfo(property); } if (!info) info = {}; - if(!info.type) - info.type = typeof this.properties[property]; - if(info.widget == "combo") - info.type = "enum"; + if(!info.type) + info.type = typeof this.properties[property]; + if(info.widget == "combo") + info.type = "enum"; - return info; - } + return info; + } /** * Defines a widget inside the node, it will be rendered on top of the node, you can control lots of properties @@ -3809,44 +3761,40 @@ * @param {String} name the text to show on the widget * @param {String} value the default value * @param {Function|String} callback function to call when it changes (optionally, it can be the name of the property to modify) - * @param {Object} options the object that contains special properties of this widget + * @param {Object} options the object that contains special properties of this widget * @return {Object} the created widget object */ - LGraphNode.prototype.addWidget = function( type, name, value, callback, options ) - { + LGraphNode.prototype.addWidget = function( type, name, value, callback, options ) { if (!this.widgets) { this.widgets = []; } - if(!options && callback && callback.constructor === Object) - { - options = callback; - callback = null; - } + if(!options && callback && callback.constructor === Object) { + options = callback; + callback = null; + } - if(options && options.constructor === String) //options can be the property name - options = { property: options }; + if(options && options.constructor === String) // options can be the property name + options = { property: options }; - if(callback && callback.constructor === String) //callback can be the property name - { - if(!options) - options = {}; - options.property = callback; - callback = null; - } + if(callback && callback.constructor === String) { // callback can be the property name + if(!options) + options = {}; + options.property = callback; + callback = null; + } - if(callback && callback.constructor !== Function) - { - console.warn("addWidget: callback must be a function"); - callback = null; - } + if(callback && callback.constructor !== Function) { + console.warn("addWidget: callback must be a function"); + callback = null; + } var w = { type: type.toLowerCase(), name: name, value: value, callback: callback, - options: options || {} + options: options || {}, }; if (w.options.y !== undefined) { @@ -3860,7 +3808,7 @@ throw "LiteGraph addWidget('combo',...) requires to pass values in options: { values:['red','blue'] }"; } this.widgets.push(w); - this.setSize( this.computeSize() ); + this.setSize( this.computeSize() ); return w; }; @@ -3884,13 +3832,13 @@ const nodePos = this.pos; const isCollapsed = this.flags.collapsed; const nodeSize = this.size; - + let left_offset = 0; // 1 offset due to how nodes are rendered - let right_offset = 1 ; + let right_offset = 1 ; let top_offset = 0; let bottom_offset = 0; - + if (compute_outer) { // 4 offset for collapsed node connection points left_offset = 4; @@ -3901,7 +3849,7 @@ // 5 offset for bottom shadow and collapsed node connection points bottom_offset = 5 + top_offset; } - + out[0] = nodePos[0] - left_offset; out[1] = nodePos[1] - LiteGraph.NODE_TITLE_HEIGHT - top_offset; out[2] = isCollapsed ? @@ -3932,7 +3880,7 @@ margin_top = 0; } if (this.flags && this.flags.collapsed) { - //if ( distance([x,y], [this.pos[0] + this.size[0]*0.5, this.pos[1] + this.size[1]*0.5]) < LiteGraph.NODE_COLLAPSED_RADIUS) + // if ( distance([x,y], [this.pos[0] + this.size[0]*0.5, this.pos[1] + this.size[1]*0.5]) < LiteGraph.NODE_COLLAPSED_RADIUS) if ( isInsideRectangle( x, @@ -3941,7 +3889,7 @@ this.pos[1] - LiteGraph.NODE_TITLE_HEIGHT - margin, (this._collapsed_width || LiteGraph.NODE_COLLAPSED_WIDTH) + 2 * margin, - LiteGraph.NODE_TITLE_HEIGHT + 2 * margin + LiteGraph.NODE_TITLE_HEIGHT + 2 * margin, ) ) { return true; @@ -3965,7 +3913,7 @@ * @return {Object} if found the object contains { input|output: slot object, slot: number, link_pos: [x,y] } */ LGraphNode.prototype.getSlotInPosition = function(x, y) { - //search for inputs + // search for inputs var link_pos = new Float32Array(2); if (this.inputs) { for (var i = 0, l = this.inputs.length; i < l; ++i) { @@ -3978,7 +3926,7 @@ link_pos[0] - 10, link_pos[1] - 5, 20, - 10 + 10, ) ) { return { input: input, slot: i, link_pos: link_pos }; @@ -3997,7 +3945,7 @@ link_pos[0] - 10, link_pos[1] - 5, 20, - 10 + 10, ) ) { return { output: output, slot: i, link_pos: link_pos }; @@ -4015,7 +3963,7 @@ * @param {boolean} returnObj if the obj itself wanted * @return {number_or_object} the slot (-1 if not found) */ - LGraphNode.prototype.findInputSlot = function(name, returnObj) { + LGraphNode.prototype.findInputSlot = function(name, returnObj) { if (!this.inputs) { return -1; } @@ -4046,9 +3994,9 @@ } return -1; }; - + // TODO refactor: USE SINGLE findInput/findOutput functions! :: merge options - + /** * returns the first free input slot * @method findInputSlotFree @@ -4057,9 +4005,10 @@ */ LGraphNode.prototype.findInputSlotFree = function(optsIn) { var optsIn = optsIn || {}; - var optsDef = {returnObj: false - ,typesNotAccepted: [] - }; + var optsDef = { + returnObj: false, + typesNotAccepted: [], + }; var opts = Object.assign(optsDef,optsIn); if (!this.inputs) { return -1; @@ -4068,7 +4017,7 @@ if (this.inputs[i].link && this.inputs[i].link != null) { continue; } - if (opts.typesNotAccepted && opts.typesNotAccepted.includes && opts.typesNotAccepted.includes(this.inputs[i].type)){ + if (opts.typesNotAccepted && opts.typesNotAccepted.includes && opts.typesNotAccepted.includes(this.inputs[i].type)) { continue; } return !opts.returnObj ? i : this.inputs[i]; @@ -4084,9 +4033,10 @@ */ LGraphNode.prototype.findOutputSlotFree = function(optsIn) { var optsIn = optsIn || {}; - var optsDef = { returnObj: false - ,typesNotAccepted: [] - }; + var optsDef = { + returnObj: false, + typesNotAccepted: [], + }; var opts = Object.assign(optsDef,optsIn); if (!this.outputs) { return -1; @@ -4095,14 +4045,14 @@ if (this.outputs[i].links && this.outputs[i].links != null) { continue; } - if (opts.typesNotAccepted && opts.typesNotAccepted.includes && opts.typesNotAccepted.includes(this.outputs[i].type)){ + if (opts.typesNotAccepted && opts.typesNotAccepted.includes && opts.typesNotAccepted.includes(this.outputs[i].type)) { continue; } return !opts.returnObj ? i : this.outputs[i]; } return -1; }; - + /** * findSlotByType for INPUTS */ @@ -4116,7 +4066,7 @@ LGraphNode.prototype.findOutputSlotByType = function(type, returnObj, preferFreeSlot, doNotUseOccupied) { return this.findSlotByType(false, type, returnObj, preferFreeSlot, doNotUseOccupied); }; - + /** * returns the output (or input) slot with a given type, -1 if not found * @method findSlotByType @@ -4135,20 +4085,20 @@ if (!aSlots) { return -1; } - // !! empty string type is considered 0, * !! - if (type == "" || type == "*") type = 0; + // !! empty string type is considered 0, * !! + if (type == "" || type == "*") type = 0; for (var i = 0, l = aSlots.length; i < l; ++i) { var tFound = false; var aSource = (type+"").toLowerCase().split(","); var aDest = aSlots[i].type=="0"||aSlots[i].type=="*"?"0":aSlots[i].type; - aDest = (aDest+"").toLowerCase().split(","); - for(var sI=0;sI= 0 && target_slot !== null){ - //console.debug("CONNbyTYPE type "+target_slotType+" for "+target_slot) + if (target_slot >= 0 && target_slot !== null) { + // console.debug("CONNbyTYPE type "+target_slotType+" for "+target_slot) return this.connect(slot, target_node, target_slot); }else{ - //console.log("type "+target_slotType+" not found or not free?") - if (opts.createEventInCase && target_slotType == LiteGraph.EVENT){ + // console.log("type "+target_slotType+" not found or not free?") + if (opts.createEventInCase && target_slotType == LiteGraph.EVENT) { // WILL CREATE THE onTrigger IN SLOT - //console.debug("connect WILL CREATE THE onTrigger "+target_slotType+" to "+target_node); + // console.debug("connect WILL CREATE THE onTrigger "+target_slotType+" to "+target_node); return this.connect(slot, target_node, -1); } - // connect to the first general output slot if not found a specific type and - if (opts.generalTypeInCase){ + // connect to the first general output slot if not found a specific type and + if (opts.generalTypeInCase) { var target_slot = target_node.findInputSlotByType(0, false, true, true); - //console.debug("connect TO a general type (*, 0), if not found the specific type ",target_slotType," to ",target_node,"RES_SLOT:",target_slot); - if (target_slot >= 0){ + // console.debug("connect TO a general type (*, 0), if not found the specific type ",target_slotType," to ",target_node,"RES_SLOT:",target_slot); + if (target_slot >= 0) { return this.connect(slot, target_node, target_slot); } } // connect to the first free input slot if not found a specific type and this output is general - if (opts.firstFreeIfOutputGeneralInCase && (target_slotType == 0 || target_slotType == "*" || target_slotType == "")){ + if (opts.firstFreeIfOutputGeneralInCase && (target_slotType == 0 || target_slotType == "*" || target_slotType == "")) { var target_slot = target_node.findInputSlotFree({typesNotAccepted: [LiteGraph.EVENT] }); - //console.debug("connect TO TheFirstFREE ",target_slotType," to ",target_node,"RES_SLOT:",target_slot); - if (target_slot >= 0){ - return this.connect(slot, target_node, target_slot); + // console.debug("connect TO TheFirstFREE ",target_slotType," to ",target_node,"RES_SLOT:",target_slot); + if (target_slot >= 0) { + return this.connect(slot, target_node, target_slot); } } - - console.debug("no way to connect type: ",target_slotType," to targetNODE ",target_node); - //TODO filter - + + console.debug("no way to connect type: ",target_slotType," to targetNODE ",target_node); + // TODO filter + return null; } } - + /** * connect this node input to the output of another node BY TYPE * @method connectByType @@ -4239,51 +4190,52 @@ */ LGraphNode.prototype.connectByTypeOutput = function(slot, source_node, source_slotType, optsIn) { var optsIn = optsIn || {}; - var optsDef = { createEventInCase: true - ,firstFreeIfInputGeneralInCase: true - ,generalTypeInCase: true - }; + var optsDef = { + createEventInCase: true, + firstFreeIfInputGeneralInCase: true, + generalTypeInCase: true, + }; var opts = Object.assign(optsDef,optsIn); if (source_node && source_node.constructor === Number) { source_node = this.graph.getNodeById(source_node); } var source_slot = source_node.findOutputSlotByType(source_slotType, false, true); - if (source_slot >= 0 && source_slot !== null){ - //console.debug("CONNbyTYPE OUT! type "+source_slotType+" for "+source_slot) + if (source_slot >= 0 && source_slot !== null) { + // console.debug("CONNbyTYPE OUT! type "+source_slotType+" for "+source_slot) return source_node.connect(source_slot, this, slot); }else{ - - // connect to the first general output slot if not found a specific type and - if (opts.generalTypeInCase){ + + // connect to the first general output slot if not found a specific type and + if (opts.generalTypeInCase) { var source_slot = source_node.findOutputSlotByType(0, false, true, true); - if (source_slot >= 0){ + if (source_slot >= 0) { return source_node.connect(source_slot, this, slot); } } - - if (opts.createEventInCase && source_slotType == LiteGraph.EVENT){ + + if (opts.createEventInCase && source_slotType == LiteGraph.EVENT) { // WILL CREATE THE onExecuted OUT SLOT - if (LiteGraph.do_add_triggers_slots){ - var source_slot = source_node.addOnExecutedOutput(); - return source_node.connect(source_slot, this, slot); - } + if (LiteGraph.do_add_triggers_slots) { + var source_slot = source_node.addOnExecutedOutput(); + return source_node.connect(source_slot, this, slot); + } } // connect to the first free output slot if not found a specific type and this input is general - if (opts.firstFreeIfInputGeneralInCase && (source_slotType == 0 || source_slotType == "*" || source_slotType == "")){ + if (opts.firstFreeIfInputGeneralInCase && (source_slotType == 0 || source_slotType == "*" || source_slotType == "")) { var source_slot = source_node.findOutputSlotFree({typesNotAccepted: [LiteGraph.EVENT] }); - if (source_slot >= 0){ + if (source_slot >= 0) { return source_node.connect(source_slot, this, slot); } } - - console.debug("no way to connect byOUT type: ",source_slotType," to sourceNODE ",source_node); - //TODO filter - - //console.log("type OUT! "+source_slotType+" not found or not free?") + + console.debug("no way to connect byOUT type: ",source_slotType," to sourceNODE ",source_node); + // TODO filter + + // console.log("type OUT! "+source_slotType+" not found or not free?") return null; } } - + /** * connect this node output to the input of another node * @method connect @@ -4296,14 +4248,12 @@ target_slot = target_slot || 0; if (!this.graph) { - //could be connected before adding it to a graph - console.log( - "Connect: Error, node doesn't belong to any graph. Nodes must be added first to a graph before connecting them." - ); //due to link ids being associated with graphs + // could be connected before adding it to a graph + console.log("Connect: Error, node doesn't belong to any graph. Nodes must be added first to a graph before connecting them."); // due to link ids being associated with graphs return null; } - //seek for the output slot + // seek for the output slot if (slot.constructor === String) { slot = this.findOutputSlot(slot); if (slot == -1) { @@ -4326,33 +4276,31 @@ throw "target node is null"; } - //avoid loopback + // avoid loopback if (target_node == this) { return null; } - //you can specify the slot by name + // you can specify the slot by name if (target_slot.constructor === String) { target_slot = target_node.findInputSlot(target_slot); if (target_slot == -1) { if (LiteGraph.debug) { - console.log( - "Connect: Error, no slot of name " + target_slot - ); + console.log("Connect: Error, no slot of name " + target_slot); } return null; } } else if (target_slot === LiteGraph.EVENT) { - - if (LiteGraph.do_add_triggers_slots){ - //search for first slot with event? :: NO this is done outside - //console.log("Connect: Creating triggerEvent"); - // force mode - target_node.changeMode(LiteGraph.ON_TRIGGER); - target_slot = target_node.findInputSlot("onTrigger"); - }else{ - return null; // -- break -- - } + + if (LiteGraph.do_add_triggers_slots) { + // search for first slot with event? :: NO this is done outside + // console.log("Connect: Creating triggerEvent"); + // force mode + target_node.changeMode(LiteGraph.ON_TRIGGER); + target_slot = target_node.findInputSlot("onTrigger"); + } else { + return null; // -- break -- + } } else if ( !target_node.inputs || target_slot >= target_node.inputs.length @@ -4363,14 +4311,14 @@ return null; } - var changed = false; + var changed = false; var input = target_node.inputs[target_slot]; var link_info = null; var output = this.outputs[slot]; - - if (!this.outputs[slot]){ - /*console.debug("Invalid slot passed: "+slot); + + if (!this.outputs[slot]) { + /* console.debug("Invalid slot passed: "+slot); console.debug(this.outputs);*/ return null; } @@ -4378,21 +4326,20 @@ // allow target node to change slot if (target_node.onBeforeConnectInput) { // This way node can choose another slot (or make a new one?) - target_slot = target_node.onBeforeConnectInput(target_slot); //callback + target_slot = target_node.onBeforeConnectInput(target_slot); // callback } - //check target_slot and check connection types - if (target_slot===false || target_slot===null || !LiteGraph.isValidConnection(output.type, input.type)) - { - this.setDirtyCanvas(false, true); - if(changed) - this.graph.connectionChange(this, link_info); - return null; - }else{ - //console.debug("valid connection",output.type, input.type); - } + // check target_slot and check connection types + if (target_slot===false || target_slot===null || !LiteGraph.isValidConnection(output.type, input.type)) { + this.setDirtyCanvas(false, true); + if(changed) + this.graph.connectionChange(this, link_info); + return null; + }else{ + // console.debug("valid connection",output.type, input.type); + } - //allows nodes to block connection, callback + // allows nodes to block connection, callback if (target_node.onConnectInput) { if ( target_node.onConnectInput(target_slot, output.type, output, this, slot) === false ) { return null; @@ -4404,23 +4351,23 @@ } } - //if there is something already plugged there, disconnect + // if there is something already plugged there, disconnect if (target_node.inputs[target_slot] && target_node.inputs[target_slot].link != null) { - this.graph.beforeChange(); + this.graph.beforeChange(); target_node.disconnectInput(target_slot, {doProcessChange: false}); - changed = true; + changed = true; } - if (output.links !== null && output.links.length){ - switch(output.type){ + if (output.links !== null && output.links.length) { + switch(output.type) { case LiteGraph.EVENT: - if (!LiteGraph.allow_multi_output_for_events){ + if (!LiteGraph.allow_multi_output_for_events) { this.graph.beforeChange(); this.disconnectOutput(slot, false, {doProcessChange: false}); // Input(target_slot, {doProcessChange: false}); changed = true; } - break; + break; default: - break; + break; } } @@ -4429,68 +4376,68 @@ nextId = LiteGraph.uuidv4(); else nextId = ++this.graph.last_link_id; - - //create link class - link_info = new LLink( - nextId, - input.type || output.type, - this.id, - slot, - target_node.id, - target_slot - ); - - //add to graph links list - this.graph.links[link_info.id] = link_info; - - //connect in output - if (output.links == null) { - output.links = []; - } - output.links.push(link_info.id); - //connect in input - target_node.inputs[target_slot].link = link_info.id; - if (this.graph) { - this.graph._version++; - } - if (this.onConnectionsChange) { - this.onConnectionsChange( - LiteGraph.OUTPUT, - slot, - true, - link_info, - output - ); - } //link_info has been created now, so its updated - if (target_node.onConnectionsChange) { - target_node.onConnectionsChange( - LiteGraph.INPUT, - target_slot, - true, - link_info, - input - ); - } - if (this.graph && this.graph.onNodeConnectionChange) { - this.graph.onNodeConnectionChange( - LiteGraph.INPUT, - target_node, - target_slot, - this, - slot - ); - this.graph.onNodeConnectionChange( - LiteGraph.OUTPUT, - this, - slot, - target_node, - target_slot - ); - } + + // create link class + link_info = new LLink( + nextId, + input.type || output.type, + this.id, + slot, + target_node.id, + target_slot, + ); + + // add to graph links list + this.graph.links[link_info.id] = link_info; + + // connect in output + if (output.links == null) { + output.links = []; + } + output.links.push(link_info.id); + // connect in input + target_node.inputs[target_slot].link = link_info.id; + if (this.graph) { + this.graph._version++; + } + if (this.onConnectionsChange) { + this.onConnectionsChange( + LiteGraph.OUTPUT, + slot, + true, + link_info, + output, + ); + } // link_info has been created now, so its updated + if (target_node.onConnectionsChange) { + target_node.onConnectionsChange( + LiteGraph.INPUT, + target_slot, + true, + link_info, + input, + ); + } + if (this.graph && this.graph.onNodeConnectionChange) { + this.graph.onNodeConnectionChange( + LiteGraph.INPUT, + target_node, + target_slot, + this, + slot, + ); + this.graph.onNodeConnectionChange( + LiteGraph.OUTPUT, + this, + slot, + target_node, + target_slot, + ); + } this.setDirtyCanvas(false, true); - this.graph.afterChange(); - this.graph.connectionChange(this, link_info); + this.graph.afterChange(); + this.graph.connectionChange(this, link_info); return link_info; }; @@ -4518,13 +4465,13 @@ return false; } - //get output slot + // get output slot var output = this.outputs[slot]; if (!output || !output.links || output.links.length == 0) { return false; } - //one of the output links in this slot + // one of the output links in this slot if (target_node) { if (target_node.constructor === Number) { target_node = this.graph.getNodeById(target_node); @@ -4537,12 +4484,12 @@ var link_id = output.links[i]; var link_info = this.graph.links[link_id]; - //is the link we are searching for... + // is the link we are searching for... if (link_info.target_id == target_node.id) { - output.links.splice(i, 1); //remove here + output.links.splice(i, 1); // remove here var input = target_node.inputs[link_info.target_slot]; - input.link = null; //remove there - delete this.graph.links[link_id]; //remove the link from the links pool + input.link = null; // remove there + delete this.graph.links[link_id]; // remove the link from the links pool if (this.graph) { this.graph._version++; } @@ -4552,47 +4499,46 @@ link_info.target_slot, false, link_info, - input + input, ); - } //link_info hasn't been modified so its ok + } // link_info hasn't been modified so its ok if (this.onConnectionsChange) { this.onConnectionsChange( LiteGraph.OUTPUT, slot, false, link_info, - output + output, ); } if (this.graph && this.graph.onNodeConnectionChange) { this.graph.onNodeConnectionChange( LiteGraph.OUTPUT, this, - slot + slot, ); } if (this.graph && this.graph.onNodeConnectionChange) { this.graph.onNodeConnectionChange( LiteGraph.OUTPUT, this, - slot + slot, ); this.graph.onNodeConnectionChange( LiteGraph.INPUT, target_node, - link_info.target_slot + link_info.target_slot, ); } break; } } - } //all the links in this output slot - else { + } else { // all the links in this output slot for (var i = 0, l = output.links.length; i < l; i++) { var link_id = output.links[i]; var link_info = this.graph.links[link_id]; if (!link_info) { - //bug: it happens sometimes + // bug: it happens sometimes continue; } @@ -4603,44 +4549,44 @@ } if (target_node) { input = target_node.inputs[link_info.target_slot]; - input.link = null; //remove other side link + input.link = null; // remove other side link if (target_node.onConnectionsChange) { target_node.onConnectionsChange( LiteGraph.INPUT, link_info.target_slot, false, link_info, - input + input, ); - } //link_info hasn't been modified so its ok + } // link_info hasn't been modified so its ok if (this.graph && this.graph.onNodeConnectionChange) { this.graph.onNodeConnectionChange( LiteGraph.INPUT, target_node, - link_info.target_slot + link_info.target_slot, ); } } - delete this.graph.links[link_id]; //remove the link from the links pool + delete this.graph.links[link_id]; // remove the link from the links pool if (this.onConnectionsChange) { this.onConnectionsChange( LiteGraph.OUTPUT, slot, false, link_info, - output + output, ); } if (this.graph && this.graph.onNodeConnectionChange) { this.graph.onNodeConnectionChange( LiteGraph.OUTPUT, this, - slot + slot, ); this.graph.onNodeConnectionChange( LiteGraph.INPUT, target_node, - link_info.target_slot + link_info.target_slot, ); } } @@ -4659,7 +4605,7 @@ * @return {boolean} if it was disconnected successfully */ LGraphNode.prototype.disconnectInput = function(slot) { - //seek for the output slot + // seek for the output slot if (slot.constructor === String) { slot = this.findInputSlot(slot); if (slot == -1) { @@ -4681,71 +4627,70 @@ } var link_id = this.inputs[slot].link; - if(link_id != null) - { - this.inputs[slot].link = null; - - //remove other side - var link_info = this.graph.links[link_id]; - if (link_info) { - var target_node = this.graph.getNodeById(link_info.origin_id); - if (!target_node) { - return false; - } - - var output = target_node.outputs[link_info.origin_slot]; - if (!output || !output.links || output.links.length == 0) { - return false; - } - - //search in the inputs list for this link - for (var i = 0, l = output.links.length; i < l; i++) { - if (output.links[i] == link_id) { - output.links.splice(i, 1); - break; - } - } - - delete this.graph.links[link_id]; //remove from the pool - if (this.graph) { - this.graph._version++; - } - if (this.onConnectionsChange) { - this.onConnectionsChange( - LiteGraph.INPUT, - slot, - false, - link_info, - input - ); - } - if (target_node.onConnectionsChange) { - target_node.onConnectionsChange( - LiteGraph.OUTPUT, - i, - false, - link_info, - output - ); - } - if (this.graph && this.graph.onNodeConnectionChange) { - this.graph.onNodeConnectionChange( - LiteGraph.OUTPUT, - target_node, - i - ); - this.graph.onNodeConnectionChange(LiteGraph.INPUT, this, slot); - } - } - } //link != null + if(link_id != null) { + this.inputs[slot].link = null; + + // remove other side + var link_info = this.graph.links[link_id]; + if (link_info) { + var target_node = this.graph.getNodeById(link_info.origin_id); + if (!target_node) { + return false; + } - this.setDirtyCanvas(false, true); - if(this.graph) - this.graph.connectionChange(this); - return true; - }; + var output = target_node.outputs[link_info.origin_slot]; + if (!output || !output.links || output.links.length == 0) { + return false; + } - /** + // search in the inputs list for this link + for (var i = 0, l = output.links.length; i < l; i++) { + if (output.links[i] == link_id) { + output.links.splice(i, 1); + break; + } + } + + delete this.graph.links[link_id]; // remove from the pool + if (this.graph) { + this.graph._version++; + } + if (this.onConnectionsChange) { + this.onConnectionsChange( + LiteGraph.INPUT, + slot, + false, + link_info, + input, + ); + } + if (target_node.onConnectionsChange) { + target_node.onConnectionsChange( + LiteGraph.OUTPUT, + i, + false, + link_info, + output, + ); + } + if (this.graph && this.graph.onNodeConnectionChange) { + this.graph.onNodeConnectionChange( + LiteGraph.OUTPUT, + target_node, + i, + ); + this.graph.onNodeConnectionChange(LiteGraph.INPUT, this, slot); + } + } + } // link != null + + this.setDirtyCanvas(false, true); + if(this.graph) + this.graph.connectionChange(this); + return true; + }; + + /** * returns the center of a connection point in canvas coords * @method getConnectionPos * @param {boolean} is_input true if if a input slot, false if it is an output @@ -4756,7 +4701,7 @@ LGraphNode.prototype.getConnectionPos = function( is_input, slot_number, - out + out, ) { out = out || new Float32Array(2); var num_slots = 0; @@ -4789,14 +4734,14 @@ return out; } - //weird feature that never got finished + // weird feature that never got finished if (is_input && slot_number == -1) { out[0] = this.pos[0] + LiteGraph.NODE_TITLE_HEIGHT * 0.5; out[1] = this.pos[1] + LiteGraph.NODE_TITLE_HEIGHT * 0.5; return out; } - //hard-coded pos + // hard-coded pos if ( is_input && num_slots > slot_number && @@ -4815,7 +4760,7 @@ return out; } - //horizontal distributed slots + // horizontal distributed slots if (this.horizontal) { out[0] = this.pos[0] + (slot_number + 0.5) * (this.size[0] / num_slots); @@ -4827,7 +4772,7 @@ return out; } - //default vertical slots + // default vertical slots if (is_input) { out[0] = this.pos[0] + offset; } else { @@ -4861,21 +4806,21 @@ this.console.shift(); } - if(this.graph.onNodeTrace) - this.graph.onNodeTrace(this, msg); + if(this.graph.onNodeTrace) + this.graph.onNodeTrace(this, msg); }; /* Forces to redraw or the main canvas (LGraphNode) or the bg canvas (links) */ LGraphNode.prototype.setDirtyCanvas = function( dirty_foreground, - dirty_background + dirty_background, ) { if (!this.graph) { return; } this.graph.sendActionToCanvas("setDirty", [ dirty_foreground, - dirty_background + dirty_background, ]); }; @@ -4892,7 +4837,7 @@ return img; }; - //safe LGraphNode action execution (not sure if safe) + // safe LGraphNode action execution (not sure if safe) /* LGraphNode.prototype.executeAction = function(action) { @@ -4941,12 +4886,12 @@ LGraphNode.prototype.executeAction = function(action) for (var i = 0; i < list.length; ++i) { var c = list[i]; - //releasing somebody elses capture?! + // releasing somebody elses capture?! if (!v && c.node_capturing_input != this) { continue; } - //change + // change c.node_capturing_input = v ? this : null; } }; @@ -4985,7 +4930,7 @@ LGraphNode.prototype.executeAction = function(action) LGraphNode.prototype.localToScreen = function(x, y, graphcanvas) { return [ (x + this.pos[0]) * graphcanvas.scale + graphcanvas.offset[0], - (y + this.pos[1]) * graphcanvas.scale + graphcanvas.offset[1] + (y + this.pos[1]) * graphcanvas.scale + graphcanvas.offset[1], ]; }; @@ -5018,7 +4963,7 @@ LGraphNode.prototype.executeAction = function(action) get: function() { return this._pos; }, - enumerable: true + enumerable: true, }); Object.defineProperty(this, "size", { @@ -5032,7 +4977,7 @@ LGraphNode.prototype.executeAction = function(action) get: function() { return this._size; }, - enumerable: true + enumerable: true, }); }; @@ -5051,10 +4996,10 @@ LGraphNode.prototype.executeAction = function(action) Math.round(b[0]), Math.round(b[1]), Math.round(b[2]), - Math.round(b[3]) + Math.round(b[3]), ], color: this.color, - font_size: this.font_size + font_size: this.font_size, }; }; @@ -5081,7 +5026,7 @@ LGraphNode.prototype.executeAction = function(action) node.getBounding(node_bounding); if (!overlapBounding(this._bounding, node_bounding)) { continue; - } //out of the visible area + } // out of the visible area this._nodes.push(node); } }; @@ -5089,9 +5034,9 @@ LGraphNode.prototype.executeAction = function(action) LGraphGroup.prototype.isPointInside = LGraphNode.prototype.isPointInside; LGraphGroup.prototype.setDirtyCanvas = LGraphNode.prototype.setDirtyCanvas; - //**************************************** + //* *************************************** - //Scale and Offset + // Scale and Offset function DragAndScale(element, skip_events) { this.offset = new Float32Array([0, 0]); this.scale = 1; @@ -5118,14 +5063,14 @@ LGraphNode.prototype.executeAction = function(action) this._binded_mouse_callback = this.onMouse.bind(this); - LiteGraph.pointerListenerAdd(element,"down", this._binded_mouse_callback); - LiteGraph.pointerListenerAdd(element,"move", this._binded_mouse_callback); - LiteGraph.pointerListenerAdd(element,"up", this._binded_mouse_callback); + LiteGraph.pointerListenerAdd(element,"down", this._binded_mouse_callback); + LiteGraph.pointerListenerAdd(element,"move", this._binded_mouse_callback); + LiteGraph.pointerListenerAdd(element,"up", this._binded_mouse_callback); element.addEventListener( "mousewheel", this._binded_mouse_callback, - false + false, ); element.addEventListener("wheel", this._binded_mouse_callback, false); }; @@ -5139,13 +5084,12 @@ LGraphNode.prototype.executeAction = function(action) var height = this.element.height; var startx = -this.offset[0]; var starty = -this.offset[1]; - if( viewport ) - { - startx += viewport[0] / this.scale; - starty += viewport[1] / this.scale; - width = viewport[2]; - height = viewport[3]; - } + if( viewport ) { + startx += viewport[0] / this.scale; + starty += viewport[1] / this.scale; + width = viewport[2]; + height = viewport[3]; + } var endx = startx + width / this.scale; var endy = starty + height / this.scale; this.visible_area[0] = startx; @@ -5166,11 +5110,11 @@ LGraphNode.prototype.executeAction = function(action) e.canvasx = x; e.canvasy = y; e.dragging = this.dragging; - - var is_inside = !this.viewport || ( this.viewport && x >= this.viewport[0] && x < (this.viewport[0] + this.viewport[2]) && y >= this.viewport[1] && y < (this.viewport[1] + this.viewport[3]) ); - //console.log("pointerevents: DragAndScale onMouse "+e.type+" "+is_inside); - + var is_inside = !this.viewport || ( this.viewport && x >= this.viewport[0] && x < (this.viewport[0] + this.viewport[2]) && y >= this.viewport[1] && y < (this.viewport[1] + this.viewport[3]) ); + + // console.log("pointerevents: DragAndScale onMouse "+e.type+" "+is_inside); + var ignore = false; if (this.onmouse) { ignore = this.onmouse(e); @@ -5178,9 +5122,9 @@ LGraphNode.prototype.executeAction = function(action) if (e.type == LiteGraph.pointerevents_method+"down" && is_inside) { this.dragging = true; - LiteGraph.pointerListenerRemove(canvas,"move",this._binded_mouse_callback); - LiteGraph.pointerListenerAdd(document,"move",this._binded_mouse_callback); - LiteGraph.pointerListenerAdd(document,"up",this._binded_mouse_callback); + LiteGraph.pointerListenerRemove(canvas,"move",this._binded_mouse_callback); + LiteGraph.pointerListenerAdd(document,"move",this._binded_mouse_callback); + LiteGraph.pointerListenerAdd(document,"up",this._binded_mouse_callback); } else if (e.type == LiteGraph.pointerevents_method+"move") { if (!ignore) { var deltax = x - this.last_mouse[0]; @@ -5191,9 +5135,9 @@ LGraphNode.prototype.executeAction = function(action) } } else if (e.type == LiteGraph.pointerevents_method+"up") { this.dragging = false; - LiteGraph.pointerListenerRemove(document,"move",this._binded_mouse_callback); - LiteGraph.pointerListenerRemove(document,"up",this._binded_mouse_callback); - LiteGraph.pointerListenerAdd(canvas,"move",this._binded_mouse_callback); + LiteGraph.pointerListenerRemove(document,"move",this._binded_mouse_callback); + LiteGraph.pointerListenerRemove(document,"up",this._binded_mouse_callback); + LiteGraph.pointerListenerAdd(canvas,"move",this._binded_mouse_callback); } else if ( is_inside && (e.type == "mousewheel" || e.type == "wheel" || @@ -5207,24 +5151,23 @@ LGraphNode.prototype.executeAction = function(action) e.wheelDeltaY != null ? e.wheelDeltaY : e.detail * -60; } - //from stack overflow + // from stack overflow e.delta = e.wheelDelta ? e.wheelDelta / 40 : e.deltaY - ? -e.deltaY / 3 - : 0; + ? -e.deltaY / 3 + : 0; this.changeDeltaScale(1.0 + e.delta * 0.05); } this.last_mouse[0] = x; this.last_mouse[1] = y; - if(is_inside) - { - e.preventDefault(); - e.stopPropagation(); - return false; - } + if(is_inside) { + e.preventDefault(); + e.stopPropagation(); + return false; + } }; DragAndScale.prototype.toCanvasContext = function(ctx) { @@ -5233,10 +5176,10 @@ LGraphNode.prototype.executeAction = function(action) }; DragAndScale.prototype.convertOffsetToCanvas = function(pos) { - //return [pos[0] / this.scale - this.offset[0], pos[1] / this.scale - this.offset[1]]; + // return [pos[0] / this.scale - this.offset[0], pos[1] / this.scale - this.offset[1]]; return [ (pos[0] + this.offset[0]) * this.scale, - (pos[1] + this.offset[1]) * this.scale + (pos[1] + this.offset[1]) * this.scale, ]; }; @@ -5278,7 +5221,7 @@ LGraphNode.prototype.executeAction = function(action) zooming_center = zooming_center || [ rect.width * 0.5, - rect.height * 0.5 + rect.height * 0.5, ]; var center = this.convertCanvasToOffset(zooming_center); this.scale = value; @@ -5289,7 +5232,7 @@ LGraphNode.prototype.executeAction = function(action) var new_center = this.convertCanvasToOffset(zooming_center); var delta_offset = [ new_center[0] - center[0], - new_center[1] - center[1] + new_center[1] - center[1], ]; this.offset[0] += delta_offset[0]; @@ -5310,9 +5253,9 @@ LGraphNode.prototype.executeAction = function(action) this.offset[1] = 0; }; - //********************************************************************************* + //* ******************************************************************************** // LGraphCanvas: LGraph renderer CLASS - //********************************************************************************* + //* ******************************************************************************** /** * This class is in charge of rendering one graph inside a canvas. And provides all the interaction required. @@ -5327,7 +5270,7 @@ LGraphNode.prototype.executeAction = function(action) function LGraphCanvas(canvas, graph, options) { this.options = options = options || {}; - //if(graph === undefined) + // if(graph === undefined) // throw ("No graph assigned"); this.background_image = LGraphCanvas.DEFAULT_BACKGROUND_IMAGE; @@ -5336,7 +5279,7 @@ LGraphNode.prototype.executeAction = function(action) } this.ds = new DragAndScale(); - this.zoom_modify_alpha = true; //otherwise it generates ugly patterns when scaling down too much + this.zoom_modify_alpha = true; // otherwise it generates ugly patterns when scaling down too much this.title_text_font = "" + LiteGraph.NODE_TEXT_SIZE + "px Arial"; this.inner_text_font = @@ -5345,93 +5288,92 @@ LGraphNode.prototype.executeAction = function(action) this.default_link_color = LiteGraph.LINK_COLOR; this.default_connection_color = { input_off: "#778", - input_on: "#7F7", //"#BBD" + input_on: "#7F7", // "#BBD" output_off: "#778", - output_on: "#7F7" //"#BBD" - }; - this.default_connection_color_byType = { - /*number: "#7F7", + output_on: "#7F7", // "#BBD" + }; + /* number: "#7F7", string: "#77F", boolean: "#F77",*/ - } - this.default_connection_color_byTypeOff = { - /*number: "#474", + this.default_connection_color_byType = {}; + + /* number: "#474", string: "#447", boolean: "#744",*/ - }; + this.default_connection_color_byTypeOff = {}; this.highquality_render = true; - this.use_gradients = false; //set to true to render titlebar with gradients - this.editor_alpha = 1; //used for transition + this.use_gradients = false; // set to true to render titlebar with gradients + this.editor_alpha = 1; // used for transition this.pause_rendering = false; this.clear_background = true; this.clear_background_color = "#222"; - this.read_only = false; //if set to true users cannot modify the graph + this.read_only = false; // if set to true users cannot modify the graph this.render_only_selected = true; this.live_mode = false; this.show_info = true; this.allow_dragcanvas = true; this.allow_dragnodes = true; - this.allow_interaction = true; //allow to control widgets, buttons, collapse, etc - this.multi_select = false; //allow selecting multi nodes without pressing extra keys + this.allow_interaction = true; // allow to control widgets, buttons, collapse, etc + this.multi_select = false; // allow selecting multi nodes without pressing extra keys this.allow_searchbox = true; - this.allow_reconnect_links = true; //allows to change a connection with having to redo it again - this.align_to_grid = false; //snap to grid + this.allow_reconnect_links = true; // allows to change a connection with having to redo it again + this.align_to_grid = false; // snap to grid this.drag_mode = false; this.dragging_rectangle = null; - this.filter = null; //allows to filter to only accept some type of nodes in a graph + this.filter = null; // allows to filter to only accept some type of nodes in a graph - this.set_canvas_dirty_on_mouse_event = true; //forces to redraw the canvas if the mouse does anything + this.set_canvas_dirty_on_mouse_event = true; // forces to redraw the canvas if the mouse does anything this.always_render_background = false; this.render_shadows = true; this.render_canvas_border = true; - this.render_connections_shadows = false; //too much cpu + this.render_connections_shadows = false; // too much cpu this.render_connections_border = true; this.render_curved_connections = false; this.render_connection_arrows = false; this.render_collapsed_slots = true; this.render_execution_order = false; this.render_title_colored = true; - this.render_link_tooltip = true; + this.render_link_tooltip = true; this.links_render_mode = LiteGraph.SPLINE_LINK; - this.mouse = [0, 0]; //mouse in canvas coordinates, where 0,0 is the top-left corner of the blue rectangle - this.graph_mouse = [0, 0]; //mouse in graph coordinates, where 0,0 is the top-left corner of the blue rectangle - this.canvas_mouse = this.graph_mouse; //LEGACY: REMOVE THIS, USE GRAPH_MOUSE INSTEAD + this.mouse = [0, 0]; // mouse in canvas coordinates, where 0,0 is the top-left corner of the blue rectangle + this.graph_mouse = [0, 0]; // mouse in graph coordinates, where 0,0 is the top-left corner of the blue rectangle + this.canvas_mouse = this.graph_mouse; // LEGACY: REMOVE THIS, USE GRAPH_MOUSE INSTEAD - //to personalize the search box + // to personalize the search box this.onSearchBox = null; this.onSearchBoxSelection = null; - //callbacks + // callbacks this.onMouse = null; - this.onDrawBackground = null; //to render background objects (behind nodes and connections) in the canvas affected by transform - this.onDrawForeground = null; //to render foreground objects (above nodes and connections) in the canvas affected by transform - this.onDrawOverlay = null; //to render foreground objects not affected by transform (for GUIs) - this.onDrawLinkTooltip = null; //called when rendering a tooltip - this.onNodeMoved = null; //called after moving a node - this.onSelectionChange = null; //called if the selection changes - this.onConnectingChange = null; //called before any link changes - this.onBeforeChange = null; //called before modifying the graph - this.onAfterChange = null; //called after modifying the graph + this.onDrawBackground = null; // to render background objects (behind nodes and connections) in the canvas affected by transform + this.onDrawForeground = null; // to render foreground objects (above nodes and connections) in the canvas affected by transform + this.onDrawOverlay = null; // to render foreground objects not affected by transform (for GUIs) + this.onDrawLinkTooltip = null; // called when rendering a tooltip + this.onNodeMoved = null; // called after moving a node + this.onSelectionChange = null; // called if the selection changes + this.onConnectingChange = null; // called before any link changes + this.onBeforeChange = null; // called before modifying the graph + this.onAfterChange = null; // called after modifying the graph this.connections_width = 3; this.round_radius = 8; this.current_node = null; - this.node_widget = null; //used for widgets - this.over_link_center = null; + this.node_widget = null; // used for widgets + this.over_link_center = null; this.last_mouse_position = [0, 0]; this.visible_area = this.ds.visible_area; this.visible_links = []; - this.viewport = options.viewport || null; //to constraint render area to a portion of the canvas + this.viewport = options.viewport || null; // to constraint render area to a portion of the canvas - //link canvas and graph + // link canvas and graph if (graph) { graph.attachCanvas(this); } @@ -5448,14 +5390,14 @@ LGraphNode.prototype.executeAction = function(action) global.LGraphCanvas = LiteGraph.LGraphCanvas = LGraphCanvas; - LGraphCanvas.DEFAULT_BACKGROUND_IMAGE = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAIAAAD/gAIDAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAQBJREFUeNrs1rEKwjAUhlETUkj3vP9rdmr1Ysammk2w5wdxuLgcMHyptfawuZX4pJSWZTnfnu/lnIe/jNNxHHGNn//HNbbv+4dr6V+11uF527arU7+u63qfa/bnmh8sWLBgwYJlqRf8MEptXPBXJXa37BSl3ixYsGDBMliwFLyCV/DeLIMFCxYsWLBMwSt4Be/NggXLYMGCBUvBK3iNruC9WbBgwYJlsGApeAWv4L1ZBgsWLFiwYJmCV/AK3psFC5bBggULloJX8BpdwXuzYMGCBctgwVLwCl7Be7MMFixYsGDBsu8FH1FaSmExVfAxBa/gvVmwYMGCZbBg/W4vAQYA5tRF9QYlv/QAAAAASUVORK5CYII="; + LGraphCanvas.DEFAULT_BACKGROUND_IMAGE = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAIAAAD/gAIDAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAQBJREFUeNrs1rEKwjAUhlETUkj3vP9rdmr1Ysammk2w5wdxuLgcMHyptfawuZX4pJSWZTnfnu/lnIe/jNNxHHGNn//HNbbv+4dr6V+11uF527arU7+u63qfa/bnmh8sWLBgwYJlqRf8MEptXPBXJXa37BSl3ixYsGDBMliwFLyCV/DeLIMFCxYsWLBMwSt4Be/NggXLYMGCBUvBK3iNruC9WbBgwYJlsGApeAWv4L1ZBgsWLFiwYJmCV/AK3psFC5bBggULloJX8BpdwXuzYMGCBctgwVLwCl7Be7MMFixYsGDBsu8FH1FaSmExVfAxBa/gvVmwYMGCZbBg/W4vAQYA5tRF9QYlv/QAAAAASUVORK5CYII="; LGraphCanvas.link_type_colors = { "-1": LiteGraph.EVENT_LINK_COLOR, number: "#AAA", - node: "#DCA" + node: "#DCA", }; - LGraphCanvas.gradients = {}; //cache of gradients + LGraphCanvas.gradients = {}; // cache of gradients /** * clears all the data inside @@ -5468,8 +5410,8 @@ LGraphNode.prototype.executeAction = function(action) this.render_time = 0; this.fps = 0; - //this.scale = 1; - //this.offset = [0,0]; + // this.scale = 1; + // this.offset = [0,0]; this.dragging_rectangle = null; @@ -5483,7 +5425,7 @@ LGraphNode.prototype.executeAction = function(action) this.connecting_node = null; this.highlighted_links = {}; - this.dragging_canvas = false; + this.dragging_canvas = false; this.dirty_canvas = true; this.dirty_bgcanvas = true; @@ -5494,8 +5436,8 @@ LGraphNode.prototype.executeAction = function(action) this.last_mouse = [0, 0]; this.last_mouseclick = 0; - this.pointer_is_down = false; - this.pointer_is_double = false; + this.pointer_is_down = false; + this.pointer_is_double = false; this.visible_area.set([0, 0, 0, 0]); if (this.onClear) { @@ -5525,9 +5467,9 @@ LGraphNode.prototype.executeAction = function(action) graph.attachCanvas(this); - //remove the graph stack in case a subgraph was open - if (this._graph_stack) - this._graph_stack = null; + // remove the graph stack in case a subgraph was open + if (this._graph_stack) + this._graph_stack = null; this.setDirty(true, true); }; @@ -5538,12 +5480,11 @@ LGraphNode.prototype.executeAction = function(action) * @method getTopGraph * @return {LGraph} graph */ - LGraphCanvas.prototype.getTopGraph = function() - { - if(this._graph_stack.length) - return this._graph_stack[0]; - return this.graph; - } + LGraphCanvas.prototype.getTopGraph = function() { + if(this._graph_stack.length) + return this._graph_stack[0]; + return this.graph; + } /** * opens a graph contained inside a node in the current graph @@ -5570,7 +5511,7 @@ LGraphNode.prototype.executeAction = function(action) } graph.attachCanvas(this); - this.checkPanels(); + this.checkPanels(); this.setDirty(true, true); }; @@ -5631,7 +5572,7 @@ LGraphNode.prototype.executeAction = function(action) } if (!canvas && this.canvas) { - //maybe detach events from old_canvas + // maybe detach events from old_canvas if (!skip_events) { this.unbindEvents(); } @@ -5644,12 +5585,12 @@ LGraphNode.prototype.executeAction = function(action) return; } - //this.canvas.tabindex = "1000"; + // this.canvas.tabindex = "1000"; canvas.className += " lgraphcanvas"; canvas.data = this; - canvas.tabindex = "1"; //to allow key events + canvas.tabindex = "1"; // to allow key events - //bg canvas: used for non changing stuff + // bg canvas: used for non changing stuff this.bgcanvas = null; if (!this.bgcanvas) { this.bgcanvas = document.createElement("canvas"); @@ -5668,14 +5609,12 @@ LGraphNode.prototype.executeAction = function(action) var ctx = (this.ctx = canvas.getContext("2d")); if (ctx == null) { if (!canvas.webgl_enabled) { - console.warn( - "This canvas seems to be WebGL, enabling WebGL renderer" - ); + console.warn("This canvas seems to be WebGL, enabling WebGL renderer"); } this.enableWebGL(); } - //input: (move and up could be unbinded) + // input: (move and up could be unbinded) // why here? this._mousemove_callback = this.processMouseMove.bind(this); // why here? this._mouseup_callback = this.processMouseUp.bind(this); @@ -5684,9 +5623,9 @@ LGraphNode.prototype.executeAction = function(action) } }; - //used in some events to capture them + // used in some events to capture them LGraphCanvas.prototype._doNothing = function doNothing(e) { - //console.log("pointerevents: _doNothing "+e.type); + // console.log("pointerevents: _doNothing "+e.type); e.preventDefault(); return false; }; @@ -5705,37 +5644,37 @@ LGraphNode.prototype.executeAction = function(action) return; } - //console.log("pointerevents: bindEvents"); - + // console.log("pointerevents: bindEvents"); + var canvas = this.canvas; var ref_window = this.getCanvasWindow(); - var document = ref_window.document; //hack used when moving canvas between windows + var document = ref_window.document; // hack used when moving canvas between windows this._mousedown_callback = this.processMouseDown.bind(this); this._mousewheel_callback = this.processMouseWheel.bind(this); // why mousemove and mouseup were not binded here? this._mousemove_callback = this.processMouseMove.bind(this); this._mouseup_callback = this.processMouseUp.bind(this); - - //touch events -- TODO IMPLEMENT - //this._touch_callback = this.touchHandler.bind(this); - LiteGraph.pointerListenerAdd(canvas,"down", this._mousedown_callback, true); //down do not need to store the binded + // touch events -- TODO IMPLEMENT + // this._touch_callback = this.touchHandler.bind(this); + + LiteGraph.pointerListenerAdd(canvas,"down", this._mousedown_callback, true); // down do not need to store the binded canvas.addEventListener("mousewheel", this._mousewheel_callback, false); LiteGraph.pointerListenerAdd(canvas,"up", this._mouseup_callback, true); // CHECK: ??? binded or not - LiteGraph.pointerListenerAdd(canvas,"move", this._mousemove_callback); - + LiteGraph.pointerListenerAdd(canvas,"move", this._mousemove_callback); + canvas.addEventListener("contextmenu", this._doNothing); canvas.addEventListener( "DOMMouseScroll", this._mousewheel_callback, - false + false, ); - //touch events -- THIS WAY DOES NOT WORK, finish implementing pointerevents, than clean the touchevents - /*if( 'touchstart' in document.documentElement ) + // touch events -- THIS WAY DOES NOT WORK, finish implementing pointerevents, than clean the touchevents + /* if( 'touchstart' in document.documentElement ) { canvas.addEventListener("touchstart", this._touch_callback, true); canvas.addEventListener("touchmove", this._touch_callback, true); @@ -5743,13 +5682,13 @@ LGraphNode.prototype.executeAction = function(action) canvas.addEventListener("touchcancel", this._touch_callback, true); }*/ - //Keyboard ****************** + // Keyboard ****************** this._key_callback = this.processKey.bind(this); - canvas.setAttribute("tabindex",1); //otherwise key events are ignored + canvas.setAttribute("tabindex",1); // otherwise key events are ignored canvas.addEventListener("keydown", this._key_callback, true); - document.addEventListener("keyup", this._key_callback, true); //in document, otherwise it doesn't fire keyup + document.addEventListener("keyup", this._key_callback, true); // in document, otherwise it doesn't fire keyup - //Dropping Stuff over nodes ************************************ + // Dropping Stuff over nodes ************************************ this._ondrop_callback = this.processDrop.bind(this); canvas.addEventListener("dragover", this._doNothing, false); @@ -5770,21 +5709,21 @@ LGraphNode.prototype.executeAction = function(action) return; } - //console.log("pointerevents: unbindEvents"); - + // console.log("pointerevents: unbindEvents"); + var ref_window = this.getCanvasWindow(); var document = ref_window.document; - LiteGraph.pointerListenerRemove(this.canvas,"move", this._mousedown_callback); + LiteGraph.pointerListenerRemove(this.canvas,"move", this._mousedown_callback); LiteGraph.pointerListenerRemove(this.canvas,"up", this._mousedown_callback); LiteGraph.pointerListenerRemove(this.canvas,"down", this._mousedown_callback); this.canvas.removeEventListener( "mousewheel", - this._mousewheel_callback + this._mousewheel_callback, ); this.canvas.removeEventListener( "DOMMouseScroll", - this._mousewheel_callback + this._mousewheel_callback, ); this.canvas.removeEventListener("keydown", this._key_callback); document.removeEventListener("keyup", this._key_callback); @@ -5792,8 +5731,8 @@ LGraphNode.prototype.executeAction = function(action) this.canvas.removeEventListener("drop", this._ondrop_callback); this.canvas.removeEventListener("dragenter", this._doReturnTrue); - //touch events -- THIS WAY DOES NOT WORK, finish implementing pointerevents, than clean the touchevents - /*this.canvas.removeEventListener("touchstart", this._touch_callback ); + // touch events -- THIS WAY DOES NOT WORK, finish implementing pointerevents, than clean the touchevents + /* this.canvas.removeEventListener("touchstart", this._touch_callback ); this.canvas.removeEventListener("touchmove", this._touch_callback ); this.canvas.removeEventListener("touchend", this._touch_callback ); this.canvas.removeEventListener("touchcancel", this._touch_callback );*/ @@ -5883,7 +5822,7 @@ LGraphNode.prototype.executeAction = function(action) LGraphCanvas.prototype.startRendering = function() { if (this.is_rendering) { return; - } //already rendering + } // already rendering this.is_rendering = true; renderFrame.call(this); @@ -5918,19 +5857,18 @@ LGraphNode.prototype.executeAction = function(action) /* LiteGraphCanvas input */ - //used to block future mouse events (because of im gui) - LGraphCanvas.prototype.blockClick = function() - { - this.block_click = true; - this.last_mouseclick = 0; - } - + // used to block future mouse events (because of im gui) + LGraphCanvas.prototype.blockClick = function() { + this.block_click = true; + this.last_mouseclick = 0; + } + LGraphCanvas.prototype.processMouseDown = function(e) { - - if( this.set_canvas_dirty_on_mouse_event ) - this.dirty_canvas = true; - - if (!this.graph) { + + if( this.set_canvas_dirty_on_mouse_event ) + this.dirty_canvas = true; + + if (!this.graph) { return; } @@ -5941,62 +5879,58 @@ LGraphNode.prototype.executeAction = function(action) LGraphCanvas.active_canvas = this; var that = this; - var x = e.clientX; - var y = e.clientY; - //console.log(y,this.viewport); - //console.log("pointerevents: processMouseDown pointerId:"+e.pointerId+" which:"+e.which+" isPrimary:"+e.isPrimary+" :: x y "+x+" "+y); + var x = e.clientX; + var y = e.clientY; + // console.log(y,this.viewport); + // console.log("pointerevents: processMouseDown pointerId:"+e.pointerId+" which:"+e.which+" isPrimary:"+e.isPrimary+" :: x y "+x+" "+y); - this.ds.viewport = this.viewport; - var is_inside = !this.viewport || ( this.viewport && x >= this.viewport[0] && x < (this.viewport[0] + this.viewport[2]) && y >= this.viewport[1] && y < (this.viewport[1] + this.viewport[3]) ); + this.ds.viewport = this.viewport; + var is_inside = !this.viewport || ( this.viewport && x >= this.viewport[0] && x < (this.viewport[0] + this.viewport[2]) && y >= this.viewport[1] && y < (this.viewport[1] + this.viewport[3]) ); - //move mouse move event to the window in case it drags outside of the canvas - if(!this.options.skip_events) - { - LiteGraph.pointerListenerRemove(this.canvas,"move", this._mousemove_callback); - LiteGraph.pointerListenerAdd(ref_window.document,"move", this._mousemove_callback,true); //catch for the entire window - LiteGraph.pointerListenerAdd(ref_window.document,"up", this._mouseup_callback,true); - } + // move mouse move event to the window in case it drags outside of the canvas + if(!this.options.skip_events) { + LiteGraph.pointerListenerRemove(this.canvas,"move", this._mousemove_callback); + LiteGraph.pointerListenerAdd(ref_window.document,"move", this._mousemove_callback,true); // catch for the entire window + LiteGraph.pointerListenerAdd(ref_window.document,"up", this._mouseup_callback,true); + } - if(!is_inside){ - return; - } + if(!is_inside) { + return; + } var node = this.graph.getNodeOnPos( e.canvasX, e.canvasY, this.visible_nodes, 5 ); var skip_dragging = false; var skip_action = false; var now = LiteGraph.getTime(); - var is_primary = (e.isPrimary === undefined || !e.isPrimary); + var is_primary = (e.isPrimary === undefined || !e.isPrimary); var is_double_click = (now - this.last_mouseclick < 300) && is_primary; - this.mouse[0] = e.clientX; - this.mouse[1] = e.clientY; + this.mouse[0] = e.clientX; + this.mouse[1] = e.clientY; this.graph_mouse[0] = e.canvasX; this.graph_mouse[1] = e.canvasY; - this.last_click_position = [this.mouse[0],this.mouse[1]]; - - if (this.pointer_is_down && is_primary ){ - this.pointer_is_double = true; - //console.log("pointerevents: pointer_is_double start"); - }else{ - this.pointer_is_double = false; - } - this.pointer_is_down = true; - - + this.last_click_position = [this.mouse[0],this.mouse[1]]; + + if (this.pointer_is_down && is_primary ) { + this.pointer_is_double = true; + // console.log("pointerevents: pointer_is_double start"); + } else { + this.pointer_is_double = false; + } + this.pointer_is_down = true; + + this.canvas.focus(); LiteGraph.closeAllContextMenus(ref_window); - if (this.onMouse) - { + if (this.onMouse) { if (this.onMouse(e) == true) return; } - //left button mouse / single finger - if (e.which == 1 && !this.pointer_is_double) - { - if (e.ctrlKey) - { + // left button mouse / single finger + if (e.which == 1 && !this.pointer_is_double) { + if (e.ctrlKey) { this.dragging_rectangle = new Float32Array(4); this.dragging_rectangle[0] = e.canvasX; this.dragging_rectangle[1] = e.canvasY; @@ -6006,9 +5940,9 @@ LGraphNode.prototype.executeAction = function(action) } // clone node ALT dragging - if (LiteGraph.alt_drag_do_clone_nodes && e.altKey && node && this.allow_interaction && !skip_action && !this.read_only) - { - if (cloned = node.clone()){ + if (LiteGraph.alt_drag_do_clone_nodes && e.altKey && node && this.allow_interaction && !skip_action && !this.read_only) { + cloned = node.clone(); + if (cloned) { cloned.pos[0] += 5; cloned.pos[1] += 5; this.graph.add(cloned,false,{doCalcSize: false}); @@ -6016,7 +5950,7 @@ LGraphNode.prototype.executeAction = function(action) skip_action = true; if (!block_drag_node) { if (this.allow_dragnodes) { - this.graph.beforeChange(); + this.graph.beforeChange(); this.node_dragged = node; } if (!this.selected_nodes[node.id]) { @@ -6025,35 +5959,36 @@ LGraphNode.prototype.executeAction = function(action) } } } - + var clicking_canvas_bg = false; - //when clicked on top of a node - //and it is not interactive + // when clicked on top of a node + // and it is not interactive if (node && (this.allow_interaction || node.flags.allow_interaction) && !skip_action && !this.read_only) { if (!this.live_mode && !node.flags.pinned) { this.bringToFront(node); - } //if it wasn't selected? + } // if it wasn't selected? - //not dragging mouse to connect two slots + // not dragging mouse to connect two slots if ( this.allow_interaction && !this.connecting_node && !node.flags.collapsed && !this.live_mode ) { - //Search for corner for resize + // Search for corner for resize if ( !skip_action && node.resizable !== false && - isInsideRectangle( e.canvasX, + isInsideRectangle( + e.canvasX, e.canvasY, node.pos[0] + node.size[0] - 5, node.pos[1] + node.size[1] - 5, 10, - 10 + 10, ) ) { - this.graph.beforeChange(); + this.graph.beforeChange(); this.resizing_node = node; this.canvas.style.cursor = "se-resize"; skip_action = true; } else { - //search for outputs + // search for outputs if (node.outputs) { for ( var i = 0, l = node.outputs.length; i < l; ++i ) { var output = node.outputs[i]; @@ -6065,7 +6000,7 @@ LGraphNode.prototype.executeAction = function(action) link_pos[0] - 15, link_pos[1] - 10, 30, - 20 + 20, ) ) { this.connecting_node = node; @@ -6074,7 +6009,7 @@ LGraphNode.prototype.executeAction = function(action) this.connecting_pos = node.getConnectionPos( false, i ); this.connecting_slot = i; - if (LiteGraph.shift_click_do_break_link_from){ + if (LiteGraph.shift_click_do_break_link_from) { if (e.shiftKey) { node.disconnectOutput(i); } @@ -6096,7 +6031,7 @@ LGraphNode.prototype.executeAction = function(action) } } - //search for inputs + // search for inputs if (node.inputs) { for ( var i = 0, l = node.inputs.length; i < l; ++i ) { var input = node.inputs[i]; @@ -6108,7 +6043,7 @@ LGraphNode.prototype.executeAction = function(action) link_pos[0] - 15, link_pos[1] - 10, 30, - 20 + 20, ) ) { if (is_double_click) { @@ -6124,8 +6059,8 @@ LGraphNode.prototype.executeAction = function(action) if (input.link !== null) { var link_info = this.graph.links[ input.link - ]; //before disconnecting - if (LiteGraph.click_do_break_link_to){ + ]; // before disconnecting + if (LiteGraph.click_do_break_link_to) { node.disconnectInput(i); this.dirty_bgcanvas = true; skip_action = true; @@ -6135,10 +6070,10 @@ LGraphNode.prototype.executeAction = function(action) if ( this.allow_reconnect_links || - //this.move_destination_link_without_shift || + // this.move_destination_link_without_shift || e.shiftKey ) { - if (!LiteGraph.click_do_break_link_to){ + if (!LiteGraph.click_do_break_link_to) { node.disconnectInput(i); } this.connecting_node = this.graph._nodes_by_id[ @@ -6150,48 +6085,48 @@ LGraphNode.prototype.executeAction = function(action) this.connecting_slot ]; this.connecting_pos = this.connecting_node.getConnectionPos( false, this.connecting_slot ); - + this.dirty_bgcanvas = true; skip_action = true; } - + }else{ // has not node } - - if (!skip_action){ + + if (!skip_action) { // connect from in to out, from to to from this.connecting_node = node; this.connecting_input = input; this.connecting_input.slot_index = i; this.connecting_pos = node.getConnectionPos( true, i ); this.connecting_slot = i; - + this.dirty_bgcanvas = true; skip_action = true; } } } } - } //not resizing + } // not resizing } - //it wasn't clicked on the links boxes + // it wasn't clicked on the links boxes if (!skip_action) { var block_drag_node = false; - var pos = [e.canvasX - node.pos[0], e.canvasY - node.pos[1]]; + var pos = [e.canvasX - node.pos[0], e.canvasY - node.pos[1]]; - //widgets + // widgets var widget = this.processNodeWidgets( node, this.graph_mouse, e ); if (widget) { block_drag_node = true; this.node_widget = [node, widget]; } - //double clicking + // double clicking if (this.allow_interaction && is_double_click && this.selected_nodes[node.id]) { - //double click node + // double click node if (node.onDblClick) { node.onDblClick( e, pos, this ); } @@ -6199,30 +6134,29 @@ LGraphNode.prototype.executeAction = function(action) block_drag_node = true; } - //if do not capture mouse + // if do not capture mouse if ( node.onMouseDown && node.onMouseDown( e, pos, this ) ) { block_drag_node = true; } else { - //open subgraph button - if(node.subgraph && !node.skip_subgraph_button) - { - if ( !node.flags.collapsed && pos[0] > node.size[0] - LiteGraph.NODE_TITLE_HEIGHT && pos[1] < 0 ) { - var that = this; - setTimeout(function() { - that.openSubgraph(node.subgraph); - }, 10); - } - } - - if (this.live_mode) { - clicking_canvas_bg = true; - block_drag_node = true; - } + // open subgraph button + if(node.subgraph && !node.skip_subgraph_button) { + if ( !node.flags.collapsed && pos[0] > node.size[0] - LiteGraph.NODE_TITLE_HEIGHT && pos[1] < 0 ) { + var that = this; + setTimeout(function() { + that.openSubgraph(node.subgraph); + }, 10); + } + } + + if (this.live_mode) { + clicking_canvas_bg = true; + block_drag_node = true; + } } if (!block_drag_node) { if (this.allow_dragnodes) { - this.graph.beforeChange(); + this.graph.beforeChange(); this.node_dragged = node; } this.processNodeSelected(node, e); @@ -6236,155 +6170,155 @@ LGraphNode.prototype.executeAction = function(action) this.dirty_canvas = true; } - } //clicked outside of nodes - else { - if (!skip_action){ - //search for link connector - if(!this.read_only) { - for (var i = 0; i < this.visible_links.length; ++i) { - var link = this.visible_links[i]; - var center = link._pos; - if ( - !center || + } else { // clicked outside of nodes + if (!skip_action) { + // search for link connector + if(!this.read_only) { + for (var i = 0; i < this.visible_links.length; ++i) { + var link = this.visible_links[i]; + var center = link._pos; + if ( + !center || e.canvasX < center[0] - 4 || e.canvasX > center[0] + 4 || e.canvasY < center[1] - 4 || e.canvasY > center[1] + 4 - ) { - continue; - } - //link clicked - this.showLinkMenu(link, e); - this.over_link_center = null; //clear tooltip - break; - } - } - - this.selected_group = this.graph.getGroupOnPos( e.canvasX, e.canvasY ); - this.selected_group_resizing = false; - if (this.selected_group && !this.read_only ) { - if (e.ctrlKey) { - this.dragging_rectangle = null; - } - - var dist = distance( [e.canvasX, e.canvasY], [ this.selected_group.pos[0] + this.selected_group.size[0], this.selected_group.pos[1] + this.selected_group.size[1] ] ); - if (dist * this.ds.scale < 10) { - this.selected_group_resizing = true; - } else { - this.selected_group.recomputeInsideNodes(); - } - } - - if (is_double_click && !this.read_only && this.allow_searchbox) { - this.showSearchBox(e); - e.preventDefault(); - e.stopPropagation(); - } - - clicking_canvas_bg = true; - } + ) { + continue; + } + // link clicked + this.showLinkMenu(link, e); + this.over_link_center = null; // clear tooltip + break; + } + } + + this.selected_group = this.graph.getGroupOnPos( e.canvasX, e.canvasY ); + this.selected_group_resizing = false; + if (this.selected_group && !this.read_only ) { + if (e.ctrlKey) { + this.dragging_rectangle = null; + } + + var dist = distance( [e.canvasX, e.canvasY], [ this.selected_group.pos[0] + this.selected_group.size[0], this.selected_group.pos[1] + this.selected_group.size[1] ] ); + if (dist * this.ds.scale < 10) { + this.selected_group_resizing = true; + } else { + this.selected_group.recomputeInsideNodes(); + } + } + + if (is_double_click && !this.read_only && this.allow_searchbox) { + this.showSearchBox(e); + e.preventDefault(); + e.stopPropagation(); + } + + clicking_canvas_bg = true; + } } if (!skip_action && clicking_canvas_bg && this.allow_dragcanvas) { - //console.log("pointerevents: dragging_canvas start"); - this.dragging_canvas = true; + // console.log("pointerevents: dragging_canvas start"); + this.dragging_canvas = true; } - + } else if (e.which == 2) { - //middle button - - if (LiteGraph.middle_click_slot_add_default_node){ - if (node && this.allow_interaction && !skip_action && !this.read_only){ - //not dragging mouse to connect two slots - if ( - !this.connecting_node && + // middle button + + if (LiteGraph.middle_click_slot_add_default_node) { + if (node && this.allow_interaction && !skip_action && !this.read_only) { + // not dragging mouse to connect two slots + if ( + !this.connecting_node && !node.flags.collapsed && !this.live_mode - ) { - var mClikSlot = false; - var mClikSlot_index = false; - var mClikSlot_isOut = false; - //search for outputs - if (node.outputs) { - for ( var i = 0, l = node.outputs.length; i < l; ++i ) { - var output = node.outputs[i]; - var link_pos = node.getConnectionPos(false, i); - if (isInsideRectangle(e.canvasX,e.canvasY,link_pos[0] - 15,link_pos[1] - 10,30,20)) { - mClikSlot = output; - mClikSlot_index = i; - mClikSlot_isOut = true; - break; - } - } - } - - //search for inputs - if (node.inputs) { - for ( var i = 0, l = node.inputs.length; i < l; ++i ) { - var input = node.inputs[i]; - var link_pos = node.getConnectionPos(true, i); - if (isInsideRectangle(e.canvasX,e.canvasY,link_pos[0] - 15,link_pos[1] - 10,30,20)) { - mClikSlot = input; - mClikSlot_index = i; - mClikSlot_isOut = false; - break; - } - } - } - //console.log("middleClickSlots? "+mClikSlot+" & "+(mClikSlot_index!==false)); - if (mClikSlot && mClikSlot_index!==false){ - - var alphaPosY = 0.5-((mClikSlot_index+1)/((mClikSlot_isOut?node.outputs.length:node.inputs.length))); - var node_bounding = node.getBounding(); - // estimate a position: this is a bad semi-bad-working mess .. REFACTOR with a correct autoplacement that knows about the others slots and nodes - var posRef = [ (!mClikSlot_isOut?node_bounding[0]:node_bounding[0]+node_bounding[2])// + node_bounding[0]/this.canvas.width*150 - ,e.canvasY-80// + node_bounding[0]/this.canvas.width*66 // vertical "derive" - ]; - var nodeCreated = this.createDefaultNodeForSlot({ nodeFrom: !mClikSlot_isOut?null:node - ,slotFrom: !mClikSlot_isOut?null:mClikSlot_index - ,nodeTo: !mClikSlot_isOut?node:null - ,slotTo: !mClikSlot_isOut?mClikSlot_index:null - ,position: posRef //,e: e - ,nodeType: "AUTO" //nodeNewType - ,posAdd:[!mClikSlot_isOut?-30:30, -alphaPosY*130] //-alphaPosY*30] - ,posSizeFix:[!mClikSlot_isOut?-1:0, 0] //-alphaPosY*2*/ - }); - - } - } - } - } else if (!skip_action && this.allow_dragcanvas) { - //console.log("pointerevents: dragging_canvas start from middle button"); - this.dragging_canvas = true; - } - - + ) { + var mClikSlot = false; + var mClikSlot_index = false; + var mClikSlot_isOut = false; + // search for outputs + if (node.outputs) { + for ( var i = 0, l = node.outputs.length; i < l; ++i ) { + var output = node.outputs[i]; + var link_pos = node.getConnectionPos(false, i); + if (isInsideRectangle(e.canvasX,e.canvasY,link_pos[0] - 15,link_pos[1] - 10,30,20)) { + mClikSlot = output; + mClikSlot_index = i; + mClikSlot_isOut = true; + break; + } + } + } + + // search for inputs + if (node.inputs) { + for ( var i = 0, l = node.inputs.length; i < l; ++i ) { + var input = node.inputs[i]; + var link_pos = node.getConnectionPos(true, i); + if (isInsideRectangle(e.canvasX,e.canvasY,link_pos[0] - 15,link_pos[1] - 10,30,20)) { + mClikSlot = input; + mClikSlot_index = i; + mClikSlot_isOut = false; + break; + } + } + } + // console.log("middleClickSlots? "+mClikSlot+" & "+(mClikSlot_index!==false)); + if (mClikSlot && mClikSlot_index!==false) { + + var alphaPosY = 0.5-((mClikSlot_index+1)/((mClikSlot_isOut?node.outputs.length:node.inputs.length))); + var node_bounding = node.getBounding(); + // estimate a position: this is a bad semi-bad-working mess .. REFACTOR with a correct autoplacement that knows about the others slots and nodes + var posRef = [ (!mClikSlot_isOut?node_bounding[0]:node_bounding[0]+node_bounding[2]),// + node_bounding[0]/this.canvas.width*150 + e.canvasY-80,// + node_bounding[0]/this.canvas.width*66 // vertical "derive" + ]; + var nodeCreated = this.createDefaultNodeForSlot({ + nodeFrom: !mClikSlot_isOut?null:node, + slotFrom: !mClikSlot_isOut?null:mClikSlot_index, + nodeTo: !mClikSlot_isOut?node:null, + slotTo: !mClikSlot_isOut?mClikSlot_index:null, + position: posRef, // ,e: e + nodeType: "AUTO", // nodeNewType + posAdd: [!mClikSlot_isOut?-30:30, -alphaPosY*130], // -alphaPosY*30] + posSizeFix: [!mClikSlot_isOut?-1:0, 0], // -alphaPosY*2*/ + }); + + } + } + } + } else if (!skip_action && this.allow_dragcanvas) { + // console.log("pointerevents: dragging_canvas start from middle button"); + this.dragging_canvas = true; + } + + } else if (e.which == 3 || this.pointer_is_double) { - - //right button - if (this.allow_interaction && !skip_action && !this.read_only){ - - // is it hover a node ? - if (node){ - if(Object.keys(this.selected_nodes).length - && (this.selected_nodes[node.id] || e.shiftKey || e.ctrlKey || e.metaKey) - ){ - // is multiselected or using shift to include the now node - if (!this.selected_nodes[node.id]) this.selectNodes([node],true); // add this if not present - }else{ - // update selection - this.selectNodes([node]); - } - } - - // show menu on this node - this.processContextMenu(node, e); - } - + + // right button + if (this.allow_interaction && !skip_action && !this.read_only) { + + // is it hover a node ? + if (node) { + if(Object.keys(this.selected_nodes).length + && (this.selected_nodes[node.id] || e.shiftKey || e.ctrlKey || e.metaKey) + ) { + // is multiselected or using shift to include the now node + if (!this.selected_nodes[node.id]) this.selectNodes([node],true); // add this if not present + }else{ + // update selection + this.selectNodes([node]); + } + } + + // show menu on this node + this.processContextMenu(node, e); + } + } - //TODO - //if(this.node_selected != prev_selected) + // TODO + // if(this.node_selected != prev_selected) // this.onNodeSelectionChange(this.node_selected); this.last_mouse[0] = e.clientX; @@ -6399,7 +6333,7 @@ LGraphNode.prototype.executeAction = function(action) this.graph.change(); - //this is to ensure to defocus(blur) if a text input element is on focus + // this is to ensure to defocus(blur) if a text input element is on focus if ( !ref_window.document.activeElement || (ref_window.document.activeElement.nodeName.toLowerCase() != @@ -6427,8 +6361,8 @@ LGraphNode.prototype.executeAction = function(action) this.resize(); } - if( this.set_canvas_dirty_on_mouse_event ) - this.dirty_canvas = true; + if( this.set_canvas_dirty_on_mouse_event ) + this.dirty_canvas = true; if (!this.graph) { return; @@ -6437,24 +6371,23 @@ LGraphNode.prototype.executeAction = function(action) LGraphCanvas.active_canvas = this; this.adjustMouseEvent(e); var mouse = [e.clientX, e.clientY]; - this.mouse[0] = mouse[0]; - this.mouse[1] = mouse[1]; + this.mouse[0] = mouse[0]; + this.mouse[1] = mouse[1]; var delta = [ mouse[0] - this.last_mouse[0], - mouse[1] - this.last_mouse[1] + mouse[1] - this.last_mouse[1], ]; this.last_mouse = mouse; this.graph_mouse[0] = e.canvasX; this.graph_mouse[1] = e.canvasY; - //console.log("pointerevents: processMouseMove "+e.pointerId+" "+e.isPrimary); - - if(this.block_click) - { - //console.log("pointerevents: processMouseMove block_click"); - e.preventDefault(); - return false; - } + // console.log("pointerevents: processMouseMove "+e.pointerId+" "+e.isPrimary); + + if(this.block_click) { + // console.log("pointerevents: processMouseMove block_click"); + e.preventDefault(); + return false; + } e.dragging = this.last_mouse_dragging; @@ -6463,27 +6396,24 @@ LGraphNode.prototype.executeAction = function(action) this.node_widget[0], this.graph_mouse, e, - this.node_widget[1] + this.node_widget[1], ); this.dirty_canvas = true; } - //get node over + // get node over var node = this.graph.getNodeOnPos(e.canvasX,e.canvasY,this.visible_nodes); - if (this.dragging_rectangle) - { + if (this.dragging_rectangle) { this.dragging_rectangle[2] = e.canvasX - this.dragging_rectangle[0]; this.dragging_rectangle[3] = e.canvasY - this.dragging_rectangle[1]; this.dirty_canvas = true; - } - else if (this.selected_group && !this.read_only) - { - //moving/resizing a group + } else if (this.selected_group && !this.read_only) { + // moving/resizing a group if (this.selected_group_resizing) { this.selected_group.size = [ e.canvasX - this.selected_group.pos[0], - e.canvasY - this.selected_group.pos[1] + e.canvasY - this.selected_group.pos[1], ]; } else { var deltax = delta[0] / this.ds.scale; @@ -6495,7 +6425,7 @@ LGraphNode.prototype.executeAction = function(action) } this.dirty_bgcanvas = true; } else if (this.dragging_canvas) { - ////console.log("pointerevents: processMouseMove is dragging_canvas"); + // //console.log("pointerevents: processMouseMove is dragging_canvas"); this.ds.offset[0] += delta[0] / this.ds.scale; this.ds.offset[1] += delta[1] / this.ds.scale; this.dirty_canvas = true; @@ -6505,10 +6435,10 @@ LGraphNode.prototype.executeAction = function(action) this.dirty_canvas = true; } - //remove mouseover flag + // remove mouseover flag for (var i = 0, l = this.graph._nodes.length; i < l; ++i) { if (this.graph._nodes[i].mouseOver && node != this.graph._nodes[i] ) { - //mouse leave + // mouse leave this.graph._nodes[i].mouseOver = false; if (this.node_over && this.node_over.onMouseLeave) { this.node_over.onMouseLeave(e); @@ -6518,15 +6448,15 @@ LGraphNode.prototype.executeAction = function(action) } } - //mouse over a node + // mouse over a node if (node) { - if(node.redraw_on_mouse) + if(node.redraw_on_mouse) this.dirty_canvas = true; - //this.canvas.style.cursor = "move"; + // this.canvas.style.cursor = "move"; if (!node.mouseOver) { - //mouse enter + // mouse enter node.mouseOver = true; this.node_over = node; this.dirty_canvas = true; @@ -6536,45 +6466,45 @@ LGraphNode.prototype.executeAction = function(action) } } - //in case the node wants to do something + // in case the node wants to do something if (node.onMouseMove) { node.onMouseMove( e, [e.canvasX - node.pos[0], e.canvasY - node.pos[1]], this ); } - //if dragging a link + // if dragging a link if (this.connecting_node) { - - if (this.connecting_output){ - - var pos = this._highlight_input || [0, 0]; //to store the output of isOverNodeInput - //on top of input + if (this.connecting_output) { + + var pos = this._highlight_input || [0, 0]; // to store the output of isOverNodeInput + + // on top of input if (this.isOverNodeBox(node, e.canvasX, e.canvasY)) { - //mouse on top of the corner box, don't know what to do + // mouse on top of the corner box, don't know what to do } else { - //check if I have a slot below de mouse + // check if I have a slot below de mouse var slot = this.isOverNodeInput( node, e.canvasX, e.canvasY, pos ); if (slot != -1 && node.inputs[slot]) { var slot_type = node.inputs[slot].type; if ( LiteGraph.isValidConnection( this.connecting_output.type, slot_type ) ) { this._highlight_input = pos; - this._highlight_input_slot = node.inputs[slot]; // XXX CHECK THIS + this._highlight_input_slot = node.inputs[slot]; // XXX CHECK THIS } } else { this._highlight_input = null; - this._highlight_input_slot = null; // XXX CHECK THIS + this._highlight_input_slot = null; // XXX CHECK THIS } } - - }else if(this.connecting_input){ - - var pos = this._highlight_output || [0, 0]; //to store the output of isOverNodeOutput - //on top of output + }else if(this.connecting_input) { + + var pos = this._highlight_output || [0, 0]; // to store the output of isOverNodeOutput + + // on top of output if (this.isOverNodeBox(node, e.canvasX, e.canvasY)) { - //mouse on top of the corner box, don't know what to do + // mouse on top of the corner box, don't know what to do } else { - //check if I have a slot below de mouse + // check if I have a slot below de mouse var slot = this.isOverNodeOutput( node, e.canvasX, e.canvasY, pos ); if (slot != -1 && node.outputs[slot]) { var slot_type = node.outputs[slot].type; @@ -6588,7 +6518,7 @@ LGraphNode.prototype.executeAction = function(action) } } - //Search for corner + // Search for corner if (this.canvas) { if ( isInsideRectangle( @@ -6597,7 +6527,7 @@ LGraphNode.prototype.executeAction = function(action) node.pos[0] + node.size[0] - 5, node.pos[1] + node.size[1] - 5, 5, - 5 + 5, ) ) { this.canvas.style.cursor = "se-resize"; @@ -6605,44 +6535,43 @@ LGraphNode.prototype.executeAction = function(action) this.canvas.style.cursor = "crosshair"; } } - } else { //not over a node + } else { // not over a node - //search for link connector - var over_link = null; - for (var i = 0; i < this.visible_links.length; ++i) { - var link = this.visible_links[i]; - var center = link._pos; - if ( - !center || + // search for link connector + var over_link = null; + for (var i = 0; i < this.visible_links.length; ++i) { + var link = this.visible_links[i]; + var center = link._pos; + if ( + !center || e.canvasX < center[0] - 4 || e.canvasX > center[0] + 4 || e.canvasY < center[1] - 4 || e.canvasY > center[1] + 4 - ) { - continue; - } - over_link = link; - break; - } - if( over_link != this.over_link_center ) - { - this.over_link_center = over_link; - this.dirty_canvas = true; - } - - if (this.canvas) { - this.canvas.style.cursor = ""; - } - } //end - - //send event to node if capturing input (used with widgets that allow drag outside of the area of the node) + ) { + continue; + } + over_link = link; + break; + } + if( over_link != this.over_link_center ) { + this.over_link_center = over_link; + this.dirty_canvas = true; + } + + if (this.canvas) { + this.canvas.style.cursor = ""; + } + } // end + + // send event to node if capturing input (used with widgets that allow drag outside of the area of the node) if ( this.node_capturing_input && this.node_capturing_input != node && this.node_capturing_input.onMouseMove ) { this.node_capturing_input.onMouseMove(e,[e.canvasX - this.node_capturing_input.pos[0],e.canvasY - this.node_capturing_input.pos[1]], this); } - //node being dragged + // node being dragged if (this.node_dragged && !this.live_mode) { - //console.log("draggin!",this.selected_nodes); + // console.log("draggin!",this.selected_nodes); for (var i in this.selected_nodes) { var n = this.selected_nodes[i]; n.pos[0] += delta[0] / this.ds.scale; @@ -6658,12 +6587,12 @@ LGraphNode.prototype.executeAction = function(action) } if (this.resizing_node && !this.live_mode) { - //convert mouse to node space - var desired_size = [ e.canvasX - this.resizing_node.pos[0], e.canvasY - this.resizing_node.pos[1] ]; - var min_size = this.resizing_node.computeSize(); - desired_size[0] = Math.max( min_size[0], desired_size[0] ); - desired_size[1] = Math.max( min_size[1], desired_size[1] ); - this.resizing_node.setSize( desired_size ); + // convert mouse to node space + var desired_size = [ e.canvasX - this.resizing_node.pos[0], e.canvasY - this.resizing_node.pos[1] ]; + var min_size = this.resizing_node.computeSize(); + desired_size[0] = Math.max( min_size[0], desired_size[0] ); + desired_size[1] = Math.max( min_size[1], desired_size[1] ); + this.resizing_node.setSize( desired_size ); this.canvas.style.cursor = "se-resize"; this.dirty_canvas = true; @@ -6681,20 +6610,20 @@ LGraphNode.prototype.executeAction = function(action) **/ LGraphCanvas.prototype.processMouseUp = function(e) { - var is_primary = ( e.isPrimary === undefined || e.isPrimary ); - - //early exit for extra pointer - if(!is_primary){ - /*e.stopPropagation(); - e.preventDefault();*/ - //console.log("pointerevents: processMouseUp pointerN_stop "+e.pointerId+" "+e.isPrimary); - return false; - } - - //console.log("pointerevents: processMouseUp "+e.pointerId+" "+e.isPrimary+" :: "+e.clientX+" "+e.clientY); - - if( this.set_canvas_dirty_on_mouse_event ) - this.dirty_canvas = true; + var is_primary = ( e.isPrimary === undefined || e.isPrimary ); + + // early exit for extra pointer + if(!is_primary) { + /* e.stopPropagation(); + e.preventDefault();*/ + // console.log("pointerevents: processMouseUp pointerN_stop "+e.pointerId+" "+e.isPrimary); + return false; + } + + // console.log("pointerevents: processMouseUp "+e.pointerId+" "+e.isPrimary+" :: "+e.clientX+" "+e.clientY); + + if( this.set_canvas_dirty_on_mouse_event ) + this.dirty_canvas = true; if (!this.graph) return; @@ -6703,37 +6632,34 @@ LGraphNode.prototype.executeAction = function(action) var document = window.document; LGraphCanvas.active_canvas = this; - //restore the mousemove event back to the canvas - if(!this.options.skip_events) - { - //console.log("pointerevents: processMouseUp adjustEventListener"); - LiteGraph.pointerListenerRemove(document,"move", this._mousemove_callback,true); - LiteGraph.pointerListenerAdd(this.canvas,"move", this._mousemove_callback,true); - LiteGraph.pointerListenerRemove(document,"up", this._mouseup_callback,true); - } + // restore the mousemove event back to the canvas + if(!this.options.skip_events) { + // console.log("pointerevents: processMouseUp adjustEventListener"); + LiteGraph.pointerListenerRemove(document,"move", this._mousemove_callback,true); + LiteGraph.pointerListenerAdd(this.canvas,"move", this._mousemove_callback,true); + LiteGraph.pointerListenerRemove(document,"up", this._mouseup_callback,true); + } this.adjustMouseEvent(e); var now = LiteGraph.getTime(); e.click_time = now - this.last_mouseclick; this.last_mouse_dragging = false; - this.last_click_position = null; + this.last_click_position = null; - if(this.block_click) - { - //console.log("pointerevents: processMouseUp block_clicks"); - this.block_click = false; //used to avoid sending twice a click in a immediate button - } + if(this.block_click) { + // console.log("pointerevents: processMouseUp block_clicks"); + this.block_click = false; // used to avoid sending twice a click in a immediate button + } + + // console.log("pointerevents: processMouseUp which: "+e.which); - //console.log("pointerevents: processMouseUp which: "+e.which); - if (e.which == 1) { - if( this.node_widget ) - { - this.processNodeWidgets( this.node_widget[0], this.graph_mouse, e ); - } + if( this.node_widget ) { + this.processNodeWidgets( this.node_widget[0], this.graph_mouse, e ); + } - //left button + // left button this.node_widget = null; if (this.selected_group) { @@ -6744,12 +6670,8 @@ LGraphNode.prototype.executeAction = function(action) this.selected_group.pos[1] - Math.round(this.selected_group.pos[1]); this.selected_group.move(diffx, diffy, e.ctrlKey); - this.selected_group.pos[0] = Math.round( - this.selected_group.pos[0] - ); - this.selected_group.pos[1] = Math.round( - this.selected_group.pos[1] - ); + this.selected_group.pos[0] = Math.round(this.selected_group.pos[0]); + this.selected_group.pos[1] = Math.round(this.selected_group.pos[1]); if (this.selected_group._nodes.length) { this.dirty_canvas = true; } @@ -6757,18 +6679,18 @@ LGraphNode.prototype.executeAction = function(action) } this.selected_group_resizing = false; - var node = this.graph.getNodeOnPos( - e.canvasX, - e.canvasY, - this.visible_nodes - ); - + var node = this.graph.getNodeOnPos( + e.canvasX, + e.canvasY, + this.visible_nodes, + ); + if (this.dragging_rectangle) { if (this.graph) { var nodes = this.graph._nodes; var node_bounding = new Float32Array(4); - - //compute bounding and flip if left to right + + // compute bounding and flip if left to right var w = Math.abs(this.dragging_rectangle[2]); var h = Math.abs(this.dragging_rectangle[3]); var startx = @@ -6784,114 +6706,114 @@ LGraphNode.prototype.executeAction = function(action) this.dragging_rectangle[2] = w; this.dragging_rectangle[3] = h; - // test dragging rect size, if minimun simulate a click - if (!node || (w > 10 && h > 10 )){ - //test against all nodes (not visible because the rectangle maybe start outside - var to_select = []; - for (var i = 0; i < nodes.length; ++i) { - var nodeX = nodes[i]; - nodeX.getBounding(node_bounding); - if ( - !overlapBounding( - this.dragging_rectangle, - node_bounding - ) - ) { - continue; - } //out of the visible area - to_select.push(nodeX); - } - if (to_select.length) { - this.selectNodes(to_select,e.shiftKey); // add to selection with shift - } - }else{ - // will select of update selection - this.selectNodes([node],e.shiftKey||e.ctrlKey); // add to selection add to selection with ctrlKey or shiftKey - } - + // test dragging rect size, if minimun simulate a click + if (!node || (w > 10 && h > 10 )) { + // test against all nodes (not visible because the rectangle maybe start outside + var to_select = []; + for (var i = 0; i < nodes.length; ++i) { + var nodeX = nodes[i]; + nodeX.getBounding(node_bounding); + if ( + !overlapBounding( + this.dragging_rectangle, + node_bounding, + ) + ) { + continue; + } // out of the visible area + to_select.push(nodeX); + } + if (to_select.length) { + this.selectNodes(to_select,e.shiftKey); // add to selection with shift + } + }else{ + // will select of update selection + this.selectNodes([node],e.shiftKey||e.ctrlKey); // add to selection add to selection with ctrlKey or shiftKey + } + } this.dragging_rectangle = null; } else if (this.connecting_node) { - //dragging a connection + // dragging a connection this.dirty_canvas = true; this.dirty_bgcanvas = true; var connInOrOut = this.connecting_output || this.connecting_input; var connType = connInOrOut.type; - - //node below mouse + + // node below mouse if (node) { - + /* no need to condition on event type.. just another type if ( connType == LiteGraph.EVENT && this.isOverNodeBox(node, e.canvasX, e.canvasY) ) { - + this.connecting_node.connect( this.connecting_slot, node, LiteGraph.EVENT ); - + } else {*/ - - //slot below mouse? connect - - if (this.connecting_output){ - - var slot = this.isOverNodeInput( - node, - e.canvasX, - e.canvasY - ); - if (slot != -1) { - this.connecting_node.connect(this.connecting_slot, node, slot); - } else { - //not on top of an input - // look for a good slot - this.connecting_node.connectByType(this.connecting_slot,node,connType); - } - - }else if (this.connecting_input){ - - var slot = this.isOverNodeOutput( - node, - e.canvasX, - e.canvasY - ); - if (slot != -1) { - node.connect(slot, this.connecting_node, this.connecting_slot); // this is inverted has output-input nature like - } else { - //not on top of an input - // look for a good slot - this.connecting_node.connectByTypeOutput(this.connecting_slot,node,connType); - } - + // slot below mouse? connect + + if (this.connecting_output) { + + var slot = this.isOverNodeInput( + node, + e.canvasX, + e.canvasY, + ); + if (slot != -1) { + this.connecting_node.connect(this.connecting_slot, node, slot); + } else { + // not on top of an input + // look for a good slot + this.connecting_node.connectByType(this.connecting_slot,node,connType); } - - - //} - - }else{ - + + }else if (this.connecting_input) { + + var slot = this.isOverNodeOutput( + node, + e.canvasX, + e.canvasY, + ); + + if (slot != -1) { + node.connect(slot, this.connecting_node, this.connecting_slot); // this is inverted has output-input nature like + } else { + // not on top of an input + // look for a good slot + this.connecting_node.connectByTypeOutput(this.connecting_slot,node,connType); + } + + } + + + // } + + } else { + // add menu when releasing link in empty space - if (LiteGraph.release_link_on_empty_shows_menu){ - if (e.shiftKey && this.allow_searchbox){ - if(this.connecting_output){ - this.showSearchBox(e,{node_from: this.connecting_node, slot_from: this.connecting_output, type_filter_in: this.connecting_output.type}); - }else if(this.connecting_input){ - this.showSearchBox(e,{node_to: this.connecting_node, slot_from: this.connecting_input, type_filter_out: this.connecting_input.type}); - } - }else{ - if(this.connecting_output){ - this.showConnectionMenu({nodeFrom: this.connecting_node, slotFrom: this.connecting_output, e: e}); - }else if(this.connecting_input){ - this.showConnectionMenu({nodeTo: this.connecting_node, slotTo: this.connecting_input, e: e}); - } - } - } + if (LiteGraph.release_link_on_empty_shows_menu) { + if (e.shiftKey && this.allow_searchbox) { + if(this.connecting_output) { + this.showSearchBox(e,{node_from: this.connecting_node, slot_from: this.connecting_output, type_filter_in: this.connecting_output.type}); + }else if(this.connecting_input) { + this.showSearchBox(e,{node_to: this.connecting_node, slot_from: this.connecting_input, type_filter_out: this.connecting_input.type}); + } + } else { + if(this.connecting_output) { + this.showConnectionMenu({nodeFrom: this.connecting_node, slotFrom: this.connecting_output, e: e}); + }else if(this.connecting_input) { + this.showConnectionMenu({nodeTo: this.connecting_node, slotTo: this.connecting_input, e: e}); + } + } + } } this.connecting_output = null; @@ -6899,14 +6821,13 @@ LGraphNode.prototype.executeAction = function(action) this.connecting_pos = null; this.connecting_node = null; this.connecting_slot = -1; - } //not dragging connection - else if (this.resizing_node) { + } else if (this.resizing_node) { // not dragging connection this.dirty_canvas = true; this.dirty_bgcanvas = true; - this.graph.afterChange(this.resizing_node); + this.graph.afterChange(this.resizing_node); this.resizing_node = null; } else if (this.node_dragged) { - //node being dragged? + // node being dragged? var node = this.node_dragged; if ( node && @@ -6923,17 +6844,16 @@ LGraphNode.prototype.executeAction = function(action) if (this.graph.config.align_to_grid || this.align_to_grid ) { this.node_dragged.alignToGrid(); } - if( this.onNodeMoved ) - this.onNodeMoved( this.node_dragged ); - this.graph.afterChange(this.node_dragged); + if( this.onNodeMoved ) + this.onNodeMoved( this.node_dragged ); + this.graph.afterChange(this.node_dragged); this.node_dragged = null; - } //no node being dragged - else { - //get node over + } else { // no node being dragged + // get node over var node = this.graph.getNodeOnPos( e.canvasX, e.canvasY, - this.visible_nodes + this.visible_nodes, ); if (!node && e.click_time < 300) { @@ -6952,18 +6872,18 @@ LGraphNode.prototype.executeAction = function(action) ) { this.node_capturing_input.onMouseUp(e, [ e.canvasX - this.node_capturing_input.pos[0], - e.canvasY - this.node_capturing_input.pos[1] + e.canvasY - this.node_capturing_input.pos[1], ]); } } } else if (e.which == 2) { - //middle button - //trace("middle"); + // middle button + // trace("middle"); this.dirty_canvas = true; this.dragging_canvas = false; } else if (e.which == 3) { - //right button - //trace("right"); + // right button + // trace("right"); this.dirty_canvas = true; this.dragging_canvas = false; } @@ -6973,15 +6893,14 @@ LGraphNode.prototype.executeAction = function(action) this.draw(); */ - if (is_primary) - { - this.pointer_is_down = false; - this.pointer_is_double = false; - } - + if (is_primary) { + this.pointer_is_down = false; + this.pointer_is_double = false; + } + this.graph.change(); - //console.log("pointerevents: processMouseUp stopPropagation"); + // console.log("pointerevents: processMouseUp stopPropagation"); e.stopPropagation(); e.preventDefault(); return false; @@ -7000,21 +6919,17 @@ LGraphNode.prototype.executeAction = function(action) this.adjustMouseEvent(e); - var x = e.clientX; - var y = e.clientY; - var is_inside = !this.viewport || ( this.viewport && x >= this.viewport[0] && x < (this.viewport[0] + this.viewport[2]) && y >= this.viewport[1] && y < (this.viewport[1] + this.viewport[3]) ); - if(!is_inside) - return; + var x = e.clientX; + var y = e.clientY; + var is_inside = !this.viewport || ( this.viewport && x >= this.viewport[0] && x < (this.viewport[0] + this.viewport[2]) && y >= this.viewport[1] && y < (this.viewport[1] + this.viewport[3]) ); + if(!is_inside) + return; var scale = this.ds.scale; - if (delta > 0) { - scale *= 1.1; - } else if (delta < 0) { - scale *= 1 / 1.1; - } + scale *= Math.pow(1.1, delta * 0.01); - //this.setZoom( scale, [ e.clientX, e.clientY ] ); + // this.setZoom( scale, [ e.clientX, e.clientY ] ); this.ds.changeScale(scale, [e.clientX, e.clientY]); this.graph.change(); @@ -7036,7 +6951,7 @@ LGraphNode.prototype.executeAction = function(action) node.pos[0] + 2, node.pos[1] + 2 - title_height, title_height - 4, - title_height - 4 + title_height - 4, ) ) { return true; @@ -7052,7 +6967,7 @@ LGraphNode.prototype.executeAction = function(action) node, canvasx, canvasy, - slot_pos + slot_pos, ) { if (node.inputs) { for (var i = 0, l = node.inputs.length; i < l; ++i) { @@ -7066,7 +6981,7 @@ LGraphNode.prototype.executeAction = function(action) link_pos[0] - 5, link_pos[1] - 10, 10, - 20 + 20, ); } else { is_inside = isInsideRectangle( @@ -7075,7 +6990,7 @@ LGraphNode.prototype.executeAction = function(action) link_pos[0] - 10, link_pos[1] - 5, 40, - 10 + 10, ); } if (is_inside) { @@ -7089,7 +7004,7 @@ LGraphNode.prototype.executeAction = function(action) } return -1; }; - + /** * returns the INDEX if a position (in graph space) is on top of a node output slot * @method isOverNodeOuput @@ -7098,7 +7013,7 @@ LGraphNode.prototype.executeAction = function(action) node, canvasx, canvasy, - slot_pos + slot_pos, ) { if (node.outputs) { for (var i = 0, l = node.outputs.length; i < l; ++i) { @@ -7112,7 +7027,7 @@ LGraphNode.prototype.executeAction = function(action) link_pos[0] - 5, link_pos[1] - 10, 10, - 20 + 20, ); } else { is_inside = isInsideRectangle( @@ -7121,7 +7036,7 @@ LGraphNode.prototype.executeAction = function(action) link_pos[0] - 10, link_pos[1] - 5, 40, - 10 + 10, ); } if (is_inside) { @@ -7146,7 +7061,7 @@ LGraphNode.prototype.executeAction = function(action) } var block_default = false; - //console.log(e); //debug + // console.log(e); //debug if (e.target.localName == "input") { return; @@ -7154,26 +7069,26 @@ LGraphNode.prototype.executeAction = function(action) if (e.type == "keydown") { if (e.keyCode == 32) { - //space + // space this.dragging_canvas = true; block_default = true; } - + if (e.keyCode == 27) { - //esc + // esc if(this.node_panel) this.node_panel.close(); if(this.options_panel) this.options_panel.close(); block_default = true; } - //select all Control A + // select all Control A if (e.keyCode == 65 && e.ctrlKey) { this.selectNodes(); block_default = true; } if ((e.keyCode === 67) && (e.metaKey || e.ctrlKey) && !e.shiftKey) { - //copy + // copy if (this.selected_nodes) { this.copyToClipboard(); block_default = true; @@ -7181,11 +7096,11 @@ LGraphNode.prototype.executeAction = function(action) } if ((e.keyCode === 86) && (e.metaKey || e.ctrlKey)) { - //paste + // paste this.pasteFromClipboard(e.shiftKey); } - //delete or backspace + // delete or backspace if (e.keyCode == 46 || e.keyCode == 8) { if ( e.target.localName != "input" && @@ -7196,10 +7111,10 @@ LGraphNode.prototype.executeAction = function(action) } } - //collapse - //... + // collapse + // ... - //TODO + // TODO if (this.selected_nodes) { for (var i in this.selected_nodes) { if (this.selected_nodes[i].onKeyDown) { @@ -7234,16 +7149,17 @@ LGraphNode.prototype.executeAction = function(action) LGraphCanvas.prototype.copyToClipboard = function() { var clipboard_info = { nodes: [], - links: [] + links: [], }; var index = 0; var selected_nodes_array = []; + var map = {}; for (var i in this.selected_nodes) { var node = this.selected_nodes[i]; if (node.clonable === false) continue; - node._relative_id = index; selected_nodes_array.push(node); + map[node.id] = index; index += 1; } @@ -7252,8 +7168,7 @@ LGraphNode.prototype.executeAction = function(action) if(node.clonable === false) continue; var cloned = node.clone(); - if(!cloned) - { + if(!cloned) { console.warn("node type not found: " + node.type ); continue; } @@ -7268,25 +7183,23 @@ LGraphNode.prototype.executeAction = function(action) if (!link_info) { continue; } - var target_node = this.graph.getNodeById( - link_info.origin_id - ); + var target_node = this.graph.getNodeById(link_info.origin_id); if (!target_node) { continue; } clipboard_info.links.push([ - target_node._relative_id, - link_info.origin_slot, //j, - node._relative_id, + map[target_node.id], + link_info.origin_slot, // j, + map[node.id], link_info.target_slot, - target_node.id + target_node.id, ]); } } } localStorage.setItem( "litegrapheditor_clipboard", - JSON.stringify(clipboard_info) + JSON.stringify(clipboard_info), ); }; @@ -7300,25 +7213,24 @@ LGraphNode.prototype.executeAction = function(action) return; } - this.graph.beforeChange(); + this.graph.beforeChange(); - //create nodes + // create nodes var clipboard_info = JSON.parse(data); // calculate top-left node, could work without this processing but using diff with last node pos :: clipboard_info.nodes[clipboard_info.nodes.length-1].pos var posMin = false; var posMinIndexes = false; for (var i = 0; i < clipboard_info.nodes.length; ++i) { - if (posMin){ - if(posMin[0]>clipboard_info.nodes[i].pos[0]){ + if (posMin) { + if(posMin[0]>clipboard_info.nodes[i].pos[0]) { posMin[0] = clipboard_info.nodes[i].pos[0]; posMinIndexes[0] = i; } - if(posMin[1]>clipboard_info.nodes[i].pos[1]){ + if(posMin[1]>clipboard_info.nodes[i].pos[1]) { posMin[1] = clipboard_info.nodes[i].pos[1]; posMinIndexes[1] = i; } - } - else{ + } else{ posMin = [clipboard_info.nodes[i].pos[0], clipboard_info.nodes[i].pos[1]]; posMinIndexes = [i, i]; } @@ -7329,21 +7241,21 @@ LGraphNode.prototype.executeAction = function(action) var node = LiteGraph.createNode(node_data.type); if (node) { node.configure(node_data); - - //paste in last known mouse position - node.pos[0] += this.graph_mouse[0] - posMin[0]; //+= 5; - node.pos[1] += this.graph_mouse[1] - posMin[1]; //+= 5; - this.graph.add(node,{doProcessChange:false}); - + // paste in last known mouse position + node.pos[0] += this.graph_mouse[0] - posMin[0]; // += 5; + node.pos[1] += this.graph_mouse[1] - posMin[1]; // += 5; + + this.graph.add(node,{doProcessChange: false}); + nodes.push(node); } } - //create links + // create links for (var i = 0; i < clipboard_info.links.length; ++i) { var link_info = clipboard_info.links[i]; - var origin_node; + var origin_node = null; var origin_node_relative_id = link_info[0]; if (origin_node_relative_id != null) { origin_node = nodes[origin_node_relative_id]; @@ -7354,15 +7266,15 @@ LGraphNode.prototype.executeAction = function(action) } } var target_node = nodes[link_info[2]]; - if( origin_node && target_node ) - origin_node.connect(link_info[1], target_node, link_info[3]); - else - console.warn("Warning, nodes missing on pasting"); + if( origin_node && target_node ) + origin_node.connect(link_info[1], target_node, link_info[3]); + else + console.warn("Warning, nodes missing on pasting"); } this.selectNodes(nodes); - this.graph.afterChange(); + this.graph.afterChange(); }; /** @@ -7372,13 +7284,13 @@ LGraphNode.prototype.executeAction = function(action) LGraphCanvas.prototype.processDrop = function(e) { e.preventDefault(); this.adjustMouseEvent(e); - var x = e.clientX; - var y = e.clientY; - var is_inside = !this.viewport || ( this.viewport && x >= this.viewport[0] && x < (this.viewport[0] + this.viewport[2]) && y >= this.viewport[1] && y < (this.viewport[1] + this.viewport[3]) ); - if(!is_inside){ - return; - // --- BREAK --- - } + var x = e.clientX; + var y = e.clientY; + var is_inside = !this.viewport || ( this.viewport && x >= this.viewport[0] && x < (this.viewport[0] + this.viewport[2]) && y >= this.viewport[1] && y < (this.viewport[1] + this.viewport[3]) ); + if(!is_inside) { + return; + // --- BREAK --- + } var pos = [e.canvasX, e.canvasY]; @@ -7403,22 +7315,22 @@ LGraphNode.prototype.executeAction = function(action) var file = e.dataTransfer.files[0]; var filename = file.name; var ext = LGraphCanvas.getFileExtension(filename); - //console.log(file); + // console.log(file); if (node.onDropFile) { node.onDropFile(file); } if (node.onDropData) { - //prepare reader + // prepare reader var reader = new FileReader(); reader.onload = function(event) { - //console.log(event.target); + // console.log(event.target); var data = event.target.result; node.onDropData(data, filename, file); }; - //read data + // read data var type = file.type.split("/")[0]; if (type == "text" || type == "") { reader.readAsText(file); @@ -7445,21 +7357,21 @@ LGraphNode.prototype.executeAction = function(action) return false; }; - //called if the graph doesn't have a default drop item behaviour + // called if the graph doesn't have a default drop item behaviour LGraphCanvas.prototype.checkDropItem = function(e) { if (e.dataTransfer.files.length) { var file = e.dataTransfer.files[0]; var ext = LGraphCanvas.getFileExtension(file.name).toLowerCase(); var nodetype = LiteGraph.node_types_by_file_extension[ext]; if (nodetype) { - this.graph.beforeChange(); + this.graph.beforeChange(); var node = LiteGraph.createNode(nodetype.type); node.pos = [e.canvasX, e.canvasY]; this.graph.add(node); if (node.onDropFile) { node.onDropFile(file); } - this.graph.afterChange(); + this.graph.afterChange(); } } }; @@ -7467,11 +7379,9 @@ LGraphNode.prototype.executeAction = function(action) LGraphCanvas.prototype.processNodeDblClicked = function(n) { if (this.onShowNodePanel) { this.onShowNodePanel(n); + } else { + this.showShowNodePanel(n); } - else - { - this.showShowNodePanel(n); - } if (this.onNodeDblClicked) { this.onNodeDblClicked(n); @@ -7493,7 +7403,7 @@ LGraphNode.prototype.executeAction = function(action) **/ LGraphCanvas.prototype.selectNode = function( node, - add_to_current_selection + add_to_current_selection, ) { if (node == null) { this.deselectAllNodes(); @@ -7506,14 +7416,13 @@ LGraphNode.prototype.executeAction = function(action) * selects several nodes (or adds them to the current selection) * @method selectNodes **/ - LGraphCanvas.prototype.selectNodes = function( nodes, add_to_current_selection ) - { - if (!add_to_current_selection) { + LGraphCanvas.prototype.selectNodes = function( nodes, add_to_current_selection ) { + if (!add_to_current_selection) { this.deselectAllNodes(); } nodes = nodes || this.graph._nodes; - if (typeof nodes == "string") nodes = [nodes]; + if (typeof nodes == "string") nodes = [nodes]; for (var i in nodes) { var node = nodes[i]; if (node.is_selected) { @@ -7544,8 +7453,8 @@ LGraphNode.prototype.executeAction = function(action) } } - if( this.onSelectionChange ) - this.onSelectionChange( this.selected_nodes ); + if( this.onSelectionChange ) + this.onSelectionChange( this.selected_nodes ); this.setDirty(true); }; @@ -7567,7 +7476,7 @@ LGraphNode.prototype.executeAction = function(action) this.onNodeDeselected(node); } - //remove highlighted + // remove highlighted if (node.inputs) { for (var i = 0; i < node.inputs.length; ++i) { delete this.highlighted_links[node.inputs[i].link]; @@ -7603,15 +7512,15 @@ LGraphNode.prototype.executeAction = function(action) node.onDeselected(); } node.is_selected = false; - if (this.onNodeDeselected) { - this.onNodeDeselected(node); - } + if (this.onNodeDeselected) { + this.onNodeDeselected(node); + } } this.selected_nodes = {}; this.current_node = null; this.highlighted_links = {}; - if( this.onSelectionChange ) - this.onSelectionChange( this.selected_nodes ); + if( this.onSelectionChange ) + this.onSelectionChange( this.selected_nodes ); this.setDirty(true); }; @@ -7621,36 +7530,35 @@ LGraphNode.prototype.executeAction = function(action) **/ LGraphCanvas.prototype.deleteSelectedNodes = function() { - this.graph.beforeChange(); + this.graph.beforeChange(); for (var i in this.selected_nodes) { var node = this.selected_nodes[i]; - if(node.block_delete) - continue; + if(node.block_delete) + continue; - //autoconnect when possible (very basic, only takes into account first input-output) - if(node.inputs && node.inputs.length && node.outputs && node.outputs.length && LiteGraph.isValidConnection( node.inputs[0].type, node.outputs[0].type ) && node.inputs[0].link && node.outputs[0].links && node.outputs[0].links.length ) - { - var input_link = node.graph.links[ node.inputs[0].link ]; - var output_link = node.graph.links[ node.outputs[0].links[0] ]; - var input_node = node.getInputNode(0); - var output_node = node.getOutputNodes(0)[0]; - if(input_node && output_node) - input_node.connect( input_link.origin_slot, output_node, output_link.target_slot ); - } + // autoconnect when possible (very basic, only takes into account first input-output) + if(node.inputs && node.inputs.length && node.outputs && node.outputs.length && LiteGraph.isValidConnection( node.inputs[0].type, node.outputs[0].type ) && node.inputs[0].link && node.outputs[0].links && node.outputs[0].links.length ) { + var input_link = node.graph.links[node.inputs[0].link]; + var output_link = node.graph.links[node.outputs[0].links[0]]; + var input_node = node.getInputNode(0); + var output_node = node.getOutputNodes(0)[0]; + if(input_node && output_node) + input_node.connect( input_link.origin_slot, output_node, output_link.target_slot ); + } this.graph.remove(node); - if (this.onNodeDeselected) { - this.onNodeDeselected(node); - } + if (this.onNodeDeselected) { + this.onNodeDeselected(node); + } } this.selected_nodes = {}; this.current_node = null; this.highlighted_links = {}; this.setDirty(true); - this.graph.afterChange(); + this.graph.afterChange(); }; - + /** * centers the camera on a given node * @method centerOnNode @@ -7672,18 +7580,18 @@ LGraphNode.prototype.executeAction = function(action) * @method adjustMouseEvent **/ LGraphCanvas.prototype.adjustMouseEvent = function(e) { - var clientX_rel = 0; + var clientX_rel = 0; var clientY_rel = 0; - - if (this.canvas) { + + if (this.canvas) { var b = this.canvas.getBoundingClientRect(); clientX_rel = e.clientX - b.left; clientY_rel = e.clientY - b.top; } else { - clientX_rel = e.clientX; - clientY_rel = e.clientY; + clientX_rel = e.clientX; + clientY_rel = e.clientY; } - + // e.deltaX = clientX_rel - this.last_mouse_position[0]; // e.deltaY = clientY_rel- this.last_mouse_position[1]; @@ -7692,8 +7600,8 @@ LGraphNode.prototype.executeAction = function(action) e.canvasX = clientX_rel / this.ds.scale - this.ds.offset[0]; e.canvasY = clientY_rel / this.ds.scale - this.ds.offset[1]; - - //console.log("pointerevents: adjustMouseEvent "+e.clientX+":"+e.clientY+" "+clientX_rel+":"+clientY_rel+" "+e.canvasX+":"+e.canvasY); + + // console.log("pointerevents: adjustMouseEvent "+e.clientX+":"+e.clientY+" "+clientX_rel+":"+clientY_rel+" "+e.canvasX+":"+e.canvasY); }; /** @@ -7742,12 +7650,12 @@ LGraphNode.prototype.executeAction = function(action) return this.ds.convertCanvasToOffset(pos, out); }; - //converts event coordinates from canvas2D to graph coordinates + // converts event coordinates from canvas2D to graph coordinates LGraphCanvas.prototype.convertEventToCanvasOffset = function(e) { var rect = this.canvas.getBoundingClientRect(); return this.convertCanvasToOffset([ e.clientX - rect.left, - e.clientY - rect.top + e.clientY - rect.top, ]); }; @@ -7795,14 +7703,14 @@ LGraphNode.prototype.executeAction = function(action) for (var i = 0, l = nodes.length; i < l; ++i) { var n = nodes[i]; - //skip rendering nodes in live mode + // skip rendering nodes in live mode if (this.live_mode && !n.onDrawBackground && !n.onDrawForeground) { continue; } if (!overlapBounding(this.visible_area, n.getBounding(temp, true))) { continue; - } //out of the visible area + } // out of the visible area visible_nodes.push(n); } @@ -7818,7 +7726,7 @@ LGraphNode.prototype.executeAction = function(action) return; } - //fps counting + // fps counting var now = LiteGraph.getTime(); this.render_time = (now - this.last_draw_time) * 0.001; this.last_draw_time = now; @@ -7858,19 +7766,19 @@ LGraphNode.prototype.executeAction = function(action) } var ctx = this.ctx; if (!ctx) { - //maybe is using webgl... + // maybe is using webgl... return; } var canvas = this.canvas; if ( ctx.start2D && !this.viewport ) { ctx.start2D(); - ctx.restore(); - ctx.setTransform(1, 0, 0, 1, 0, 0); + ctx.restore(); + ctx.setTransform(1, 0, 0, 1, 0, 0); } - //clip dirty area if there is one, otherwise work in full canvas - var area = this.viewport || this.dirty_area; + // clip dirty area if there is one, otherwise work in full canvas + var area = this.viewport || this.dirty_area; if (area) { ctx.save(); ctx.beginPath(); @@ -7878,89 +7786,88 @@ LGraphNode.prototype.executeAction = function(action) ctx.clip(); } - //clear - //canvas.width = canvas.width; + // clear + // canvas.width = canvas.width; if (this.clear_background) { - if(area) - ctx.clearRect( area[0],area[1],area[2],area[3] ); - else - ctx.clearRect(0, 0, canvas.width, canvas.height); + if(area) + ctx.clearRect( area[0],area[1],area[2],area[3] ); + else + ctx.clearRect(0, 0, canvas.width, canvas.height); } - //draw bg canvas + // draw bg canvas if (this.bgcanvas == this.canvas) { this.drawBackCanvas(); } else { ctx.drawImage( this.bgcanvas, 0, 0 ); } - //rendering + // rendering if (this.onRender) { this.onRender(canvas, ctx); } - //info widget + // info widget if (this.show_info) { this.renderInfo(ctx, area ? area[0] : 0, area ? area[1] : 0 ); } if (this.graph) { - //apply transformations + // apply transformations ctx.save(); this.ds.toCanvasContext(ctx); - //draw nodes + // draw nodes var drawn_nodes = 0; var visible_nodes = this.computeVisibleNodes( null, - this.visible_nodes + this.visible_nodes, ); for (var i = 0; i < visible_nodes.length; ++i) { var node = visible_nodes[i]; - //transform coords system + // transform coords system ctx.save(); ctx.translate(node.pos[0], node.pos[1]); - //Draw + // Draw this.drawNode(node, ctx); drawn_nodes += 1; - //Restore + // Restore ctx.restore(); } - //on top (debug) + // on top (debug) if (this.render_execution_order) { this.drawExecutionOrder(ctx); } - //connections ontop? + // connections ontop? if (this.graph.config.links_ontop) { if (!this.live_mode) { this.drawConnections(ctx); } } - //current connection (the one being dragged by the mouse) + // current connection (the one being dragged by the mouse) if (this.connecting_pos != null) { ctx.lineWidth = this.connections_width; var link_color = null; - + var connInOrOut = this.connecting_output || this.connecting_input; var connType = connInOrOut.type; var connDir = connInOrOut.dir; - if(connDir == null) - { - if (this.connecting_output) - connDir = this.connecting_node.horizontal ? LiteGraph.DOWN : LiteGraph.RIGHT; - else - connDir = this.connecting_node.horizontal ? LiteGraph.UP : LiteGraph.LEFT; - } + if(connDir == null) { + if (this.connecting_output) + connDir = this.connecting_node.horizontal ? LiteGraph.DOWN : LiteGraph.RIGHT; + else + connDir = this.connecting_node.horizontal ? LiteGraph.UP : LiteGraph.LEFT; + } var connShape = connInOrOut.shape; - + switch (connType) { case LiteGraph.EVENT: link_color = LiteGraph.EVENT_LINK_COLOR; @@ -7969,7 +7876,7 @@ LGraphNode.prototype.executeAction = function(action) link_color = LiteGraph.CONNECTING_LINK_COLOR; } - //the connection being dragged by the mouse + // the connection being dragged by the mouse this.renderLink( ctx, this.connecting_pos, @@ -7979,7 +7886,7 @@ LGraphNode.prototype.executeAction = function(action) null, link_color, connDir, - LiteGraph.CENTER + LiteGraph.CENTER, ); ctx.beginPath(); @@ -7991,38 +7898,37 @@ LGraphNode.prototype.executeAction = function(action) this.connecting_pos[0] - 6 + 0.5, this.connecting_pos[1] - 5 + 0.5, 14, - 10 + 10, ); - ctx.fill(); - ctx.beginPath(); + ctx.fill(); + ctx.beginPath(); ctx.rect( this.graph_mouse[0] - 6 + 0.5, this.graph_mouse[1] - 5 + 0.5, 14, - 10 + 10, ); } else if (connShape === LiteGraph.ARROW_SHAPE) { ctx.moveTo(this.connecting_pos[0] + 8, this.connecting_pos[1] + 0.5); ctx.lineTo(this.connecting_pos[0] - 4, this.connecting_pos[1] + 6 + 0.5); ctx.lineTo(this.connecting_pos[0] - 4, this.connecting_pos[1] - 6 + 0.5); ctx.closePath(); - } - else { + } else { ctx.arc( this.connecting_pos[0], this.connecting_pos[1], 4, 0, - Math.PI * 2 + Math.PI * 2, ); - ctx.fill(); - ctx.beginPath(); + ctx.fill(); + ctx.beginPath(); ctx.arc( this.graph_mouse[0], this.graph_mouse[1], 4, 0, - Math.PI * 2 + Math.PI * 2, ); } ctx.fill(); @@ -8042,7 +7948,7 @@ LGraphNode.prototype.executeAction = function(action) this._highlight_input[1], 6, 0, - Math.PI * 2 + Math.PI * 2, ); } ctx.fill(); @@ -8060,32 +7966,32 @@ LGraphNode.prototype.executeAction = function(action) this._highlight_output[1], 6, 0, - Math.PI * 2 + Math.PI * 2, ); } ctx.fill(); } } - //the selection rectangle + // the selection rectangle if (this.dragging_rectangle) { ctx.strokeStyle = "#FFF"; ctx.strokeRect( this.dragging_rectangle[0], this.dragging_rectangle[1], this.dragging_rectangle[2], - this.dragging_rectangle[3] + this.dragging_rectangle[3], ); } - //on top of link center - if(this.over_link_center && this.render_link_tooltip) - this.drawLinkTooltip( ctx, this.over_link_center ); - else - if(this.onDrawLinkTooltip) //to remove - this.onDrawLinkTooltip(ctx,null); + // on top of link center + if(this.over_link_center && this.render_link_tooltip) + this.drawLinkTooltip( ctx, this.over_link_center ); + else + if(this.onDrawLinkTooltip) // to remove + this.onDrawLinkTooltip(ctx,null); - //custom info + // custom info if (this.onDrawForeground) { this.onDrawForeground(ctx, this.visible_rect); } @@ -8093,22 +7999,22 @@ LGraphNode.prototype.executeAction = function(action) ctx.restore(); } - //draws panel in the corner - if (this._graph_stack && this._graph_stack.length) { - this.drawSubgraphPanel( ctx ); - } + // draws panel in the corner + if (this._graph_stack && this._graph_stack.length) { + this.drawSubgraphPanel( ctx ); + } if (this.onDrawOverlay) { this.onDrawOverlay(ctx); } - if (area){ + if (area) { ctx.restore(); } if (ctx.finish2D) { - //this is a function I use in webgl renderer + // this is a function I use in webgl renderer ctx.finish2D(); } }; @@ -8159,7 +8065,7 @@ LGraphNode.prototype.executeAction = function(action) if (input.not_subgraph_input) continue; - //input button clicked + // input button clicked if (this.drawButton(20, y + 2, w - 20, h - 2)) { var type = subnode.constructor.input_node_type || "graph/input"; this.graph.beforeChange(); @@ -8176,8 +8082,7 @@ LGraphNode.prototype.executeAction = function(action) this.node_dragged.pos[0] = this.graph_mouse[0] - 5; this.node_dragged.pos[1] = this.graph_mouse[1] - 5; this.graph.afterChange(); - } - else + } else console.error("graph input node not found:", type); } ctx.fillStyle = "#9C9"; @@ -8191,7 +8096,7 @@ LGraphNode.prototype.executeAction = function(action) ctx.fillText(input.type, 130, y + h * 0.75); y += h; } - //add + button + // add + button if (this.drawButton(20, y + 2, w - 20, h - 2, "+", "#151515", "#222")) { this.showSubgraphPropertiesDialog(subnode); } @@ -8229,7 +8134,7 @@ LGraphNode.prototype.executeAction = function(action) if (output.not_subgraph_input) continue; - //output button clicked + // output button clicked if (this.drawButton(canvas_w - w, y + 2, w - 20, h - 2)) { var type = subnode.constructor.output_node_type || "graph/output"; this.graph.beforeChange(); @@ -8246,8 +8151,7 @@ LGraphNode.prototype.executeAction = function(action) this.node_dragged.pos[0] = this.graph_mouse[0] - 5; this.node_dragged.pos[1] = this.graph_mouse[1] - 5; this.graph.afterChange(); - } - else + } else console.error("graph input node not found:", type); } ctx.fillStyle = "#9C9"; @@ -8261,64 +8165,60 @@ LGraphNode.prototype.executeAction = function(action) ctx.fillText(output.type, canvas_w - w + 130, y + h * 0.75); y += h; } - //add + button + // add + button if (this.drawButton(canvas_w - w, y + 2, w - 20, h - 2, "+", "#151515", "#222")) { this.showSubgraphPropertiesDialogRight(subnode); } } - //Draws a button into the canvas overlay and computes if it was clicked using the immediate gui paradigm - LGraphCanvas.prototype.drawButton = function( x,y,w,h, text, bgcolor, hovercolor, textcolor ) - { - var ctx = this.ctx; - bgcolor = bgcolor || LiteGraph.NODE_DEFAULT_COLOR; - hovercolor = hovercolor || "#555"; - textcolor = textcolor || LiteGraph.NODE_TEXT_COLOR; - var pos = this.ds.convertOffsetToCanvas(this.graph_mouse); - var hover = LiteGraph.isInsideRectangle( pos[0], pos[1], x,y,w,h ); - pos = this.last_click_position ? [this.last_click_position[0], this.last_click_position[1]] : null; + // Draws a button into the canvas overlay and computes if it was clicked using the immediate gui paradigm + LGraphCanvas.prototype.drawButton = function( x,y,w,h, text, bgcolor, hovercolor, textcolor ) { + var ctx = this.ctx; + bgcolor = bgcolor || LiteGraph.NODE_DEFAULT_COLOR; + hovercolor = hovercolor || "#555"; + textcolor = textcolor || LiteGraph.NODE_TEXT_COLOR; + var pos = this.ds.convertOffsetToCanvas(this.graph_mouse); + var hover = LiteGraph.isInsideRectangle( pos[0], pos[1], x,y,w,h ); + pos = this.last_click_position ? [this.last_click_position[0], this.last_click_position[1]] : null; if(pos) { var rect = this.canvas.getBoundingClientRect(); pos[0] -= rect.left; pos[1] -= rect.top; } - var clicked = pos && LiteGraph.isInsideRectangle( pos[0], pos[1], x,y,w,h ); + var clicked = pos && LiteGraph.isInsideRectangle( pos[0], pos[1], x,y,w,h ); - ctx.fillStyle = hover ? hovercolor : bgcolor; - if(clicked) - ctx.fillStyle = "#AAA"; - ctx.beginPath(); - ctx.roundRect(x,y,w,h,[4] ); - ctx.fill(); + ctx.fillStyle = hover ? hovercolor : bgcolor; + if(clicked) + ctx.fillStyle = "#AAA"; + ctx.beginPath(); + ctx.roundRect(x,y,w,h,[4] ); + ctx.fill(); - if(text != null) - { - if(text.constructor == String) - { - ctx.fillStyle = textcolor; - ctx.textAlign = "center"; - ctx.font = ((h * 0.65)|0) + "px Arial"; - ctx.fillText( text, x + w * 0.5,y + h * 0.75 ); - ctx.textAlign = "left"; - } - } + if(text != null) { + if(text.constructor == String) { + ctx.fillStyle = textcolor; + ctx.textAlign = "center"; + ctx.font = ((h * 0.65)|0) + "px Arial"; + ctx.fillText( text, x + w * 0.5,y + h * 0.75 ); + ctx.textAlign = "left"; + } + } - var was_clicked = clicked && !this.block_click; - if(clicked) - this.blockClick(); - return was_clicked; - } + var was_clicked = clicked && !this.block_click; + if(clicked) + this.blockClick(); + return was_clicked; + } - LGraphCanvas.prototype.isAreaClicked = function( x,y,w,h, hold_click ) - { - var pos = this.mouse; - var hover = LiteGraph.isInsideRectangle( pos[0], pos[1], x,y,w,h ); - pos = this.last_click_position; - var clicked = pos && LiteGraph.isInsideRectangle( pos[0], pos[1], x,y,w,h ); - var was_clicked = clicked && !this.block_click; - if(clicked && hold_click) - this.blockClick(); - return was_clicked; - } + LGraphCanvas.prototype.isAreaClicked = function( x,y,w,h, hold_click ) { + var pos = this.mouse; + var hover = LiteGraph.isInsideRectangle( pos[0], pos[1], x,y,w,h ); + pos = this.last_click_position; + var clicked = pos && LiteGraph.isInsideRectangle( pos[0], pos[1], x,y,w,h ); + var was_clicked = clicked && !this.block_click; + if(clicked && hold_click) + this.blockClick(); + return was_clicked; + } /** * draws some useful stats in the corner of the canvas @@ -8333,7 +8233,7 @@ LGraphNode.prototype.executeAction = function(action) ctx.font = "10px Arial"; ctx.fillStyle = "#888"; - ctx.textAlign = "left"; + ctx.textAlign = "left"; if (this.graph) { ctx.fillText( "T: " + this.graph.globaltime.toFixed(2) + "s", 5, 13 * 1 ); ctx.fillText("I: " + this.graph.iteration, 5, 13 * 2 ); @@ -8368,14 +8268,14 @@ LGraphNode.prototype.executeAction = function(action) ctx.start(); } - var viewport = this.viewport || [0,0,ctx.canvas.width,ctx.canvas.height]; + var viewport = this.viewport || [0,0,ctx.canvas.width,ctx.canvas.height]; - //clear + // clear if (this.clear_background) { ctx.clearRect( viewport[0], viewport[1], viewport[2], viewport[3] ); } - //show subgraph stack header + // show subgraph stack header if (this._graph_stack && this._graph_stack.length) { ctx.save(); var parent_graph = this._graph_stack[this._graph_stack.length - 1]; @@ -8395,7 +8295,7 @@ LGraphNode.prototype.executeAction = function(action) ctx.fillText( title + subgraph_node.getTitle(), canvas.width * 0.5, - 40 + 40, ); ctx.restore(); } @@ -8405,28 +8305,26 @@ LGraphNode.prototype.executeAction = function(action) bg_already_painted = this.onRenderBackground(canvas, ctx); } - //reset in case of error - if ( !this.viewport ) - { - ctx.restore(); - ctx.setTransform(1, 0, 0, 1, 0, 0); - } + // reset in case of error + if ( !this.viewport ) { + ctx.restore(); + ctx.setTransform(1, 0, 0, 1, 0, 0); + } this.visible_links.length = 0; if (this.graph) { - //apply transformations + // apply transformations ctx.save(); this.ds.toCanvasContext(ctx); - //render BG - if ( this.ds.scale < 1.5 && !bg_already_painted && this.clear_background_color ) - { + // render BG + if ( this.ds.scale < 1.5 && !bg_already_painted && this.clear_background_color ) { ctx.fillStyle = this.clear_background_color; ctx.fillRect( this.visible_area[0], this.visible_area[1], this.visible_area[2], - this.visible_area[3] + this.visible_area[3], ); } @@ -8441,7 +8339,7 @@ LGraphNode.prototype.executeAction = function(action) } else { ctx.globalAlpha = this.editor_alpha; } - ctx.imageSmoothingEnabled = ctx.imageSmoothingEnabled = false; // ctx.mozImageSmoothingEnabled = + ctx.imageSmoothingEnabled = ctx.imageSmoothingEnabled = false; // ctx.mozImageSmoothingEnabled = if ( !this._bg_img || this._bg_img.name != this.background_image @@ -8469,16 +8367,16 @@ LGraphNode.prototype.executeAction = function(action) this.visible_area[0], this.visible_area[1], this.visible_area[2], - this.visible_area[3] + this.visible_area[3], ); ctx.fillStyle = "transparent"; } ctx.globalAlpha = 1.0; - ctx.imageSmoothingEnabled = ctx.imageSmoothingEnabled = true; //= ctx.mozImageSmoothingEnabled + ctx.imageSmoothingEnabled = ctx.imageSmoothingEnabled = true; // = ctx.mozImageSmoothingEnabled } - //groups + // groups if (this.graph._groups.length && !this.live_mode) { this.drawGroups(canvas, ctx); } @@ -8487,18 +8385,16 @@ LGraphNode.prototype.executeAction = function(action) this.onDrawBackground(ctx, this.visible_area); } if (this.onBackgroundRender) { - //LEGACY - console.error( - "WARNING! onBackgroundRender deprecated, now is named onDrawBackground " - ); + // LEGACY + console.error("WARNING! onBackgroundRender deprecated, now is named onDrawBackground "); this.onBackgroundRender = null; } - //DEBUG: show clipping area - //ctx.fillStyle = "red"; - //ctx.fillRect( this.visible_area[0] + 10, this.visible_area[1] + 10, this.visible_area[2] - 20, this.visible_area[3] - 20); + // DEBUG: show clipping area + // ctx.fillStyle = "red"; + // ctx.fillRect( this.visible_area[0] + 10, this.visible_area[1] + 10, this.visible_area[2] - 20, this.visible_area[3] - 20); - //bg + // bg if (this.render_canvas_border) { ctx.strokeStyle = "#235"; ctx.strokeRect(0, 0, canvas.width, canvas.height); @@ -8513,14 +8409,14 @@ LGraphNode.prototype.executeAction = function(action) ctx.shadowColor = "rgba(0,0,0,0)"; } - //draw connections + // draw connections if (!this.live_mode) { this.drawConnections(ctx); } ctx.shadowColor = "rgba(0,0,0,0)"; - //restore state + // restore state ctx.restore(); } @@ -8529,7 +8425,7 @@ LGraphNode.prototype.executeAction = function(action) } this.dirty_bgcanvas = false; - this.dirty_canvas = true; //to force to repaint the front canvas with the bgcanvas + this.dirty_canvas = true; // to force to repaint the front canvas with the bgcanvas }; var temp_vec2 = new Float32Array(2); @@ -8545,14 +8441,14 @@ LGraphNode.prototype.executeAction = function(action) var color = node.color || node.constructor.color || LiteGraph.NODE_DEFAULT_COLOR; var bgcolor = node.bgcolor || node.constructor.bgcolor || LiteGraph.NODE_DEFAULT_BGCOLOR; - //shadow and glow + // shadow and glow if (node.mouseOver) { glow = true; } - var low_quality = this.ds.scale < 0.6; //zoomed out + var low_quality = this.ds.scale < 0.6; // zoomed out - //only render if it forces it to do it + // only render if it forces it to do it if (this.live_mode) { if (!node.flags.collapsed) { ctx.shadowColor = "transparent"; @@ -8575,7 +8471,7 @@ LGraphNode.prototype.executeAction = function(action) ctx.shadowColor = "transparent"; } - //custom draw collapsed method (draw after shadows because they are affected) + // custom draw collapsed method (draw after shadows because they are affected) if ( node.flags.collapsed && node.onDrawCollapsed && @@ -8584,7 +8480,7 @@ LGraphNode.prototype.executeAction = function(action) return; } - //clip if required (mask) + // clip if required (mask) var shape = node._shape || LiteGraph.BOX_SHAPE; var size = temp_vec2; temp_vec2.set(node.size); @@ -8597,15 +8493,15 @@ LGraphNode.prototype.executeAction = function(action) node._collapsed_width = Math.min( node.size[0], ctx.measureText(title).width + - LiteGraph.NODE_TITLE_HEIGHT * 2 - ); //LiteGraph.NODE_COLLAPSED_WIDTH; + LiteGraph.NODE_TITLE_HEIGHT * 2, + ); // LiteGraph.NODE_COLLAPSED_WIDTH; size[0] = node._collapsed_width; size[1] = 0; } } if (node.clip_area) { - //Start clipping + // Start clipping ctx.save(); ctx.beginPath(); if (shape == LiteGraph.BOX_SHAPE) { @@ -8618,13 +8514,13 @@ LGraphNode.prototype.executeAction = function(action) size[1] * 0.5, size[0] * 0.5, 0, - Math.PI * 2 + Math.PI * 2, ); } ctx.clip(); } - //draw shape + // draw shape if (node.has_errors) { bgcolor = "red"; } @@ -8635,16 +8531,16 @@ LGraphNode.prototype.executeAction = function(action) color, bgcolor, node.is_selected, - node.mouseOver + node.mouseOver, ); ctx.shadowColor = "transparent"; - //draw foreground + // draw foreground if (node.onDrawForeground) { node.onDrawForeground(ctx, this, this.canvas); } - //connection slots + // connection slots ctx.textAlign = horizontal ? "center" : "left"; ctx.font = this.inner_text_font; @@ -8655,20 +8551,20 @@ LGraphNode.prototype.executeAction = function(action) ctx.lineWidth = 1; var max_y = 0; - var slot_pos = new Float32Array(2); //to reuse + var slot_pos = new Float32Array(2); // to reuse - //render inputs and outputs + // render inputs and outputs if (!node.flags.collapsed) { - //input connection slots + // input connection slots if (node.inputs) { for (var i = 0; i < node.inputs.length; i++) { var slot = node.inputs[i]; - + var slot_type = slot.type; var slot_shape = slot.shape; - + ctx.globalAlpha = editor_alpha; - //change opacity of incompatible slots when dragging a connection + // change opacity of incompatible slots when dragging a connection if ( this.connecting_output && !LiteGraph.isValidConnection( slot.type , out_slot.type) ) { ctx.globalAlpha = 0.4 * editor_alpha; } @@ -8692,12 +8588,12 @@ LGraphNode.prototype.executeAction = function(action) ctx.beginPath(); - if (slot_type == "array"){ + if (slot_type == "array") { slot_shape = LiteGraph.GRID_SHAPE; // place in addInput? addOutput instead? } - + var doStroke = true; - + if ( slot.type === LiteGraph.EVENT || slot.shape === LiteGraph.BOX_SHAPE @@ -8707,14 +8603,14 @@ LGraphNode.prototype.executeAction = function(action) pos[0] - 5 + 0.5, pos[1] - 8 + 0.5, 10, - 14 + 14, ); } else { ctx.rect( pos[0] - 6 + 0.5, pos[1] - 5 + 0.5, 14, - 10 + 10, ); } } else if (slot_shape === LiteGraph.ARROW_SHAPE) { @@ -8734,14 +8630,14 @@ LGraphNode.prototype.executeAction = function(action) ctx.rect(pos[0] + 2, pos[1] + 2, 2, 2); doStroke = false; } else { - if(low_quality) - ctx.rect(pos[0] - 4, pos[1] - 4, 8, 8 ); //faster - else - ctx.arc(pos[0], pos[1], 4, 0, Math.PI * 2); + if(low_quality) + ctx.rect(pos[0] - 4, pos[1] - 4, 8, 8 ); // faster + else + ctx.arc(pos[0], pos[1], 4, 0, Math.PI * 2); } ctx.fill(); - //render name + // render name if (render_text) { var text = slot.label != null ? slot.label : slot.name; if (text) { @@ -8756,22 +8652,22 @@ LGraphNode.prototype.executeAction = function(action) } } - //output connection slots + // output connection slots ctx.textAlign = horizontal ? "center" : "right"; ctx.strokeStyle = "black"; if (node.outputs) { for (var i = 0; i < node.outputs.length; i++) { var slot = node.outputs[i]; - + var slot_type = slot.type; var slot_shape = slot.shape; - - //change opacity of incompatible slots when dragging a connection + + // change opacity of incompatible slots when dragging a connection if (this.connecting_input && !LiteGraph.isValidConnection( slot_type , in_slot.type) ) { ctx.globalAlpha = 0.4 * editor_alpha; } - + var pos = node.getConnectionPos(false, i, slot_pos); pos[0] -= node.pos[0]; pos[1] -= node.pos[1]; @@ -8789,14 +8685,14 @@ LGraphNode.prototype.executeAction = function(action) this.default_connection_color_byType[slot_type] || this.default_connection_color.output_off; ctx.beginPath(); - //ctx.rect( node.size[0] - 14,i*14,10,10); + // ctx.rect( node.size[0] - 14,i*14,10,10); - if (slot_type == "array"){ + if (slot_type == "array") { slot_shape = LiteGraph.GRID_SHAPE; } - + var doStroke = true; - + if ( slot_type === LiteGraph.EVENT || slot_shape === LiteGraph.BOX_SHAPE @@ -8806,14 +8702,14 @@ LGraphNode.prototype.executeAction = function(action) pos[0] - 5 + 0.5, pos[1] - 8 + 0.5, 10, - 14 + 14, ); } else { ctx.rect( pos[0] - 6 + 0.5, pos[1] - 5 + 0.5, 14, - 10 + 10, ); } } else if (slot_shape === LiteGraph.ARROW_SHAPE) { @@ -8821,7 +8717,7 @@ LGraphNode.prototype.executeAction = function(action) ctx.lineTo(pos[0] - 4, pos[1] + 6 + 0.5); ctx.lineTo(pos[0] - 4, pos[1] - 6 + 0.5); ctx.closePath(); - } else if (slot_shape === LiteGraph.GRID_SHAPE) { + } else if (slot_shape === LiteGraph.GRID_SHAPE) { ctx.rect(pos[0] - 4, pos[1] - 4, 2, 2); ctx.rect(pos[0] - 1, pos[1] - 4, 2, 2); ctx.rect(pos[0] + 2, pos[1] - 4, 2, 2); @@ -8833,22 +8729,22 @@ LGraphNode.prototype.executeAction = function(action) ctx.rect(pos[0] + 2, pos[1] + 2, 2, 2); doStroke = false; } else { - if(low_quality) - ctx.rect(pos[0] - 4, pos[1] - 4, 8, 8 ); - else - ctx.arc(pos[0], pos[1], 4, 0, Math.PI * 2); + if(low_quality) + ctx.rect(pos[0] - 4, pos[1] - 4, 8, 8 ); + else + ctx.arc(pos[0], pos[1], 4, 0, Math.PI * 2); } - //trigger - //if(slot.node_id != null && slot.slot == -1) + // trigger + // if(slot.node_id != null && slot.slot == -1) // ctx.fillStyle = "#F85"; - //if(slot.links != null && slot.links.length) + // if(slot.links != null && slot.links.length) ctx.fill(); - if(!low_quality && doStroke) - ctx.stroke(); + if(!low_quality && doStroke) + ctx.stroke(); - //render output name + // render output name if (render_text) { var text = slot.label != null ? slot.label : slot.name; if (text) { @@ -8867,11 +8763,11 @@ LGraphNode.prototype.executeAction = function(action) ctx.globalAlpha = 1; if (node.widgets) { - var widgets_y = max_y; + var widgets_y = max_y; if (horizontal || node.widgets_up) { widgets_y = 2; } - if( node.widgets_start_y != null ) + if( node.widgets_start_y != null ) widgets_y = node.widgets_start_y; this.drawNodeWidgets( node, @@ -8879,15 +8775,15 @@ LGraphNode.prototype.executeAction = function(action) ctx, this.node_widget && this.node_widget[0] == node ? this.node_widget[1] - : null + : null, ); } } else if (this.render_collapsed_slots) { - //if collapsed + // if collapsed var input_slot = null; var output_slot = null; - //get first connected slot to render + // get first connected slot to render if (node.inputs) { for (var i = 0; i < node.inputs.length; i++) { var slot = node.inputs[i]; @@ -8910,7 +8806,7 @@ LGraphNode.prototype.executeAction = function(action) if (input_slot) { var x = 0; - var y = LiteGraph.NODE_TITLE_HEIGHT * -0.5; //center + var y = LiteGraph.NODE_TITLE_HEIGHT * -0.5; // center if (horizontal) { x = node._collapsed_width * 0.5; y = -LiteGraph.NODE_TITLE_HEIGHT; @@ -8935,7 +8831,7 @@ LGraphNode.prototype.executeAction = function(action) if (output_slot) { var x = node._collapsed_width; - var y = LiteGraph.NODE_TITLE_HEIGHT * -0.5; //center + var y = LiteGraph.NODE_TITLE_HEIGHT * -0.5; // center if (horizontal) { x = node._collapsed_width * 0.5; y = 0; @@ -8957,7 +8853,7 @@ LGraphNode.prototype.executeAction = function(action) ctx.arc(x, y, 4, 0, Math.PI * 2); } ctx.fill(); - //ctx.stroke(); + // ctx.stroke(); } } @@ -8968,60 +8864,59 @@ LGraphNode.prototype.executeAction = function(action) ctx.globalAlpha = 1.0; }; - //used by this.over_link_center - LGraphCanvas.prototype.drawLinkTooltip = function( ctx, link ) - { - var pos = link._pos; - ctx.fillStyle = "black"; - ctx.beginPath(); - ctx.arc( pos[0], pos[1], 3, 0, Math.PI * 2 ); - ctx.fill(); - - if(link.data == null) - return; - - if(this.onDrawLinkTooltip) - if( this.onDrawLinkTooltip(ctx,link,this) == true ) - return; - - var data = link.data; - var text = null; - - if( data.constructor === Number ) - text = data.toFixed(2); - else if( data.constructor === String ) - text = "\"" + data + "\""; - else if( data.constructor === Boolean ) - text = String(data); - else if (data.toToolTip) - text = data.toToolTip(); - else - text = "[" + data.constructor.name + "]"; - - if(text == null) - return; - text = text.substr(0,30); //avoid weird - - ctx.font = "14px Courier New"; - var info = ctx.measureText(text); - var w = info.width + 20; - var h = 24; - ctx.shadowColor = "black"; - ctx.shadowOffsetX = 2; - ctx.shadowOffsetY = 2; - ctx.shadowBlur = 3; - ctx.fillStyle = "#454"; - ctx.beginPath(); - ctx.roundRect( pos[0] - w*0.5, pos[1] - 15 - h, w, h, [3]); - ctx.moveTo( pos[0] - 10, pos[1] - 15 ); - ctx.lineTo( pos[0] + 10, pos[1] - 15 ); - ctx.lineTo( pos[0], pos[1] - 5 ); - ctx.fill(); + // used by this.over_link_center + LGraphCanvas.prototype.drawLinkTooltip = function( ctx, link ) { + var pos = link._pos; + ctx.fillStyle = "black"; + ctx.beginPath(); + ctx.arc( pos[0], pos[1], 3, 0, Math.PI * 2 ); + ctx.fill(); + + if(link.data == null) + return; + + if(this.onDrawLinkTooltip) + if( this.onDrawLinkTooltip(ctx,link,this) == true ) + return; + + var data = link.data; + var text = null; + + if( data.constructor === Number ) + text = data.toFixed(2); + else if( data.constructor === String ) + text = "\"" + data + "\""; + else if( data.constructor === Boolean ) + text = String(data); + else if (data.toToolTip) + text = data.toToolTip(); + else + text = "[" + data.constructor.name + "]"; + + if(text == null) + return; + text = text.substr(0,30); // avoid weird + + ctx.font = "14px Courier New"; + var info = ctx.measureText(text); + var w = info.width + 20; + var h = 24; + ctx.shadowColor = "black"; + ctx.shadowOffsetX = 2; + ctx.shadowOffsetY = 2; + ctx.shadowBlur = 3; + ctx.fillStyle = "#454"; + ctx.beginPath(); + ctx.roundRect( pos[0] - w*0.5, pos[1] - 15 - h, w, h, [3]); + ctx.moveTo( pos[0] - 10, pos[1] - 15 ); + ctx.lineTo( pos[0] + 10, pos[1] - 15 ); + ctx.lineTo( pos[0], pos[1] - 5 ); + ctx.fill(); ctx.shadowColor = "transparent"; - ctx.textAlign = "center"; - ctx.fillStyle = "#CEC"; - ctx.fillText(text, pos[0], pos[1] - 15 - h * 0.3); - } + ctx.textAlign = "center"; + ctx.fillStyle = "#CEC"; + ctx.fillText(text, pos[0], pos[1] - 15 - h * 0.3); + } /** * draws the shape of the given node in the canvas @@ -9036,16 +8931,16 @@ LGraphNode.prototype.executeAction = function(action) fgcolor, bgcolor, selected, - mouse_over + mouse_over, ) { - //bg rect + // bg rect ctx.strokeStyle = fgcolor; ctx.fillStyle = bgcolor; var title_height = LiteGraph.NODE_TITLE_HEIGHT; var low_quality = this.ds.scale < 0.5; - //render node area depending on shape + // render node area depending on shape var shape = node._shape || node.constructor.shape || LiteGraph.ROUND_SHAPE; @@ -9059,15 +8954,15 @@ LGraphNode.prototype.executeAction = function(action) } var area = tmp_area; - area[0] = 0; //x - area[1] = render_title ? -title_height : 0; //y - area[2] = size[0] + 1; //w - area[3] = render_title ? size[1] + title_height : size[1]; //h + area[0] = 0; // x + area[1] = render_title ? -title_height : 0; // y + area[2] = size[0] + 1; // w + area[3] = render_title ? size[1] + title_height : size[1]; // h var old_alpha = ctx.globalAlpha; - //full node shape - //if(node.flags.collapsed) + // full node shape + // if(node.flags.collapsed) { ctx.beginPath(); if (shape == LiteGraph.BOX_SHAPE || low_quality) { @@ -9081,7 +8976,7 @@ LGraphNode.prototype.executeAction = function(action) area[1], area[2], area[3], - shape == LiteGraph.CARD_SHAPE ? [this.round_radius,this.round_radius,0,0] : [this.round_radius] + shape == LiteGraph.CARD_SHAPE ? [this.round_radius,this.round_radius,0,0] : [this.round_radius], ); } else if (shape == LiteGraph.CIRCLE_SHAPE) { ctx.arc( @@ -9089,18 +8984,17 @@ LGraphNode.prototype.executeAction = function(action) size[1] * 0.5, size[0] * 0.5, 0, - Math.PI * 2 + Math.PI * 2, ); } ctx.fill(); - //separator - if(!node.flags.collapsed && render_title) - { - ctx.shadowColor = "transparent"; - ctx.fillStyle = "rgba(0,0,0,0.2)"; - ctx.fillRect(0, -1, area[2], 2); - } + // separator + if(!node.flags.collapsed && render_title) { + ctx.shadowColor = "transparent"; + ctx.fillStyle = "rgba(0,0,0,0.2)"; + ctx.fillRect(0, -1, area[2], 2); + } } ctx.shadowColor = "transparent"; @@ -9108,9 +9002,9 @@ LGraphNode.prototype.executeAction = function(action) node.onDrawBackground(ctx, this, this.canvas, this.graph_mouse ); } - //title bg (remember, it is rendered ABOVE the node) + // title bg (remember, it is rendered ABOVE the node) if (render_title || title_mode == LiteGraph.TRANSPARENT_TITLE) { - //title bar + // title bar if (node.onDrawTitleBar) { node.onDrawTitleBar( ctx, title_height, size, this.ds.scale, fgcolor ); } else if ( @@ -9127,7 +9021,7 @@ LGraphNode.prototype.executeAction = function(action) if (this.use_gradients) { var grad = LGraphCanvas.gradients[title_color]; if (!grad) { - grad = LGraphCanvas.gradients[ title_color ] = ctx.createLinearGradient(0, 0, 400, 0); + grad = LGraphCanvas.gradients[title_color] = ctx.createLinearGradient(0, 0, 400, 0); grad.addColorStop(0, title_color); // TODO refactor: validate color !! prevent DOMException grad.addColorStop(1, "#000"); } @@ -9136,17 +9030,17 @@ LGraphNode.prototype.executeAction = function(action) ctx.fillStyle = title_color; } - //ctx.globalAlpha = 0.5 * old_alpha; + // ctx.globalAlpha = 0.5 * old_alpha; ctx.beginPath(); if (shape == LiteGraph.BOX_SHAPE || low_quality) { ctx.rect(0, -title_height, size[0] + 1, title_height); - } else if ( shape == LiteGraph.ROUND_SHAPE || shape == LiteGraph.CARD_SHAPE ) { + } else if ( shape == LiteGraph.ROUND_SHAPE || shape == LiteGraph.CARD_SHAPE ) { ctx.roundRect( 0, -title_height, size[0] + 1, title_height, - node.flags.collapsed ? [this.round_radius] : [this.round_radius,this.round_radius,0,0] + node.flags.collapsed ? [this.round_radius] : [this.round_radius,this.round_radius,0,0], ); } ctx.fill(); @@ -9154,16 +9048,16 @@ LGraphNode.prototype.executeAction = function(action) } var colState = false; - if (LiteGraph.node_box_coloured_by_mode){ - if(LiteGraph.NODE_MODES_COLORS[node.mode]){ + if (LiteGraph.node_box_coloured_by_mode) { + if(LiteGraph.NODE_MODES_COLORS[node.mode]) { colState = LiteGraph.NODE_MODES_COLORS[node.mode]; } } - if (LiteGraph.node_box_coloured_when_on){ + if (LiteGraph.node_box_coloured_when_on) { colState = node.action_triggered ? "#FFF" : (node.execute_triggered ? "#AAA" : colState); } - - //title box + + // title box var box_size = 10; if (node.onDrawTitleBox) { node.onDrawTitleBox(ctx, title_height, size, this.ds.scale); @@ -9180,26 +9074,25 @@ LGraphNode.prototype.executeAction = function(action) title_height * -0.5, box_size * 0.5 + 1, 0, - Math.PI * 2 + Math.PI * 2, ); ctx.fill(); } - + ctx.fillStyle = node.boxcolor || colState || LiteGraph.NODE_DEFAULT_BOXCOLOR; - if(low_quality) - ctx.fillRect( title_height * 0.5 - box_size *0.5, title_height * -0.5 - box_size *0.5, box_size , box_size ); - else - { - ctx.beginPath(); - ctx.arc( - title_height * 0.5, - title_height * -0.5, - box_size * 0.5, - 0, - Math.PI * 2 - ); - ctx.fill(); - } + if(low_quality) + ctx.fillRect( title_height * 0.5 - box_size *0.5, title_height * -0.5 - box_size *0.5, box_size , box_size ); + else { + ctx.beginPath(); + ctx.arc( + title_height * 0.5, + title_height * -0.5, + box_size * 0.5, + 0, + Math.PI * 2, + ); + ctx.fill(); + } } else { if (low_quality) { ctx.fillStyle = "black"; @@ -9207,7 +9100,7 @@ LGraphNode.prototype.executeAction = function(action) (title_height - box_size) * 0.5 - 1, (title_height + box_size) * -0.5 - 1, box_size + 2, - box_size + 2 + box_size + 2, ); } ctx.fillStyle = node.boxcolor || colState || LiteGraph.NODE_DEFAULT_BOXCOLOR; @@ -9215,12 +9108,12 @@ LGraphNode.prototype.executeAction = function(action) (title_height - box_size) * 0.5, (title_height + box_size) * -0.5, box_size, - box_size + box_size, ); } ctx.globalAlpha = old_alpha; - //title text + // title text if (node.onDrawTitleText) { node.onDrawTitleText( ctx, @@ -9228,7 +9121,7 @@ LGraphNode.prototype.executeAction = function(action) size, this.ds.scale, this.title_text_font, - selected + selected, ); } if (!low_quality) { @@ -9246,9 +9139,9 @@ LGraphNode.prototype.executeAction = function(action) ctx.textAlign = "left"; var measure = ctx.measureText(title); ctx.fillText( - title.substr(0,20), //avoid urls too long + title.substr(0,20), // avoid urls too long title_height,// + measure.width * 0.5, - LiteGraph.NODE_TITLE_TEXT_Y - title_height + LiteGraph.NODE_TITLE_TEXT_Y - title_height, ); ctx.textAlign = "left"; } else { @@ -9256,41 +9149,40 @@ LGraphNode.prototype.executeAction = function(action) ctx.fillText( title, title_height, - LiteGraph.NODE_TITLE_TEXT_Y - title_height + LiteGraph.NODE_TITLE_TEXT_Y - title_height, ); } } } - //subgraph box - if (!node.flags.collapsed && node.subgraph && !node.skip_subgraph_button) { - var w = LiteGraph.NODE_TITLE_HEIGHT; - var x = node.size[0] - w; - var over = LiteGraph.isInsideRectangle( this.graph_mouse[0] - node.pos[0], this.graph_mouse[1] - node.pos[1], x+2, -w+2, w-4, w-4 ); - ctx.fillStyle = over ? "#888" : "#555"; - if( shape == LiteGraph.BOX_SHAPE || low_quality) - ctx.fillRect(x+2, -w+2, w-4, w-4); - else - { - ctx.beginPath(); - ctx.roundRect(x+2, -w+2, w-4, w-4,[4]); - ctx.fill(); - } - ctx.fillStyle = "#333"; - ctx.beginPath(); - ctx.moveTo(x + w * 0.2, -w * 0.6); - ctx.lineTo(x + w * 0.8, -w * 0.6); - ctx.lineTo(x + w * 0.5, -w * 0.3); - ctx.fill(); - } + // subgraph box + if (!node.flags.collapsed && node.subgraph && !node.skip_subgraph_button) { + var w = LiteGraph.NODE_TITLE_HEIGHT; + var x = node.size[0] - w; + var over = LiteGraph.isInsideRectangle( this.graph_mouse[0] - node.pos[0], this.graph_mouse[1] - node.pos[1], x+2, -w+2, w-4, w-4 ); + ctx.fillStyle = over ? "#888" : "#555"; + if( shape == LiteGraph.BOX_SHAPE || low_quality) + ctx.fillRect(x+2, -w+2, w-4, w-4); + else { + ctx.beginPath(); + ctx.roundRect(x+2, -w+2, w-4, w-4,[4]); + ctx.fill(); + } + ctx.fillStyle = "#333"; + ctx.beginPath(); + ctx.moveTo(x + w * 0.2, -w * 0.6); + ctx.lineTo(x + w * 0.8, -w * 0.6); + ctx.lineTo(x + w * 0.5, -w * 0.3); + ctx.fill(); + } - //custom title render + // custom title render if (node.onDrawTitle) { node.onDrawTitle(ctx); } } - //render selection marker + // render selection marker if (selected) { if (node.onBounding) { node.onBounding(area); @@ -9308,7 +9200,7 @@ LGraphNode.prototype.executeAction = function(action) -6 + area[0], -6 + area[1], 12 + area[2], - 12 + area[3] + 12 + area[3], ); } else if ( shape == LiteGraph.ROUND_SHAPE || @@ -9319,7 +9211,7 @@ LGraphNode.prototype.executeAction = function(action) -6 + area[1], 12 + area[2], 12 + area[3], - [this.round_radius * 2] + [this.round_radius * 2], ); } else if (shape == LiteGraph.CARD_SHAPE) { ctx.roundRect( @@ -9327,7 +9219,7 @@ LGraphNode.prototype.executeAction = function(action) -6 + area[1], 12 + area[2], 12 + area[3], - [this.round_radius * 2,2,this.round_radius * 2,2] + [this.round_radius * 2,2,this.round_radius * 2,2], ); } else if (shape == LiteGraph.CIRCLE_SHAPE) { ctx.arc( @@ -9335,7 +9227,7 @@ LGraphNode.prototype.executeAction = function(action) size[1] * 0.5, size[0] * 0.5 + 6, 0, - Math.PI * 2 + Math.PI * 2, ); } ctx.strokeStyle = LiteGraph.NODE_BOX_OUTLINE_COLOR; @@ -9343,7 +9235,7 @@ LGraphNode.prototype.executeAction = function(action) ctx.strokeStyle = fgcolor; ctx.globalAlpha = 1; } - + // these counter helps in conditioning drawing based on if the node has been executed or an action occurred if (node.execute_triggered>0) node.execute_triggered--; if (node.action_triggered>0) node.action_triggered--; @@ -9367,17 +9259,17 @@ LGraphNode.prototype.executeAction = function(action) margin_area[2] = visible_area[2] + 40; margin_area[3] = visible_area[3] + 40; - //draw connections + // draw connections ctx.lineWidth = this.connections_width; ctx.fillStyle = "#AAA"; ctx.strokeStyle = "#AAA"; ctx.globalAlpha = this.editor_alpha; - //for every node + // for every node var nodes = this.graph._nodes; for (var n = 0, l = nodes.length; n < l; ++n) { var node = nodes[n]; - //for every input (we render just inputs because it is easier as every slot can only have one input) + // for every input (we render just inputs because it is easier as every slot can only have one input) if (!node.inputs || !node.inputs.length) { continue; } @@ -9393,7 +9285,7 @@ LGraphNode.prototype.executeAction = function(action) continue; } - //find link info + // find link info var start_node = this.graph.getNodeById(link.origin_id); if (start_node == null) { continue; @@ -9403,18 +9295,18 @@ LGraphNode.prototype.executeAction = function(action) if (start_node_slot == -1) { start_node_slotpos = [ start_node.pos[0] + 10, - start_node.pos[1] + 10 + start_node.pos[1] + 10, ]; } else { start_node_slotpos = start_node.getConnectionPos( false, start_node_slot, - tempA + tempA, ); } var end_node_slotpos = node.getConnectionPos(true, i, tempB); - //compute link bounding + // compute link bounding link_bounding[0] = start_node_slotpos[0]; link_bounding[1] = start_node_slotpos[1]; link_bounding[2] = end_node_slotpos[0] - start_node_slotpos[0]; @@ -9428,7 +9320,7 @@ LGraphNode.prototype.executeAction = function(action) link_bounding[3] = Math.abs(link_bounding[3]); } - //skip links outside of the visible area of the canvas + // skip links outside of the visible area of the canvas if (!overlapBounding(link_bounding, margin_area)) { continue; } @@ -9454,10 +9346,10 @@ LGraphNode.prototype.executeAction = function(action) 0, null, start_dir, - end_dir + end_dir, ); - //event triggered rendered on top + // event triggered rendered on top if (link && link._last_time && now - link._last_time < 1000) { var f = 2.0 - (now - link._last_time) * 0.002; var tmp = ctx.globalAlpha; @@ -9471,7 +9363,7 @@ LGraphNode.prototype.executeAction = function(action) f, "white", start_dir, - end_dir + end_dir, ); ctx.globalAlpha = tmp; } @@ -9503,13 +9395,13 @@ LGraphNode.prototype.executeAction = function(action) color, start_dir, end_dir, - num_sublines + num_sublines, ) { if (link) { this.visible_links.push(link); } - //choose color + // choose color if (!color && link) { color = link.color || LGraphCanvas.link_type_colors[link.type]; } @@ -9534,7 +9426,7 @@ LGraphNode.prototype.executeAction = function(action) ctx.lineWidth = 0.5; } - //begin line shape + // begin line shape ctx.beginPath(); for (var i = 0; i < num_sublines; i += 1) { var offsety = (i - (num_sublines - 1) * 0.5) * 5; @@ -9579,7 +9471,7 @@ LGraphNode.prototype.executeAction = function(action) b[0] + end_offset_x, b[1] + end_offset_y + offsety, b[0], - b[1] + offsety + b[1] + offsety, ); } else if (this.links_render_mode == LiteGraph.LINEAR_LINK) { ctx.moveTo(a[0], a[1] + offsety); @@ -9618,11 +9510,11 @@ LGraphNode.prototype.executeAction = function(action) var l = 15; ctx.lineTo( a[0] + start_offset_x * l, - a[1] + start_offset_y * l + offsety + a[1] + start_offset_y * l + offsety, ); ctx.lineTo( b[0] + end_offset_x * l, - b[1] + end_offset_y * l + offsety + b[1] + end_offset_y * l + offsety, ); ctx.lineTo(b[0], b[1] + offsety); } else if (this.links_render_mode == LiteGraph.STRAIGHT_LINK) { @@ -9648,10 +9540,10 @@ LGraphNode.prototype.executeAction = function(action) ctx.lineTo(b[0], b[1]); } else { return; - } //unknown + } // unknown } - //rendering the outline of the connection can be a little bit slow + // rendering the outline of the connection can be a little bit slow if ( this.render_connections_border && this.ds.scale > 0.6 && @@ -9664,7 +9556,7 @@ LGraphNode.prototype.executeAction = function(action) ctx.lineWidth = this.connections_width; ctx.fillStyle = ctx.strokeStyle = color; ctx.stroke(); - //end line shape + // end line shape var pos = this.computeConnectionPoint(a, b, 0.5, start_dir, end_dir); if (link && link._pos) { @@ -9672,45 +9564,45 @@ LGraphNode.prototype.executeAction = function(action) link._pos[1] = pos[1]; } - //render arrow in the middle + // render arrow in the middle if ( this.ds.scale >= 0.6 && this.highquality_render && end_dir != LiteGraph.CENTER ) { - //render arrow + // render arrow if (this.render_connection_arrows) { - //compute two points in the connection + // compute two points in the connection var posA = this.computeConnectionPoint( a, b, 0.25, start_dir, - end_dir + end_dir, ); var posB = this.computeConnectionPoint( a, b, 0.26, start_dir, - end_dir + end_dir, ); var posC = this.computeConnectionPoint( a, b, 0.75, start_dir, - end_dir + end_dir, ); var posD = this.computeConnectionPoint( a, b, 0.76, start_dir, - end_dir + end_dir, ); - //compute the angle between them so the arrow points in the right direction + // compute the angle between them so the arrow points in the right direction var angleA = 0; var angleB = 0; if (this.render_curved_connections) { @@ -9720,7 +9612,7 @@ LGraphNode.prototype.executeAction = function(action) angleB = angleA = b[1] > a[1] ? 0 : Math.PI; } - //render arrow + // render arrow ctx.save(); ctx.translate(posA[0], posA[1]); ctx.rotate(angleA); @@ -9741,13 +9633,13 @@ LGraphNode.prototype.executeAction = function(action) ctx.restore(); } - //circle + // circle ctx.beginPath(); ctx.arc(pos[0], pos[1], 5, 0, Math.PI * 2); ctx.fill(); } - //render flowing points + // render flowing points if (flow) { ctx.fillStyle = color; for (var i = 0; i < 5; ++i) { @@ -9757,7 +9649,7 @@ LGraphNode.prototype.executeAction = function(action) b, f, start_dir, - end_dir + end_dir, ); ctx.beginPath(); ctx.arc(pos[0], pos[1], 5, 0, 2 * Math.PI); @@ -9766,13 +9658,13 @@ LGraphNode.prototype.executeAction = function(action) } }; - //returns the link center point based on curvature + // returns the link center point based on curvature LGraphCanvas.prototype.computeConnectionPoint = function( a, b, t, start_dir, - end_dir + end_dir, ) { start_dir = start_dir || LiteGraph.RIGHT; end_dir = end_dir || LiteGraph.LEFT; @@ -9838,21 +9730,21 @@ LGraphNode.prototype.executeAction = function(action) node.pos[0] - LiteGraph.NODE_TITLE_HEIGHT, node.pos[1] - LiteGraph.NODE_TITLE_HEIGHT, LiteGraph.NODE_TITLE_HEIGHT, - LiteGraph.NODE_TITLE_HEIGHT + LiteGraph.NODE_TITLE_HEIGHT, ); if (node.order == 0) { ctx.strokeRect( node.pos[0] - LiteGraph.NODE_TITLE_HEIGHT + 0.5, node.pos[1] - LiteGraph.NODE_TITLE_HEIGHT + 0.5, LiteGraph.NODE_TITLE_HEIGHT, - LiteGraph.NODE_TITLE_HEIGHT + LiteGraph.NODE_TITLE_HEIGHT, ); } ctx.fillStyle = "#FFF"; ctx.fillText( node.order, node.pos[0] + LiteGraph.NODE_TITLE_HEIGHT * -0.5, - node.pos[1] - 6 + node.pos[1] - 6, ); } ctx.globalAlpha = 1; @@ -9866,7 +9758,7 @@ LGraphNode.prototype.executeAction = function(action) node, posY, ctx, - active_widget + active_widget, ) { if (!node.widgets || !node.widgets.length) { return 0; @@ -9881,7 +9773,7 @@ LGraphNode.prototype.executeAction = function(action) var outline_color = LiteGraph.WIDGET_OUTLINE_COLOR; var background_color = LiteGraph.WIDGET_BGCOLOR; var text_color = LiteGraph.WIDGET_TEXT_COLOR; - var secondary_text_color = LiteGraph.WIDGET_SECONDARY_TEXT_COLOR; + var secondary_text_color = LiteGraph.WIDGET_SECONDARY_TEXT_COLOR; var margin = 15; for (var i = 0; i < widgets.length; ++i) { @@ -9894,10 +9786,10 @@ LGraphNode.prototype.executeAction = function(action) ctx.strokeStyle = outline_color; ctx.fillStyle = "#222"; ctx.textAlign = "left"; - //ctx.lineWidth = 2; - if(w.disabled) - ctx.globalAlpha *= 0.5; - var widget_width = w.width || width; + // ctx.lineWidth = 2; + if(w.disabled) + ctx.globalAlpha *= 0.5; + var widget_width = w.width || width; switch (w.type) { case "button": @@ -9907,8 +9799,8 @@ LGraphNode.prototype.executeAction = function(action) this.dirty_canvas = true; } ctx.fillRect(margin, y, widget_width - margin * 2, H); - if(show_text && !w.disabled) - ctx.strokeRect( margin, y, widget_width - margin * 2, H ); + if(show_text && !w.disabled) + ctx.strokeRect( margin, y, widget_width - margin * 2, H ); if (show_text) { ctx.textAlign = "center"; ctx.fillStyle = text_color; @@ -9921,19 +9813,19 @@ LGraphNode.prototype.executeAction = function(action) ctx.fillStyle = background_color; ctx.beginPath(); if (show_text) - ctx.roundRect(margin, y, widget_width - margin * 2, H, [H * 0.5]); - else - ctx.rect(margin, y, widget_width - margin * 2, H ); + ctx.roundRect(margin, y, widget_width - margin * 2, H, [H * 0.5]); + else + ctx.rect(margin, y, widget_width - margin * 2, H ); ctx.fill(); - if(show_text && !w.disabled) - ctx.stroke(); + if(show_text && !w.disabled) + ctx.stroke(); ctx.fillStyle = w.value ? "#89A" : "#333"; ctx.beginPath(); ctx.arc( widget_width - margin * 2, y + H * 0.5, H * 0.36, 0, Math.PI * 2 ); ctx.fill(); if (show_text) { ctx.fillStyle = secondary_text_color; - const label = w.label || w.name; + const label = w.label || w.name; if (label != null) { ctx.fillText(label, margin * 2, y + H * 0.7); } @@ -9944,7 +9836,7 @@ LGraphNode.prototype.executeAction = function(action) ? w.options.on || "true" : w.options.off || "false", widget_width - 40, - y + H * 0.7 + y + H * 0.7, ); } break; @@ -9953,16 +9845,16 @@ LGraphNode.prototype.executeAction = function(action) ctx.fillRect(margin, y, widget_width - margin * 2, H); var range = w.options.max - w.options.min; var nvalue = (w.value - w.options.min) / range; - if(nvalue < 0.0) nvalue = 0.0; - if(nvalue > 1.0) nvalue = 1.0; + if(nvalue < 0.0) nvalue = 0.0; + if(nvalue > 1.0) nvalue = 1.0; ctx.fillStyle = w.options.hasOwnProperty("slider_color") ? w.options.slider_color : (active_widget == w ? "#89A" : "#678"); ctx.fillRect(margin, y, nvalue * (widget_width - margin * 2), H); - if(show_text && !w.disabled) - ctx.strokeRect(margin, y, widget_width - margin * 2, H); + if(show_text && !w.disabled) + ctx.strokeRect(margin, y, widget_width - margin * 2, H); if (w.marker) { var marker_nvalue = (w.marker - w.options.min) / range; - if(marker_nvalue < 0.0) marker_nvalue = 0.0; - if(marker_nvalue > 1.0) marker_nvalue = 1.0; + if(marker_nvalue < 0.0) marker_nvalue = 0.0; + if(marker_nvalue > 1.0) marker_nvalue = 1.0; ctx.fillStyle = w.options.hasOwnProperty("marker_color") ? w.options.marker_color : "#AA9"; ctx.fillRect( margin + marker_nvalue * (widget_width - margin * 2), y, 2, H ); } @@ -9970,13 +9862,11 @@ LGraphNode.prototype.executeAction = function(action) ctx.textAlign = "center"; ctx.fillStyle = text_color; ctx.fillText( - w.label || w.name + " " + Number(w.value).toFixed( - w.options.precision != null - ? w.options.precision - : 3 - ), + w.label || w.name + " " + Number(w.value).toFixed(w.options.precision != null + ? w.options.precision + : 3), widget_width * 0.5, - y + H * 0.7 + y + H * 0.7, ); } break; @@ -9986,56 +9876,52 @@ LGraphNode.prototype.executeAction = function(action) ctx.strokeStyle = outline_color; ctx.fillStyle = background_color; ctx.beginPath(); - if(show_text) - ctx.roundRect(margin, y, widget_width - margin * 2, H, [H * 0.5] ); - else - ctx.rect(margin, y, widget_width - margin * 2, H ); + if(show_text) + ctx.roundRect(margin, y, widget_width - margin * 2, H, [H * 0.5] ); + else + ctx.rect(margin, y, widget_width - margin * 2, H ); ctx.fill(); if (show_text) { - if(!w.disabled) - ctx.stroke(); + if(!w.disabled) + ctx.stroke(); ctx.fillStyle = text_color; - if(!w.disabled) - { - ctx.beginPath(); - ctx.moveTo(margin + 16, y + 5); - ctx.lineTo(margin + 6, y + H * 0.5); - ctx.lineTo(margin + 16, y + H - 5); - ctx.fill(); - ctx.beginPath(); - ctx.moveTo(widget_width - margin - 16, y + 5); - ctx.lineTo(widget_width - margin - 6, y + H * 0.5); - ctx.lineTo(widget_width - margin - 16, y + H - 5); - ctx.fill(); - } + if(!w.disabled) { + ctx.beginPath(); + ctx.moveTo(margin + 16, y + 5); + ctx.lineTo(margin + 6, y + H * 0.5); + ctx.lineTo(margin + 16, y + H - 5); + ctx.fill(); + ctx.beginPath(); + ctx.moveTo(widget_width - margin - 16, y + 5); + ctx.lineTo(widget_width - margin - 6, y + H * 0.5); + ctx.lineTo(widget_width - margin - 16, y + H - 5); + ctx.fill(); + } ctx.fillStyle = secondary_text_color; ctx.fillText(w.label || w.name, margin * 2 + 5, y + H * 0.7); ctx.fillStyle = text_color; ctx.textAlign = "right"; if (w.type == "number") { ctx.fillText( - Number(w.value).toFixed( - w.options.precision !== undefined - ? w.options.precision - : 3 - ), + Number(w.value).toFixed(w.options.precision !== undefined + ? w.options.precision + : 3), widget_width - margin * 2 - 20, - y + H * 0.7 + y + H * 0.7, ); } else { - var v = w.value; - if( w.options.values ) - { - var values = w.options.values; - if( values.constructor === Function ) - values = values(); - if(values && values.constructor !== Array) - v = values[ w.value ]; - } + var v = w.value; + if( w.options.values ) { + var values = w.options.values; + if( values.constructor === Function ) + values = values(); + if(values && values.constructor !== Array) + v = values[w.value]; + } ctx.fillText( v, widget_width - margin * 2 - 20, - y + H * 0.7 + y + H * 0.7, ); } } @@ -10047,28 +9933,28 @@ LGraphNode.prototype.executeAction = function(action) ctx.fillStyle = background_color; ctx.beginPath(); if (show_text) - ctx.roundRect(margin, y, widget_width - margin * 2, H, [H * 0.5]); - else - ctx.rect( margin, y, widget_width - margin * 2, H ); + ctx.roundRect(margin, y, widget_width - margin * 2, H, [H * 0.5]); + else + ctx.rect( margin, y, widget_width - margin * 2, H ); ctx.fill(); - if (show_text) { - if(!w.disabled) - ctx.stroke(); - ctx.save(); - ctx.beginPath(); - ctx.rect(margin, y, widget_width - margin * 2, H); - ctx.clip(); - - //ctx.stroke(); + if (show_text) { + if(!w.disabled) + ctx.stroke(); + ctx.save(); + ctx.beginPath(); + ctx.rect(margin, y, widget_width - margin * 2, H); + ctx.clip(); + + // ctx.stroke(); ctx.fillStyle = secondary_text_color; - const label = w.label || w.name; + const label = w.label || w.name; if (label != null) { ctx.fillText(label, margin * 2, y + H * 0.7); } ctx.fillStyle = text_color; ctx.textAlign = "right"; - ctx.fillText(String(w.value).substr(0,30), widget_width - margin * 2, y + H * 0.7); //30 chars max - ctx.restore(); + ctx.fillText(String(w.value).substr(0,30), widget_width - margin * 2, y + H * 0.7); // 30 chars max + ctx.restore(); } break; default: @@ -10078,11 +9964,11 @@ LGraphNode.prototype.executeAction = function(action) break; } posY += (w.computeSize ? w.computeSize(widget_width)[1] : H) + 4; - ctx.globalAlpha = this.editor_alpha; + ctx.globalAlpha = this.editor_alpha; } ctx.restore(); - ctx.textAlign = "left"; + ctx.textAlign = "left"; }; /** @@ -10093,7 +9979,7 @@ LGraphNode.prototype.executeAction = function(action) node, pos, event, - active_widget + active_widget, ) { if (!node.widgets || !node.widgets.length || (!this.allow_interaction && !node.flags.allow_interaction)) { return null; @@ -10108,22 +9994,22 @@ LGraphNode.prototype.executeAction = function(action) for (var i = 0; i < node.widgets.length; ++i) { var w = node.widgets[i]; - if(!w || w.disabled) - continue; - var widget_height = w.computeSize ? w.computeSize(width)[1] : LiteGraph.NODE_WIDGET_HEIGHT; - var widget_width = w.width || width; - //outside - if ( w != active_widget && - (x < 6 || x > widget_width - 12 || y < w.last_y || y > w.last_y + widget_height || w.last_y === undefined) ) - continue; - - var old_value = w.value; - - //if ( w == active_widget || (x > 6 && x < widget_width - 12 && y > w.last_y && y < w.last_y + widget_height) ) { - //inside widget - switch (w.type) { - case "button": - if (event.type === LiteGraph.pointerevents_method+"down") { + if(!w || w.disabled) + continue; + var widget_height = w.computeSize ? w.computeSize(width)[1] : LiteGraph.NODE_WIDGET_HEIGHT; + var widget_width = w.width || width; + // outside + if ( w != active_widget && + (x < 6 || x > widget_width - 12 || y < w.last_y || y > w.last_y + widget_height || w.last_y === undefined) ) + continue; + + var old_value = w.value; + + // if ( w == active_widget || (x > 6 && x < widget_width - 12 && y > w.last_y && y < w.last_y + widget_height) ) { + // inside widget + switch (w.type) { + case "button": + if (event.type === LiteGraph.pointerevents_method+"down") { if (w.callback) { setTimeout(function() { w.callback(w, that, node, pos, event); @@ -10132,150 +10018,155 @@ LGraphNode.prototype.executeAction = function(action) w.clicked = true; this.dirty_canvas = true; } - break; - case "slider": - var old_value = w.value; - var nvalue = clamp((x - 15) / (widget_width - 30), 0, 1); - if(w.options.read_only) break; - w.value = w.options.min + (w.options.max - w.options.min) * nvalue; - if (old_value != w.value) { - setTimeout(function() { - inner_value_change(w, w.value); - }, 20); - } - this.dirty_canvas = true; - break; - case "number": - case "combo": - var old_value = w.value; - if (event.type == LiteGraph.pointerevents_method+"move" && w.type == "number") { + break; + case "slider": + var old_value = w.value; + var nvalue = clamp((x - 15) / (widget_width - 30), 0, 1); + if(w.options.read_only) break; + w.value = w.options.min + (w.options.max - w.options.min) * nvalue; + if (old_value != w.value) { + setTimeout(function() { + inner_value_change(w, w.value); + }, 20); + } + this.dirty_canvas = true; + break; + case "number": + case "combo": + var old_value = w.value; + if (event.type == LiteGraph.pointerevents_method+"move" && w.type == "number") { if(deltaX) - w.value += deltaX * 0.1 * (w.options.step || 1); - if ( w.options.min != null && w.value < w.options.min ) { - w.value = w.options.min; - } - if ( w.options.max != null && w.value > w.options.max ) { - w.value = w.options.max; - } - } else if (event.type == LiteGraph.pointerevents_method+"down") { - var values = w.options.values; - if (values && values.constructor === Function) { - values = w.options.values(w, node); - } - var values_list = null; - - if( w.type != "number") - values_list = values.constructor === Array ? values : Object.keys(values); - - var delta = x < 40 ? -1 : x > widget_width - 40 ? 1 : 0; - if (w.type == "number") { - w.value += delta * 0.1 * (w.options.step || 1); - if ( w.options.min != null && w.value < w.options.min ) { - w.value = w.options.min; - } - if ( w.options.max != null && w.value > w.options.max ) { - w.value = w.options.max; - } - } else if (delta) { //clicked in arrow, used for combos - var index = -1; - this.last_mouseclick = 0; //avoids dobl click event - if(values.constructor === Object) - index = values_list.indexOf( String( w.value ) ) + delta; - else - index = values_list.indexOf( w.value ) + delta; - if (index >= values_list.length) { - index = values_list.length - 1; - } - if (index < 0) { - index = 0; - } - if( values.constructor === Array ) - w.value = values[index]; - else - w.value = index; - } else { //combo clicked - var text_values = values != values_list ? Object.values(values) : values; - var menu = new LiteGraph.ContextMenu(text_values, { - scale: Math.max(1, this.ds.scale), - event: event, - className: "dark", - callback: inner_clicked.bind(w) - }, - ref_window); - function inner_clicked(v, option, event) { - if(values != values_list) - v = text_values.indexOf(v); - this.value = v; - inner_value_change(this, v); - that.dirty_canvas = true; - return false; - } - } - } //end mousedown - else if(event.type == LiteGraph.pointerevents_method+"up" && w.type == "number") - { - var delta = x < 40 ? -1 : x > widget_width - 40 ? 1 : 0; - if (event.click_time < 200 && delta == 0) { - this.prompt("Value",w.value,function(v) { - // check if v is a valid equation or a number - if (/^[0-9+\-*/()\s]+|\d+\.\d+$/.test(v)) { - try {//solve the equation if possible - v = eval(v); - } catch (e) { } - } - this.value = Number(v); - inner_value_change(this, this.value); - }.bind(w), - event); - } - } - - if( old_value != w.value ) - setTimeout( - function() { - inner_value_change(this, this.value); - }.bind(w), - 20 - ); - this.dirty_canvas = true; - break; - case "toggle": - if (event.type == LiteGraph.pointerevents_method+"down") { - w.value = !w.value; - setTimeout(function() { - inner_value_change(w, w.value); - }, 20); - } - break; - case "string": - case "text": - if (event.type == LiteGraph.pointerevents_method+"down") { - this.prompt("Value",w.value,function(v) { - inner_value_change(this, v); - }.bind(w), - event,w.options ? w.options.multiline : false ); - } - break; - default: - if (w.mouse) { - this.dirty_canvas = w.mouse(event, [x, y], node); - } - break; - } //end switch - - //value changed - if( old_value != w.value ) - { - if(node.onWidgetChanged) - node.onWidgetChanged( w.name,w.value,old_value,w ); + w.value += deltaX * 0.1 * (w.options.step || 1); + if ( w.options.min != null && w.value < w.options.min ) { + w.value = w.options.min; + } + if ( w.options.max != null && w.value > w.options.max ) { + w.value = w.options.max; + } + } else if (event.type == LiteGraph.pointerevents_method+"down") { + var values = w.options.values; + if (values && values.constructor === Function) { + values = w.options.values(w, node); + } + var values_list = null; + + if( w.type != "number") + values_list = values.constructor === Array ? values : Object.keys(values); + + var delta = x < 40 ? -1 : x > widget_width - 40 ? 1 : 0; + if (w.type == "number") { + w.value += delta * 0.1 * (w.options.step || 1); + if ( w.options.min != null && w.value < w.options.min ) { + w.value = w.options.min; + } + if ( w.options.max != null && w.value > w.options.max ) { + w.value = w.options.max; + } + } else if (delta) { // clicked in arrow, used for combos + var index = -1; + this.last_mouseclick = 0; // avoids dobl click event + if(values.constructor === Object) + index = values_list.indexOf( String( w.value ) ) + delta; + else + index = values_list.indexOf( w.value ) + delta; + if (index >= values_list.length) { + index = values_list.length - 1; + } + if (index < 0) { + index = 0; + } + if( values.constructor === Array ) + w.value = values[index]; + else + w.value = index; + } else { // combo clicked + var text_values = values != values_list ? Object.values(values) : values; + var menu = new LiteGraph.ContextMenu( + text_values, { + scale: Math.max(1, this.ds.scale), + event: event, + className: "dark", + callback: inner_clicked.bind(w), + }, + ref_window, + ); + function inner_clicked(v, option, event) { + if(values != values_list) + v = text_values.indexOf(v); + this.value = v; + inner_value_change(this, v); + that.dirty_canvas = true; + return false; + } + } + } else if(event.type == LiteGraph.pointerevents_method+"up" && w.type == "number") { // end mousedown + var delta = x < 40 ? -1 : x > widget_width - 40 ? 1 : 0; + if (event.click_time < 200 && delta == 0) { + this.prompt( + "Value",w.value,function(v) { + // check if v is a valid equation or a number + if (/^[0-9+\-*/()\s]+|\d+\.\d+$/.test(v)) { + try { // solve the equation if possible + v = eval(v); + } catch (e) { + console.error(e); + } + } + this.value = Number(v); + inner_value_change(this, this.value); + }.bind(w), + event, + ); + } + } + + if( old_value != w.value ) + setTimeout( + function() { + inner_value_change(this, this.value); + }.bind(w), + 20, + ); + this.dirty_canvas = true; + break; + case "toggle": + if (event.type == LiteGraph.pointerevents_method+"down") { + w.value = !w.value; + setTimeout(function() { + inner_value_change(w, w.value); + }, 20); + } + break; + case "string": + case "text": + if (event.type == LiteGraph.pointerevents_method+"down") { + this.prompt( + "Value",w.value,function(v) { + inner_value_change(this, v); + }.bind(w), + event,w.options ? w.options.multiline : false, + ); + } + break; + default: + if (w.mouse) { + this.dirty_canvas = w.mouse(event, [x, y], node); + } + break; + } // end switch + + // value changed + if( old_value != w.value ) { + if(node.onWidgetChanged) + node.onWidgetChanged( w.name,w.value,old_value,w ); node.graph._version++; - } + } - return w; - }//end for + return w; + }// end for function inner_value_change(widget, value) { - if(widget.type == "number"){ + if(widget.type == "number") { value = Number(value); } widget.value = value; @@ -10309,7 +10200,7 @@ LGraphNode.prototype.executeAction = function(action) if (!overlapBounding(this.visible_area, group._bounding)) { continue; - } //out of the visible area + } // out of the visible area ctx.fillStyle = group.color || "#335"; ctx.strokeStyle = group.color || "#335"; @@ -10331,7 +10222,7 @@ LGraphNode.prototype.executeAction = function(action) var font_size = group.font_size || LiteGraph.DEFAULT_GROUP_FONT_SIZE; ctx.font = font_size + "px Arial"; - ctx.textAlign = "left"; + ctx.textAlign = "left"; ctx.fillText(group.title, pos[0] + 4, pos[1] + font_size); } @@ -10407,12 +10298,12 @@ LGraphNode.prototype.executeAction = function(action) }; LGraphCanvas.prototype.onNodeSelectionChange = function(node) { - return; //disabled + return; // disabled }; /* this is an implementation for touch not in production and not ready */ - /*LGraphCanvas.prototype.touchHandler = function(event) { + /* LGraphCanvas.prototype.touchHandler = function(event) { //alert("foo"); var touches = event.changedTouches, first = touches[0], @@ -10442,7 +10333,7 @@ LGraphNode.prototype.executeAction = function(action) }else{ var window = this.getCanvasWindow(); } - + var document = window.document; var simulatedEvent = document.createEvent("MouseEvent"); @@ -10511,7 +10402,7 @@ LGraphNode.prototype.executeAction = function(action) "top": top, "right": right, "bottom": bottom, - "left": left + "left": left, }; } /** @@ -10542,7 +10433,7 @@ LGraphNode.prototype.executeAction = function(action) "top": align_to, "right": align_to, "bottom": align_to, - "left": align_to + "left": align_to, } } @@ -10599,41 +10490,48 @@ LGraphNode.prototype.executeAction = function(action) if (!graph) return; - function inner_onMenuAdded(base_category ,prev_menu){ - - var categories = LiteGraph.getNodeTypesCategories(canvas.filter || graph.filter).filter(function(category){return category.startsWith(base_category)}); + function inner_onMenuAdded(base_category ,prev_menu) { + + var categories = LiteGraph.getNodeTypesCategories(canvas.filter || graph.filter).filter(function(category) { + return category.startsWith(base_category) + }); var entries = []; - - categories.map(function(category){ - - if (!category) + + categories.map(function(category) { + + if (!category) return; - + var base_category_regex = new RegExp('^(' + base_category + ')'); var category_name = category.replace(base_category_regex,"").split('/')[0]; - var category_path = base_category === '' ? category_name + '/' : base_category + category_name + '/'; - + var category_path = base_category === '' ? category_name + '/' : base_category + category_name + '/'; + var name = category_name; - if(name.indexOf("::") != -1) //in case it has a namespace like "shader::math/rand" it hides the namespace + if(name.indexOf("::") != -1) // in case it has a namespace like "shader::math/rand" it hides the namespace name = name.split("::")[1]; - - var index = entries.findIndex(function(entry){return entry.value === category_path}); + + var index = entries.findIndex(function(entry) { + return entry.value === category_path + }); if (index === -1) { - entries.push({ value: category_path, content: name, has_submenu: true, callback : function(value, event, mouseEvent, contextMenu){ - inner_onMenuAdded(value.value, contextMenu) - }}); + entries.push({ + value: category_path, content: name, has_submenu: true, callback: function(value, event, mouseEvent, contextMenu) { + inner_onMenuAdded(value.value, contextMenu) + }, + }); } - + }); - + var nodes = LiteGraph.getNodeTypesInCategory(base_category.slice(0, -1), canvas.filter || graph.filter ); - nodes.map(function(node){ - + nodes.map(function(node) { + if (node.skip_list) return; - - var entry = { value: node.type, content: node.title, has_submenu: false , callback : function(value, event, mouseEvent, contextMenu){ - + + var entry = { + value: node.type, content: node.title, has_submenu: false , callback: function(value, event, mouseEvent, contextMenu) { + var first_event = contextMenu.getFirstEvent(); canvas.graph.beforeChange(); var node = LiteGraph.createNode(value.value); @@ -10644,21 +10542,21 @@ LGraphNode.prototype.executeAction = function(action) if(callback) callback(node); canvas.graph.afterChange(); - - } + + }, } - + entries.push(entry); - + }); - + new LiteGraph.ContextMenu( entries, { event: e, parentMenu: prev_menu }, ref_window ); - + } - + inner_onMenuAdded('',prev_menu); return false; - + }; LGraphCanvas.onMenuCollapseAll = function() {}; @@ -10670,7 +10568,7 @@ LGraphNode.prototype.executeAction = function(action) options, e, prev_menu, - node + node, ) { if (!node) { return; @@ -10694,14 +10592,14 @@ LGraphNode.prototype.executeAction = function(action) continue; } var label = entry[0]; - if(!entry[2]) - entry[2] = {}; + if(!entry[2]) + entry[2] = {}; if (entry[2].label) { label = entry[2].label; } - entry[2].removable = true; + entry[2].removable = true; var data = { content: label, value: entry }; if (entry[1] == LiteGraph.ACTION) { data.className = "event"; @@ -10716,7 +10614,7 @@ LGraphNode.prototype.executeAction = function(action) } if (!entries.length) { - console.log("no input entries"); + console.log("no input entries"); return; } @@ -10726,9 +10624,9 @@ LGraphNode.prototype.executeAction = function(action) event: e, callback: inner_clicked, parentMenu: prev_menu, - node: node + node: node, }, - ref_window + ref_window, ); function inner_clicked(v, e, prev) { @@ -10741,14 +10639,14 @@ LGraphNode.prototype.executeAction = function(action) } if (v.value) { - node.graph.beforeChange(); + node.graph.beforeChange(); node.addInput(v.value[0], v.value[1], v.value[2]); if (node.onNodeInputAdd) { // callback to the node when adding a slot node.onNodeInputAdd(v.value); } node.setDirtyCanvas(true, true); - node.graph.afterChange(); + node.graph.afterChange(); } } @@ -10760,7 +10658,7 @@ LGraphNode.prototype.executeAction = function(action) options, e, prev_menu, - node + node, ) { if (!node) { return; @@ -10780,7 +10678,7 @@ LGraphNode.prototype.executeAction = function(action) for (var i=0; i < options.length; i++) { var entry = options[i]; if (!entry) { - //separator? + // separator? entries.push(null); continue; } @@ -10791,14 +10689,14 @@ LGraphNode.prototype.executeAction = function(action) node.findOutputSlot(entry[0]) != -1 ) { continue; - } //skip the ones already on + } // skip the ones already on var label = entry[0]; - if(!entry[2]) - entry[2] = {}; + if(!entry[2]) + entry[2] = {}; if (entry[2].label) { label = entry[2].label; } - entry[2].removable = true; + entry[2].removable = true; var data = { content: label, value: entry }; if (entry[1] == LiteGraph.EVENT) { data.className = "event"; @@ -10810,9 +10708,9 @@ LGraphNode.prototype.executeAction = function(action) if (this.onMenuNodeOutputs) { entries = this.onMenuNodeOutputs(entries); } - if (LiteGraph.do_add_triggers_slots){ //canvas.allow_addOutSlot_onExecuted - if (node.findOutputSlot("onExecuted") == -1){ - entries.push({content: "On Executed", value: ["onExecuted", LiteGraph.EVENT, {nameLocked: true}], className: "event"}); //, opts: {} + if (LiteGraph.do_add_triggers_slots) { // canvas.allow_addOutSlot_onExecuted + if (node.findOutputSlot("onExecuted") == -1) { + entries.push({content: "On Executed", value: ["onExecuted", LiteGraph.EVENT, {nameLocked: true}], className: "event"}); // , opts: {} } } // add callback for modifing the menu elements onMenuNodeOutputs @@ -10831,9 +10729,9 @@ LGraphNode.prototype.executeAction = function(action) event: e, callback: inner_clicked, parentMenu: prev_menu, - node: node + node: node, }, - ref_window + ref_window, ); function inner_clicked(v, e, prev) { @@ -10855,7 +10753,7 @@ LGraphNode.prototype.executeAction = function(action) value && (value.constructor === Object || value.constructor === Array) ) { - //submenu why? + // submenu why? var entries = []; for (var i in value) { entries.push({ content: i, value: value[i] }); @@ -10864,18 +10762,18 @@ LGraphNode.prototype.executeAction = function(action) event: e, callback: inner_clicked, parentMenu: prev_menu, - node: node + node: node, }); return false; } else { - node.graph.beforeChange(); + node.graph.beforeChange(); node.addOutput(v.value[0], v.value[1], v.value[2]); if (node.onNodeOutputAdd) { // a callback to the node when adding a slot node.onNodeOutputAdd(v.value); } node.setDirtyCanvas(true, true); - node.graph.afterChange(); + node.graph.afterChange(); } } @@ -10887,7 +10785,7 @@ LGraphNode.prototype.executeAction = function(action) options, e, prev_menu, - node + node, ) { if (!node || !node.properties) { return; @@ -10900,13 +10798,13 @@ LGraphNode.prototype.executeAction = function(action) var entries = []; for (var i in node.properties) { var value = node.properties[i] !== undefined ? node.properties[i] : " "; - if( typeof value == "object" ) - value = JSON.stringify(value); - var info = node.getPropertyInfo(i); - if(info.type == "enum" || info.type == "combo") - value = LGraphCanvas.getPropertyPrintableValue( value, info.values ); + if( typeof value == "object" ) + value = JSON.stringify(value); + var info = node.getPropertyInfo(i); + if(info.type == "enum" || info.type == "combo") + value = LGraphCanvas.getPropertyPrintableValue( value, info.values ); - //value could contain invalid html characters, clean that + // value could contain invalid html characters, clean that value = LGraphCanvas.decodeHTML(value); entries.push({ content: @@ -10916,7 +10814,7 @@ LGraphNode.prototype.executeAction = function(action) "" + value + "", - value: i + value: i, }); } if (!entries.length) { @@ -10930,9 +10828,9 @@ LGraphNode.prototype.executeAction = function(action) callback: inner_clicked, parentMenu: prev_menu, allow_html: true, - node: node + node: node, }, - ref_window + ref_window, ); function inner_clicked(v, options, e, prev) { @@ -10940,9 +10838,7 @@ LGraphNode.prototype.executeAction = function(action) return; } var rect = this.getBoundingClientRect(); - canvas.showEditPropertyValue(node, v.value, { - position: [rect.left, rect.top] - }); + canvas.showEditPropertyValue(node, v.value, {position: [rect.left, rect.top]}); } return false; @@ -10958,65 +10854,65 @@ LGraphNode.prototype.executeAction = function(action) if (!node) { return; } - - var fApplyMultiNode = function(node){ - node.size = node.computeSize(); - if (node.onResize) - node.onResize(node.size); - } - - var graphcanvas = LGraphCanvas.active_canvas; - if (!graphcanvas.selected_nodes || Object.keys(graphcanvas.selected_nodes).length <= 1){ - fApplyMultiNode(node); - }else{ - for (var i in graphcanvas.selected_nodes) { - fApplyMultiNode(graphcanvas.selected_nodes[i]); - } - } - + + var fApplyMultiNode = function(node) { + node.size = node.computeSize(); + if (node.onResize) + node.onResize(node.size); + } + + var graphcanvas = LGraphCanvas.active_canvas; + if (!graphcanvas.selected_nodes || Object.keys(graphcanvas.selected_nodes).length <= 1) { + fApplyMultiNode(node); + }else{ + for (var i in graphcanvas.selected_nodes) { + fApplyMultiNode(graphcanvas.selected_nodes[i]); + } + } + node.setDirtyCanvas(true, true); }; LGraphCanvas.prototype.showLinkMenu = function(link, e) { var that = this; - // console.log(link); - var node_left = that.graph.getNodeById( link.origin_id ); - var node_right = that.graph.getNodeById( link.target_id ); - var fromType = false; - if (node_left && node_left.outputs && node_left.outputs[link.origin_slot]) fromType = node_left.outputs[link.origin_slot].type; + // console.log(link); + var node_left = that.graph.getNodeById( link.origin_id ); + var node_right = that.graph.getNodeById( link.target_id ); + var fromType = false; + if (node_left && node_left.outputs && node_left.outputs[link.origin_slot]) fromType = node_left.outputs[link.origin_slot].type; var destType = false; - if (node_right && node_right.outputs && node_right.outputs[link.target_slot]) destType = node_right.inputs[link.target_slot].type; - - var options = ["Add Node",null,"Delete",null]; - - + if (node_right && node_right.outputs && node_right.outputs[link.target_slot]) destType = node_right.inputs[link.target_slot].type; + + var options = ["Add Node",null,"Delete",null]; + + var menu = new LiteGraph.ContextMenu(options, { event: e, - title: link.data != null ? link.data.constructor.name : null, - callback: inner_clicked + title: link.data != null ? link.data.constructor.name : null, + callback: inner_clicked, }); function inner_clicked(v,options,e) { switch (v) { case "Add Node": - LGraphCanvas.onMenuAdd(null, null, e, menu, function(node){ - // console.debug("node autoconnect"); - if(!node.inputs || !node.inputs.length || !node.outputs || !node.outputs.length){ - return; - } - // leave the connection type checking inside connectByType - if (node_left.connectByType( link.origin_slot, node, fromType )){ - node.connectByType( link.target_slot, node_right, destType ); + LGraphCanvas.onMenuAdd(null, null, e, menu, function(node) { + // console.debug("node autoconnect"); + if(!node.inputs || !node.inputs.length || !node.outputs || !node.outputs.length) { + return; + } + // leave the connection type checking inside connectByType + if (node_left.connectByType( link.origin_slot, node, fromType )) { + node.connectByType( link.target_slot, node_right, destType ); node.pos[0] -= node.size[0] * 0.5; } - }); - break; - + }); + break; + case "Delete": that.graph.removeLink(link.id); break; default: - /*var nodeCreated = createDefaultNodeForSlot({ nodeFrom: node_left + /* var nodeCreated = createDefaultNodeForSlot({ nodeFrom: node_left ,slotFrom: link.origin_slot ,nodeTo: node ,slotTo: link.target_slot @@ -11029,264 +10925,271 @@ LGraphNode.prototype.executeAction = function(action) return false; }; - - LGraphCanvas.prototype.createDefaultNodeForSlot = function(optPass) { // addNodeMenu for connection + + LGraphCanvas.prototype.createDefaultNodeForSlot = function(optPass) { // addNodeMenu for connection var optPass = optPass || {}; - var opts = Object.assign({ nodeFrom: null // input - ,slotFrom: null // input - ,nodeTo: null // output - ,slotTo: null // output - ,position: [] // pass the event coords - ,nodeType: null // choose a nodetype to add, AUTO to set at first good - ,posAdd:[0,0] // adjust x,y - ,posSizeFix:[0,0] // alpha, adjust the position x,y based on the new node size w,h - } - ,optPass - ); + var opts = Object.assign( + { + nodeFrom: null, // input + slotFrom: null, // input + nodeTo: null, // output + slotTo: null, // output + position: [], // pass the event coords + nodeType: null, // choose a nodetype to add, AUTO to set at first good + posAdd: [0,0], // adjust x,y + posSizeFix: [0,0], // alpha, adjust the position x,y based on the new node size w,h + }, + optPass, + ); var that = this; - + var isFrom = opts.nodeFrom && opts.slotFrom!==null; var isTo = !isFrom && opts.nodeTo && opts.slotTo!==null; - - if (!isFrom && !isTo){ + + if (!isFrom && !isTo) { console.warn("No data passed to createDefaultNodeForSlot "+opts.nodeFrom+" "+opts.slotFrom+" "+opts.nodeTo+" "+opts.slotTo); return false; } - if (!opts.nodeType){ + if (!opts.nodeType) { console.warn("No type to createDefaultNodeForSlot"); return false; } - + var nodeX = isFrom ? opts.nodeFrom : opts.nodeTo; var slotX = isFrom ? opts.slotFrom : opts.slotTo; - + var iSlotConn = false; - switch (typeof slotX){ + switch (typeof slotX) { case "string": iSlotConn = isFrom ? nodeX.findOutputSlot(slotX,false) : nodeX.findInputSlot(slotX,false); slotX = isFrom ? nodeX.outputs[slotX] : nodeX.inputs[slotX]; - break; + break; case "object": // ok slotX iSlotConn = isFrom ? nodeX.findOutputSlot(slotX.name) : nodeX.findInputSlot(slotX.name); - break; + break; case "number": iSlotConn = slotX; slotX = isFrom ? nodeX.outputs[slotX] : nodeX.inputs[slotX]; - break; - case "undefined": + break; + case "undefined": default: // bad ? - //iSlotConn = 0; + // iSlotConn = 0; console.warn("Cant get slot information "+slotX); return false; } - - if (slotX===false || iSlotConn===false){ - console.warn("createDefaultNodeForSlot bad slotX "+slotX+" "+iSlotConn); - } - - // check for defaults nodes for this slottype - var fromSlotType = slotX.type==LiteGraph.EVENT?"_event_":slotX.type; - var slotTypesDefault = isFrom ? LiteGraph.slot_types_default_out : LiteGraph.slot_types_default_in; - if(slotTypesDefault && slotTypesDefault[fromSlotType]){ - if (slotX.link !== null) { - // is connected - }else{ - // is not not connected - } - nodeNewType = false; - if(typeof slotTypesDefault[fromSlotType] == "object" || typeof slotTypesDefault[fromSlotType] == "array"){ - for(var typeX in slotTypesDefault[fromSlotType]){ - if (opts.nodeType == slotTypesDefault[fromSlotType][typeX] || opts.nodeType == "AUTO"){ - nodeNewType = slotTypesDefault[fromSlotType][typeX]; - // console.log("opts.nodeType == slotTypesDefault[fromSlotType][typeX] :: "+opts.nodeType); - break; // -------- - } - } - }else{ - if (opts.nodeType == slotTypesDefault[fromSlotType] || opts.nodeType == "AUTO") nodeNewType = slotTypesDefault[fromSlotType]; - } - if (nodeNewType) { - var nodeNewOpts = false; - if (typeof nodeNewType == "object" && nodeNewType.node){ - nodeNewOpts = nodeNewType; - nodeNewType = nodeNewType.node; - } - - //that.graph.beforeChange(); - - var newNode = LiteGraph.createNode(nodeNewType); - if(newNode){ - // if is object pass options - if (nodeNewOpts){ - if (nodeNewOpts.properties) { - for (var i in nodeNewOpts.properties) { - newNode.addProperty( i, nodeNewOpts.properties[i] ); - } - } - if (nodeNewOpts.inputs) { - newNode.inputs = []; - for (var i in nodeNewOpts.inputs) { - newNode.addOutput( - nodeNewOpts.inputs[i][0], - nodeNewOpts.inputs[i][1] - ); - } - } - if (nodeNewOpts.outputs) { - newNode.outputs = []; - for (var i in nodeNewOpts.outputs) { - newNode.addOutput( - nodeNewOpts.outputs[i][0], - nodeNewOpts.outputs[i][1] - ); - } - } - if (nodeNewOpts.title) { - newNode.title = nodeNewOpts.title; - } - if (nodeNewOpts.json) { - newNode.configure(nodeNewOpts.json); - } - - } - - // add the node - that.graph.add(newNode); - newNode.pos = [ opts.position[0]+opts.posAdd[0]+(opts.posSizeFix[0]?opts.posSizeFix[0]*newNode.size[0]:0) - ,opts.position[1]+opts.posAdd[1]+(opts.posSizeFix[1]?opts.posSizeFix[1]*newNode.size[1]:0)]; //that.last_click_position; //[e.canvasX+30, e.canvasX+5];*/ - - //that.graph.afterChange(); - - // connect the two! - if (isFrom){ - opts.nodeFrom.connectByType( iSlotConn, newNode, fromSlotType ); - }else{ - opts.nodeTo.connectByTypeOutput( iSlotConn, newNode, fromSlotType ); - } - - // if connecting in between - if (isFrom && isTo){ - // TODO - } - - return true; - - }else{ - console.log("failed creating "+nodeNewType); - } - } - } - return false; - } - + + if (slotX===false || iSlotConn===false) { + console.warn("createDefaultNodeForSlot bad slotX "+slotX+" "+iSlotConn); + } + + // check for defaults nodes for this slottype + var fromSlotType = slotX.type==LiteGraph.EVENT?"_event_":slotX.type; + var slotTypesDefault = isFrom ? LiteGraph.slot_types_default_out : LiteGraph.slot_types_default_in; + if(slotTypesDefault && slotTypesDefault[fromSlotType]) { + if (slotX.link !== null) { + // is connected + }else{ + // is not not connected + } + nodeNewType = false; + if(Array.isArray(slotTypesDefault[fromSlotType]) || (typeof slotTypesDefault[fromSlotType] === "object")) { + for(var typeX in slotTypesDefault[fromSlotType]) { + if (opts.nodeType == slotTypesDefault[fromSlotType][typeX] || opts.nodeType == "AUTO") { + nodeNewType = slotTypesDefault[fromSlotType][typeX]; + // console.log("opts.nodeType == slotTypesDefault[fromSlotType][typeX] :: "+opts.nodeType); + break; // -------- + } + } + }else{ + if (opts.nodeType == slotTypesDefault[fromSlotType] || opts.nodeType == "AUTO") nodeNewType = slotTypesDefault[fromSlotType]; + } + if (nodeNewType) { + var nodeNewOpts = false; + if (typeof nodeNewType == "object" && nodeNewType.node) { + nodeNewOpts = nodeNewType; + nodeNewType = nodeNewType.node; + } + + // that.graph.beforeChange(); + + var newNode = LiteGraph.createNode(nodeNewType); + if(newNode) { + // if is object pass options + if (nodeNewOpts) { + if (nodeNewOpts.properties) { + for (var i in nodeNewOpts.properties) { + newNode.addProperty( i, nodeNewOpts.properties[i] ); + } + } + if (nodeNewOpts.inputs) { + newNode.inputs = []; + for (var i in nodeNewOpts.inputs) { + newNode.addOutput( + nodeNewOpts.inputs[i][0], + nodeNewOpts.inputs[i][1], + ); + } + } + if (nodeNewOpts.outputs) { + newNode.outputs = []; + for (var i in nodeNewOpts.outputs) { + newNode.addOutput( + nodeNewOpts.outputs[i][0], + nodeNewOpts.outputs[i][1], + ); + } + } + if (nodeNewOpts.title) { + newNode.title = nodeNewOpts.title; + } + if (nodeNewOpts.json) { + newNode.configure(nodeNewOpts.json); + } + + } + + // add the node + that.graph.add(newNode); + newNode.pos = [ + opts.position[0]+opts.posAdd[0]+(opts.posSizeFix[0]?opts.posSizeFix[0]*newNode.size[0]:0), + opts.position[1]+opts.posAdd[1]+(opts.posSizeFix[1]?opts.posSizeFix[1]*newNode.size[1]:0), + ]; // that.last_click_position; //[e.canvasX+30, e.canvasX+5];*/ + + // that.graph.afterChange(); + + // connect the two! + if (isFrom) { + opts.nodeFrom.connectByType( iSlotConn, newNode, fromSlotType ); + }else{ + opts.nodeTo.connectByTypeOutput( iSlotConn, newNode, fromSlotType ); + } + + // if connecting in between + if (isFrom && isTo) { + // TODO + } + + return true; + + }else{ + console.log("failed creating "+nodeNewType); + } + } + } + return false; + } + LGraphCanvas.prototype.showConnectionMenu = function(optPass) { // addNodeMenu for connection var optPass = optPass || {}; - var opts = Object.assign({ nodeFrom: null // input - ,slotFrom: null // input - ,nodeTo: null // output - ,slotTo: null // output - ,e: null - } - ,optPass - ); + var opts = Object.assign( + { + nodeFrom: null, // input + slotFrom: null, // input + nodeTo: null, // output + slotTo: null, // output + e: null, + } + ,optPass, + ); var that = this; - + var isFrom = opts.nodeFrom && opts.slotFrom; var isTo = !isFrom && opts.nodeTo && opts.slotTo; - - if (!isFrom && !isTo){ + + if (!isFrom && !isTo) { console.warn("No data passed to showConnectionMenu"); return false; } - + var nodeX = isFrom ? opts.nodeFrom : opts.nodeTo; var slotX = isFrom ? opts.slotFrom : opts.slotTo; - + var iSlotConn = false; - switch (typeof slotX){ + switch (typeof slotX) { case "string": iSlotConn = isFrom ? nodeX.findOutputSlot(slotX,false) : nodeX.findInputSlot(slotX,false); slotX = isFrom ? nodeX.outputs[slotX] : nodeX.inputs[slotX]; - break; + break; case "object": // ok slotX iSlotConn = isFrom ? nodeX.findOutputSlot(slotX.name) : nodeX.findInputSlot(slotX.name); - break; + break; case "number": iSlotConn = slotX; slotX = isFrom ? nodeX.outputs[slotX] : nodeX.inputs[slotX]; - break; + break; default: // bad ? - //iSlotConn = 0; + // iSlotConn = 0; console.warn("Cant get slot information "+slotX); return false; } - - var options = ["Add Node",null]; - - if (that.allow_searchbox){ - options.push("Search"); - options.push(null); - } - - // get defaults nodes for this slottype - var fromSlotType = slotX.type==LiteGraph.EVENT?"_event_":slotX.type; - var slotTypesDefault = isFrom ? LiteGraph.slot_types_default_out : LiteGraph.slot_types_default_in; - if(slotTypesDefault && slotTypesDefault[fromSlotType]){ - if(typeof slotTypesDefault[fromSlotType] == "object" || typeof slotTypesDefault[fromSlotType] == "array"){ - for(var typeX in slotTypesDefault[fromSlotType]){ - options.push(slotTypesDefault[fromSlotType][typeX]); - } - }else{ - options.push(slotTypesDefault[fromSlotType]); - } - } - - // build menu + + var options = ["Add Node",null]; + + if (that.allow_searchbox) { + options.push("Search"); + options.push(null); + } + + // get defaults nodes for this slottype + var fromSlotType = slotX.type==LiteGraph.EVENT?"_event_":slotX.type; + var slotTypesDefault = isFrom ? LiteGraph.slot_types_default_out : LiteGraph.slot_types_default_in; + if(slotTypesDefault && slotTypesDefault[fromSlotType]) { + if(Array.isArray(slotTypesDefault[fromSlotType]) || (typeof slotTypesDefault[fromSlotType] === "object")) { + for(var typeX in slotTypesDefault[fromSlotType]) { + options.push(slotTypesDefault[fromSlotType][typeX]); + } + }else{ + options.push(slotTypesDefault[fromSlotType]); + } + } + + // build menu var menu = new LiteGraph.ContextMenu(options, { event: opts.e, - title: (slotX && slotX.name!="" ? (slotX.name + (fromSlotType?" | ":"")) : "")+(slotX && fromSlotType ? fromSlotType : ""), - callback: inner_clicked + title: (slotX && slotX.name!="" ? (slotX.name + (fromSlotType?" | ":"")) : "")+(slotX && fromSlotType ? fromSlotType : ""), + callback: inner_clicked, }); - - // callback + + // callback function inner_clicked(v,options,e) { - //console.log("Process showConnectionMenu selection"); + // console.log("Process showConnectionMenu selection"); switch (v) { case "Add Node": - LGraphCanvas.onMenuAdd(null, null, e, menu, function(node){ - if (isFrom){ + LGraphCanvas.onMenuAdd(null, null, e, menu, function(node) { + if (isFrom) { opts.nodeFrom.connectByType( iSlotConn, node, fromSlotType ); }else{ opts.nodeTo.connectByTypeOutput( iSlotConn, node, fromSlotType ); } }); break; - case "Search": - if(isFrom){ - that.showSearchBox(e,{node_from: opts.nodeFrom, slot_from: slotX, type_filter_in: fromSlotType}); - }else{ - that.showSearchBox(e,{node_to: opts.nodeTo, slot_from: slotX, type_filter_out: fromSlotType}); - } - break; + case "Search": + if(isFrom) { + that.showSearchBox(e,{node_from: opts.nodeFrom, slot_from: slotX, type_filter_in: fromSlotType}); + }else{ + that.showSearchBox(e,{node_to: opts.nodeTo, slot_from: slotX, type_filter_out: fromSlotType}); + } + break; default: - // check for defaults nodes for this slottype - var nodeCreated = that.createDefaultNodeForSlot(Object.assign(opts,{ position: [opts.e.canvasX, opts.e.canvasY] - ,nodeType: v - })); - if (nodeCreated){ - // new node created - //console.log("node "+v+" created") - }else{ - // failed or v is not in defaults - } - break; - } - } - + // check for defaults nodes for this slottype + var nodeCreated = that.createDefaultNodeForSlot(Object.assign(opts,{ + position: [opts.e.canvasX, opts.e.canvasY], + nodeType: v, + })); + if (nodeCreated) { + // new node created + // console.log("node "+v+" created") + }else{ + // failed or v is not in defaults + } + break; + } + } + return false; }; @@ -11297,7 +11200,7 @@ LGraphNode.prototype.executeAction = function(action) var value = node[property]; // TODO refactor :: use createDialog ? - + var dialog = document.createElement("div"); dialog.is_modified = false; dialog.className = "graphdialog"; @@ -11319,7 +11222,7 @@ LGraphNode.prototype.executeAction = function(action) input.addEventListener("keydown", function(e) { dialog.is_modified = true; if (e.keyCode == 27) { - //ESC + // ESC dialog.close(); } else if (e.keyCode == 13) { inner(); // save @@ -11355,18 +11258,18 @@ LGraphNode.prototype.executeAction = function(action) canvas.parentNode.appendChild(dialog); if(input) input.focus(); - + var dialogCloseTimer = null; dialog.addEventListener("mouseleave", function(e) { if(LiteGraph.dialog_close_on_mouse_leave) if (!dialog.is_modified && LiteGraph.dialog_close_on_mouse_leave) - dialogCloseTimer = setTimeout(dialog.close, LiteGraph.dialog_close_on_mouse_leave_delay); //dialog.close(); + dialogCloseTimer = setTimeout(dialog.close, LiteGraph.dialog_close_on_mouse_leave_delay); // dialog.close(); }); dialog.addEventListener("mouseenter", function(e) { if(LiteGraph.dialog_close_on_mouse_leave) if(dialogCloseTimer) clearTimeout(dialogCloseTimer); }); - + function inner() { if(input) setValue(input.value); } @@ -11395,9 +11298,9 @@ LGraphNode.prototype.executeAction = function(action) dialog.is_modified = false; dialog.className = "graphdialog rounded"; if(multiline) - dialog.innerHTML = " "; - else - dialog.innerHTML = " "; + dialog.innerHTML = " "; + else + dialog.innerHTML = " "; dialog.close = function() { that.prompt_box = null; if (dialog.parentNode) { @@ -11408,7 +11311,7 @@ LGraphNode.prototype.executeAction = function(action) var graphcanvas = LGraphCanvas.active_canvas; var canvas = graphcanvas.canvas; canvas.parentNode.appendChild(dialog); - + if (this.ds.scale > 1) { dialog.style.transform = "scale(" + this.ds.scale + ")"; } @@ -11420,21 +11323,21 @@ LGraphNode.prototype.executeAction = function(action) return; if(LiteGraph.dialog_close_on_mouse_leave) if (!dialog.is_modified && LiteGraph.dialog_close_on_mouse_leave) - dialogCloseTimer = setTimeout(dialog.close, LiteGraph.dialog_close_on_mouse_leave_delay); //dialog.close(); + dialogCloseTimer = setTimeout(dialog.close, LiteGraph.dialog_close_on_mouse_leave_delay); // dialog.close(); }); LiteGraph.pointerListenerAdd(dialog,"enter", function(e) { if(LiteGraph.dialog_close_on_mouse_leave) if(dialogCloseTimer) clearTimeout(dialogCloseTimer); }); var selInDia = dialog.querySelectorAll("select"); - if (selInDia){ + if (selInDia) { // if filtering, check focus changed to comboboxes and prevent closing selInDia.forEach(function(selIn) { selIn.addEventListener("click", function(e) { prevent_timeout++; }); selIn.addEventListener("blur", function(e) { - prevent_timeout = 0; + prevent_timeout = 0; }); selIn.addEventListener("change", function(e) { prevent_timeout = -1; @@ -11460,7 +11363,7 @@ LGraphNode.prototype.executeAction = function(action) input.addEventListener("keydown", function(e) { dialog.is_modified = true; if (e.keyCode == 27) { - //ESC + // ESC dialog.close(); } else if (e.keyCode == 13 && e.target.localName != "textarea") { if (callback) { @@ -11509,22 +11412,23 @@ LGraphNode.prototype.executeAction = function(action) LGraphCanvas.search_limit = -1; LGraphCanvas.prototype.showSearchBox = function(event, options) { // proposed defaults - var def_options = { slot_from: null - ,node_from: null - ,node_to: null - ,do_type_filter: LiteGraph.search_filter_enabled // TODO check for registered_slot_[in/out]_types not empty // this will be checked for functionality enabled : filter on slot type, in and out - ,type_filter_in: false // these are default: pass to set initially set values - ,type_filter_out: false - ,show_general_if_none_on_typefilter: true - ,show_general_after_typefiltered: true - ,hide_on_mouse_leave: LiteGraph.search_hide_on_mouse_leave - ,show_all_if_empty: true - ,show_all_on_open: LiteGraph.search_show_all_on_open - }; + var def_options = { + slot_from: null, + node_from: null, + node_to: null, + do_type_filter: LiteGraph.search_filter_enabled, // TODO check for registered_slot_[in/out]_types not empty // this will be checked for functionality enabled : filter on slot type, in and out + type_filter_in: false, // these are default: pass to set initially set values + type_filter_out: false, + show_general_if_none_on_typefilter: true, + show_general_after_typefiltered: true, + hide_on_mouse_leave: LiteGraph.search_hide_on_mouse_leave, + show_all_if_empty: true, + show_all_on_open: LiteGraph.search_show_all_on_open, + }; options = Object.assign(def_options, options || {}); - - //console.log(options); - + + // console.log(options); + var that = this; var input_html = ""; var graphcanvas = LGraphCanvas.active_canvas; @@ -11534,35 +11438,34 @@ LGraphNode.prototype.executeAction = function(action) var dialog = document.createElement("div"); dialog.className = "litegraph litesearchbox graphdialog rounded"; dialog.innerHTML = "Search "; - if (options.do_type_filter){ + if (options.do_type_filter) { dialog.innerHTML += ""; dialog.innerHTML += ""; } dialog.innerHTML += "
"; - + if( root_document.fullscreenElement ) - root_document.fullscreenElement.appendChild(dialog); - else - { - root_document.body.appendChild(dialog); - root_document.body.style.overflow = "hidden"; - } + root_document.fullscreenElement.appendChild(dialog); + else { + root_document.body.appendChild(dialog); + root_document.body.style.overflow = "hidden"; + } // dialog element has been appended - - if (options.do_type_filter){ + + if (options.do_type_filter) { var selIn = dialog.querySelector(".slot_in_type_filter"); var selOut = dialog.querySelector(".slot_out_type_filter"); } - + dialog.close = function() { that.search_box = null; - this.blur(); + this.blur(); canvas.focus(); - root_document.body.style.overflow = ""; + root_document.body.style.overflow = ""; setTimeout(function() { that.canvas.focus(); - }, 20); //important, if canvas loses focus keys wont be captured + }, 20); // important, if canvas loses focus keys wont be captured if (dialog.parentNode) { dialog.parentNode.removeChild(dialog); } @@ -11573,7 +11476,7 @@ LGraphNode.prototype.executeAction = function(action) } // hide on mouse leave - if(options.hide_on_mouse_leave){ + if(options.hide_on_mouse_leave) { var prevent_timeout = false; var timeout_close = null; LiteGraph.pointerListenerAdd(dialog,"enter", function(e) { @@ -11583,7 +11486,7 @@ LGraphNode.prototype.executeAction = function(action) } }); LiteGraph.pointerListenerAdd(dialog,"leave", function(e) { - if (prevent_timeout){ + if (prevent_timeout) { return; } timeout_close = setTimeout(function() { @@ -11591,12 +11494,12 @@ LGraphNode.prototype.executeAction = function(action) }, 500); }); // if filtering, check focus changed to comboboxes and prevent closing - if (options.do_type_filter){ + if (options.do_type_filter) { selIn.addEventListener("click", function(e) { prevent_timeout++; }); selIn.addEventListener("blur", function(e) { - prevent_timeout = 0; + prevent_timeout = 0; }); selIn.addEventListener("change", function(e) { prevent_timeout = -1; @@ -11605,7 +11508,7 @@ LGraphNode.prototype.executeAction = function(action) prevent_timeout++; }); selOut.addEventListener("blur", function(e) { - prevent_timeout = 0; + prevent_timeout = 0; }); selOut.addEventListener("change", function(e) { prevent_timeout = -1; @@ -11632,13 +11535,13 @@ LGraphNode.prototype.executeAction = function(action) }); input.addEventListener("keydown", function(e) { if (e.keyCode == 38) { - //UP + // UP changeSelection(false); } else if (e.keyCode == 40) { - //DOWN + // DOWN changeSelection(true); } else if (e.keyCode == 27) { - //ESC + // ESC dialog.close(); } else if (e.keyCode == 13) { refreshHelper(); @@ -11658,67 +11561,67 @@ LGraphNode.prototype.executeAction = function(action) } e.preventDefault(); e.stopPropagation(); - e.stopImmediatePropagation(); - return true; + e.stopImmediatePropagation(); + return true; }); } - + // if should filter on type, load and fill selected and choose elements if passed - if (options.do_type_filter){ - if (selIn){ + if (options.do_type_filter) { + if (selIn) { var aSlots = LiteGraph.slot_types_in; var nSlots = aSlots.length; // this for object :: Object.keys(aSlots).length; - + if (options.type_filter_in == LiteGraph.EVENT || options.type_filter_in == LiteGraph.ACTION) options.type_filter_in = "_event_"; /* this will filter on * .. but better do it manually in case else if(options.type_filter_in === "" || options.type_filter_in === 0) options.type_filter_in = "*";*/ - - for (var iK=0; iK (rect.height - 200)) + // To avoid out of screen problems + if(event.layerY > (rect.height - 200)) helper.style.maxHeight = (rect.height - event.layerY - 20) + "px"; - /* + /* var offsetx = -20; var offsety = -20; if (rect) { @@ -11761,12 +11664,10 @@ LGraphNode.prototype.executeAction = function(action) name = extra.type; } - graphcanvas.graph.beforeChange(); + graphcanvas.graph.beforeChange(); var node = LiteGraph.createNode(name); if (node) { - node.pos = graphcanvas.convertEventToCanvasOffset( - event - ); + node.pos = graphcanvas.convertEventToCanvasOffset(event); graphcanvas.graph.add(node, false); } @@ -11781,7 +11682,7 @@ LGraphNode.prototype.executeAction = function(action) for (var i in extra.data.inputs) { node.addOutput( extra.data.inputs[i][0], - extra.data.inputs[i][1] + extra.data.inputs[i][1], ); } } @@ -11790,7 +11691,7 @@ LGraphNode.prototype.executeAction = function(action) for (var i in extra.data.outputs) { node.addOutput( extra.data.outputs[i][0], - extra.data.outputs[i][1] + extra.data.outputs[i][1], ); } } @@ -11804,56 +11705,56 @@ LGraphNode.prototype.executeAction = function(action) } // join node after inserting - if (options.node_from){ + if (options.node_from) { var iS = false; - switch (typeof options.slot_from){ + switch (typeof options.slot_from) { case "string": - iS = options.node_from.findOutputSlot(options.slot_from); - break; + iS = options.node_from.findOutputSlot(options.slot_from); + break; case "object": - if (options.slot_from.name){ + if (options.slot_from.name) { iS = options.node_from.findOutputSlot(options.slot_from.name); }else{ iS = -1; } if (iS==-1 && typeof options.slot_from.slot_index !== "undefined") iS = options.slot_from.slot_index; - break; + break; case "number": iS = options.slot_from; - break; + break; default: iS = 0; // try with first if no name set } - if (typeof options.node_from.outputs[iS] !== "undefined"){ - if (iS!==false && iS>-1){ + if (typeof options.node_from.outputs[iS] !== "undefined") { + if (iS!==false && iS>-1) { options.node_from.connectByType( iS, node, options.node_from.outputs[iS].type ); } }else{ // console.warn("cant find slot " + options.slot_from); } } - if (options.node_to){ + if (options.node_to) { var iS = false; - switch (typeof options.slot_from){ + switch (typeof options.slot_from) { case "string": - iS = options.node_to.findInputSlot(options.slot_from); - break; + iS = options.node_to.findInputSlot(options.slot_from); + break; case "object": - if (options.slot_from.name){ + if (options.slot_from.name) { iS = options.node_to.findInputSlot(options.slot_from.name); }else{ iS = -1; } if (iS==-1 && typeof options.slot_from.slot_index !== "undefined") iS = options.slot_from.slot_index; - break; + break; case "number": iS = options.slot_from; - break; + break; default: iS = 0; // try with first if no name set } - if (typeof options.node_to.inputs[iS] !== "undefined"){ - if (iS!==false && iS>-1){ + if (typeof options.node_to.inputs[iS] !== "undefined") { + if (iS!==false && iS>-1) { // try connection options.node_to.connectByTypeOutput(iS,node,options.node_to.inputs[iS].type); } @@ -11861,7 +11762,7 @@ LGraphNode.prototype.executeAction = function(action) // console.warn("cant find slot_nodeTO " + options.slot_from); } } - + graphcanvas.graph.afterChange(); } } @@ -11912,26 +11813,26 @@ LGraphNode.prototype.executeAction = function(action) } else { var c = 0; str = str.toLowerCase(); - var filter = graphcanvas.filter || graphcanvas.graph.filter; + var filter = graphcanvas.filter || graphcanvas.graph.filter; // filter by type preprocess - if(options.do_type_filter && that.search_box){ + if(options.do_type_filter && that.search_box) { var sIn = that.search_box.querySelector(".slot_in_type_filter"); var sOut = that.search_box.querySelector(".slot_out_type_filter"); }else{ var sIn = false; var sOut = false; } - - //extras + + // extras for (var i in LiteGraph.searchbox_extras) { var extra = LiteGraph.searchbox_extras[i]; if ((!options.show_all_if_empty || str) && extra.desc.toLowerCase().indexOf(str) === -1) { continue; } - var ctor = LiteGraph.registered_node_types[ extra.type ]; - if( ctor && ctor.filter != filter ) - continue; + var ctor = LiteGraph.registered_node_types[extra.type]; + if( ctor && ctor.filter != filter ) + continue; if( ! inner_test_filter(extra.type) ) continue; addResult( extra.desc, "searchbox_extra" ); @@ -11940,33 +11841,33 @@ LGraphNode.prototype.executeAction = function(action) } } - var filtered = null; - if (Array.prototype.filter) { //filter supported - var keys = Object.keys( LiteGraph.registered_node_types ); //types + var filtered = null; + if (Array.prototype.filter) { // filter supported + var keys = Object.keys( LiteGraph.registered_node_types ); // types var filtered = keys.filter( inner_test_filter ); } else { - filtered = []; + filtered = []; for (var i in LiteGraph.registered_node_types) { - if( inner_test_filter(i) ) - filtered.push(i); + if( inner_test_filter(i) ) + filtered.push(i); + } + } + + for (var i = 0; i < filtered.length; i++) { + addResult(filtered[i]); + if ( LGraphCanvas.search_limit !== -1 && c++ > LGraphCanvas.search_limit ) { + break; } } - for (var i = 0; i < filtered.length; i++) { - addResult(filtered[i]); - if ( LGraphCanvas.search_limit !== -1 && c++ > LGraphCanvas.search_limit ) { - break; - } - } - // add general type if filtering if (options.show_general_after_typefiltered - && (sIn.value || sOut.value) - ){ + && (sIn.value || sOut.value) + ) { filtered_extra = []; for (var i in LiteGraph.registered_node_types) { - if( inner_test_filter(i, {inTypeOverride: sIn&&sIn.value?"*":false, outTypeOverride: sOut&&sOut.value?"*":false}) ) - filtered_extra.push(i); + if( inner_test_filter(i, {inTypeOverride: sIn&&sIn.value?"*":false, outTypeOverride: sOut&&sOut.value?"*":false}) ) + filtered_extra.push(i); } for (var i = 0; i < filtered_extra.length; i++) { addResult(filtered_extra[i], "generic_type"); @@ -11975,15 +11876,15 @@ LGraphNode.prototype.executeAction = function(action) } } } - + // check il filtering gave no results - if ((sIn.value || sOut.value) && + if ((sIn.value || sOut.value) && ( (helper.childNodes.length == 0 && options.show_general_if_none_on_typefilter) ) - ){ + ) { filtered_extra = []; for (var i in LiteGraph.registered_node_types) { - if( inner_test_filter(i, {skipFilter: true}) ) - filtered_extra.push(i); + if( inner_test_filter(i, {skipFilter: true}) ) + filtered_extra.push(i); } for (var i = 0; i < filtered_extra.length; i++) { addResult(filtered_extra[i], "not_in_filter"); @@ -11992,57 +11893,57 @@ LGraphNode.prototype.executeAction = function(action) } } } - - function inner_test_filter( type, optsIn ) - { + + function inner_test_filter( type, optsIn ) { var optsIn = optsIn || {}; - var optsDef = { skipFilter: false - ,inTypeOverride: false - ,outTypeOverride: false - }; + var optsDef = { + skipFilter: false, + inTypeOverride: false, + outTypeOverride: false, + }; var opts = Object.assign(optsDef,optsIn); - var ctor = LiteGraph.registered_node_types[ type ]; - if(filter && ctor.filter != filter ) - return false; + var ctor = LiteGraph.registered_node_types[type]; + if(filter && ctor.filter != filter ) + return false; if ((!options.show_all_if_empty || str) && type.toLowerCase().indexOf(str) === -1) return false; - + // filter by slot IN, OUT types - if(options.do_type_filter && !opts.skipFilter){ + if(options.do_type_filter && !opts.skipFilter) { var sType = type; - + var sV = sIn.value; if (opts.inTypeOverride!==false) sV = opts.inTypeOverride; - //if (sV.toLowerCase() == "_event_") sV = LiteGraph.EVENT; // -1 - - if(sIn && sV){ - //console.log("will check filter against "+sV); - if (LiteGraph.registered_slot_in_types[sV] && LiteGraph.registered_slot_in_types[sV].nodes){ // type is stored - //console.debug("check "+sType+" in "+LiteGraph.registered_slot_in_types[sV].nodes); + // if (sV.toLowerCase() == "_event_") sV = LiteGraph.EVENT; // -1 + + if(sIn && sV) { + // console.log("will check filter against "+sV); + if (LiteGraph.registered_slot_in_types[sV] && LiteGraph.registered_slot_in_types[sV].nodes) { // type is stored + // console.debug("check "+sType+" in "+LiteGraph.registered_slot_in_types[sV].nodes); var doesInc = LiteGraph.registered_slot_in_types[sV].nodes.includes(sType); - if (doesInc!==false){ - //console.log(sType+" HAS "+sV); + if (doesInc!==false) { + // console.log(sType+" HAS "+sV); }else{ - /*console.debug(LiteGraph.registered_slot_in_types[sV]); + /* console.debug(LiteGraph.registered_slot_in_types[sV]); console.log(+" DONT includes "+type);*/ return false; } } } - + var sV = sOut.value; if (opts.outTypeOverride!==false) sV = opts.outTypeOverride; - //if (sV.toLowerCase() == "_event_") sV = LiteGraph.EVENT; // -1 - - if(sOut && sV){ - //console.log("search will check filter against "+sV); - if (LiteGraph.registered_slot_out_types[sV] && LiteGraph.registered_slot_out_types[sV].nodes){ // type is stored - //console.debug("check "+sType+" in "+LiteGraph.registered_slot_out_types[sV].nodes); + // if (sV.toLowerCase() == "_event_") sV = LiteGraph.EVENT; // -1 + + if(sOut && sV) { + // console.log("search will check filter against "+sV); + if (LiteGraph.registered_slot_out_types[sV] && LiteGraph.registered_slot_out_types[sV].nodes) { // type is stored + // console.debug("check "+sType+" in "+LiteGraph.registered_slot_out_types[sV].nodes); var doesInc = LiteGraph.registered_slot_out_types[sV].nodes.includes(sType); - if (doesInc!==false){ - //console.log(sType+" HAS "+sV); + if (doesInc!==false) { + // console.log(sType+" HAS "+sV); }else{ - /*console.debug(LiteGraph.registered_slot_out_types[sV]); + /* console.debug(LiteGraph.registered_slot_out_types[sV]); console.log(+" DONT includes "+type);*/ return false; } @@ -12050,7 +11951,7 @@ LGraphNode.prototype.executeAction = function(action) } } return true; - } + } } function addResult(type, className) { @@ -12083,7 +11984,7 @@ LGraphNode.prototype.executeAction = function(action) var that = this; var info = node.getPropertyInfo(property); - var type = info.type; + var type = info.type; var input_html = ""; @@ -12093,8 +11994,8 @@ LGraphNode.prototype.executeAction = function(action) input_html = ""; - var textarea = panel.alt_content.querySelector("textarea"); - var fDoneWith = function(){ - panel.toggleAltContent(false); //if(node_prop_div) node_prop_div.style.display = "block"; // panel.close(); - panel.toggleFooterVisibility(true); - textarea.parentNode.removeChild(textarea); - panel.classList.add("settings"); - panel.classList.remove("centered"); - inner_refresh(); - } - textarea.value = node.properties[propname]; - textarea.addEventListener("keydown", function(e){ - if(e.code == "Enter" && e.ctrlKey ) - { - node.setProperty(propname, textarea.value); - fDoneWith(); - } - }); - panel.toggleAltContent(true); - panel.toggleFooterVisibility(false); - textarea.style.height = "calc(100% - 40px)"; - /*}*/ - var assign = panel.addButton( "Assign", function(){ - node.setProperty(propname, textarea.value); + panel.alt_content.innerHTML = ""; + var textarea = panel.alt_content.querySelector("textarea"); + var fDoneWith = function() { + panel.toggleAltContent(false); // if(node_prop_div) node_prop_div.style.display = "block"; // panel.close(); + panel.toggleFooterVisibility(true); + textarea.parentNode.removeChild(textarea); + panel.classList.add("settings"); + panel.classList.remove("centered"); + inner_refresh(); + } + textarea.value = node.properties[propname]; + textarea.addEventListener("keydown", function(e) { + if(e.code == "Enter" && e.ctrlKey ) { + node.setProperty(propname, textarea.value); + fDoneWith(); + } + }); + panel.toggleAltContent(true); + panel.toggleFooterVisibility(false); + textarea.style.height = "calc(100% - 40px)"; + /* }*/ + var assign = panel.addButton( "Assign", function() { + node.setProperty(propname, textarea.value); fDoneWith(); - }); - panel.alt_content.appendChild(assign); //panel.content.appendChild(assign); - var button = panel.addButton( "Close", fDoneWith); - button.style.float = "right"; - panel.alt_content.appendChild(button); // panel.content.appendChild(button); - } + }); + panel.alt_content.appendChild(assign); // panel.content.appendChild(assign); + var button = panel.addButton( "Close", fDoneWith); + button.style.float = "right"; + panel.alt_content.appendChild(button); // panel.content.appendChild(button); + } - inner_refresh(); + inner_refresh(); - this.canvas.parentNode.appendChild( panel ); - } - - LGraphCanvas.prototype.showSubgraphPropertiesDialog = function(node) - { - console.log("showing subgraph properties dialog"); + this.canvas.parentNode.appendChild( panel ); + } - var old_panel = this.canvas.parentNode.querySelector(".subgraph_dialog"); - if(old_panel) - old_panel.close(); + LGraphCanvas.prototype.showSubgraphPropertiesDialog = function(node) { + console.log("showing subgraph properties dialog"); + + var old_panel = this.canvas.parentNode.querySelector(".subgraph_dialog"); + if(old_panel) + old_panel.close(); - var panel = this.createPanel("Subgraph Inputs",{closable:true, width: 500}); - panel.node = node; - panel.classList.add("subgraph_dialog"); + var panel = this.createPanel("Subgraph Inputs",{closable: true, width: 500}); + panel.node = node; + panel.classList.add("subgraph_dialog"); - function inner_refresh() - { - panel.clear(); - - //show currents - if(node.inputs) - for(var i = 0; i < node.inputs.length; ++i) - { - var input = node.inputs[i]; - if(input.not_subgraph_input) - continue; - var html = " "; - var elem = panel.addHTML(html,"subgraph_property"); - elem.dataset["name"] = input.name; - elem.dataset["slot"] = i; - elem.querySelector(".name").innerText = input.name; - elem.querySelector(".type").innerText = input.type; - elem.querySelector("button").addEventListener("click",function(e){ - node.removeInput( Number( this.parentNode.dataset["slot"] ) ); - inner_refresh(); - }); - } - } + function inner_refresh() { + panel.clear(); - //add extra - var html = " + NameType"; - var elem = panel.addHTML(html,"subgraph_property extra", true); - elem.querySelector("button").addEventListener("click", function(e){ - var elem = this.parentNode; - var name = elem.querySelector(".name").value; - var type = elem.querySelector(".type").value; - if(!name || node.findInputSlot(name) != -1) - return; - node.addInput(name,type); - elem.querySelector(".name").value = ""; - elem.querySelector(".type").value = ""; - inner_refresh(); - }); - - inner_refresh(); - this.canvas.parentNode.appendChild(panel); - return panel; - } + // show currents + if(node.inputs) + for(var i = 0; i < node.inputs.length; ++i) { + var input = node.inputs[i]; + if(input.not_subgraph_input) + continue; + var html = " "; + var elem = panel.addHTML(html,"subgraph_property"); + elem.dataset["name"] = input.name; + elem.dataset["slot"] = i; + elem.querySelector(".name").innerText = input.name; + elem.querySelector(".type").innerText = input.type; + elem.querySelector("button").addEventListener("click",function(e) { + node.removeInput( Number( this.parentNode.dataset["slot"] ) ); + inner_refresh(); + }); + } + } + + // add extra + var html = " + NameType"; + var elem = panel.addHTML(html,"subgraph_property extra", true); + elem.querySelector("button").addEventListener("click", function(e) { + var elem = this.parentNode; + var name = elem.querySelector(".name").value; + var type = elem.querySelector(".type").value; + if(!name || node.findInputSlot(name) != -1) + return; + node.addInput(name,type); + elem.querySelector(".name").value = ""; + elem.querySelector(".type").value = ""; + inner_refresh(); + }); + + inner_refresh(); + this.canvas.parentNode.appendChild(panel); + return panel; + } LGraphCanvas.prototype.showSubgraphPropertiesDialogRight = function (node) { // console.log("showing subgraph properties dialog"); @@ -12847,7 +12729,7 @@ LGraphNode.prototype.executeAction = function(action) function inner_refresh() { panel.clear(); - //show currents + // show currents if (node.outputs) for (var i = 0; i < node.outputs.length; ++i) { var input = node.outputs[i]; @@ -12866,7 +12748,7 @@ LGraphNode.prototype.executeAction = function(action) } } - //add extra + // add extra var html = " + NameType"; var elem = panel.addHTML(html, "subgraph_property extra", true); elem.querySelector(".name").addEventListener("keydown", function (e) { @@ -12893,38 +12775,36 @@ LGraphNode.prototype.executeAction = function(action) this.canvas.parentNode.appendChild(panel); return panel; } - LGraphCanvas.prototype.checkPanels = function() - { - if(!this.canvas) - return; - var panels = this.canvas.parentNode.querySelectorAll(".litegraph.dialog"); - for(var i = 0; i < panels.length; ++i) - { - var panel = panels[i]; - if( !panel.node ) - continue; - if( !panel.node.graph || panel.graph != this.graph ) - panel.close(); - } - } + LGraphCanvas.prototype.checkPanels = function() { + if(!this.canvas) + return; + var panels = this.canvas.parentNode.querySelectorAll(".litegraph.dialog"); + for(var i = 0; i < panels.length; ++i) { + var panel = panels[i]; + if( !panel.node ) + continue; + if( !panel.node.graph || panel.graph != this.graph ) + panel.close(); + } + } LGraphCanvas.onMenuNodeCollapse = function(value, options, e, menu, node) { - node.graph.beforeChange(/*?*/); - - var fApplyMultiNode = function(node){ - node.collapse(); - } - - var graphcanvas = LGraphCanvas.active_canvas; - if (!graphcanvas.selected_nodes || Object.keys(graphcanvas.selected_nodes).length <= 1){ - fApplyMultiNode(node); - }else{ - for (var i in graphcanvas.selected_nodes) { - fApplyMultiNode(graphcanvas.selected_nodes[i]); - } - } - - node.graph.afterChange(/*?*/); + node.graph.beforeChange(/* ?*/); + + var fApplyMultiNode = function(node) { + node.collapse(); + } + + var graphcanvas = LGraphCanvas.active_canvas; + if (!graphcanvas.selected_nodes || Object.keys(graphcanvas.selected_nodes).length <= 1) { + fApplyMultiNode(node); + }else{ + for (var i in graphcanvas.selected_nodes) { + fApplyMultiNode(graphcanvas.selected_nodes[i]); + } + } + + node.graph.afterChange(/* ?*/); }; LGraphCanvas.onMenuNodePin = function(value, options, e, menu, node) { @@ -12934,7 +12814,7 @@ LGraphNode.prototype.executeAction = function(action) LGraphCanvas.onMenuNodeMode = function(value, options, e, menu, node) { new LiteGraph.ContextMenu( LiteGraph.NODE_MODES, - { event: e, callback: inner_clicked, parentMenu: menu, node: node } + { event: e, callback: inner_clicked, parentMenu: menu, node: node }, ); function inner_clicked(v) { @@ -12942,23 +12822,23 @@ LGraphNode.prototype.executeAction = function(action) return; } var kV = Object.values(LiteGraph.NODE_MODES).indexOf(v); - var fApplyMultiNode = function(node){ - if (kV>=0 && LiteGraph.NODE_MODES[kV]) - node.changeMode(kV); - else{ - console.warn("unexpected mode: "+v); - node.changeMode(LiteGraph.ALWAYS); - } - } - - var graphcanvas = LGraphCanvas.active_canvas; - if (!graphcanvas.selected_nodes || Object.keys(graphcanvas.selected_nodes).length <= 1){ - fApplyMultiNode(node); - }else{ - for (var i in graphcanvas.selected_nodes) { - fApplyMultiNode(graphcanvas.selected_nodes[i]); - } - } + var fApplyMultiNode = function(node) { + if (kV>=0 && LiteGraph.NODE_MODES[kV]) + node.changeMode(kV); + else{ + console.warn("unexpected mode: "+v); + node.changeMode(LiteGraph.ALWAYS); + } + } + + var graphcanvas = LGraphCanvas.active_canvas; + if (!graphcanvas.selected_nodes || Object.keys(graphcanvas.selected_nodes).length <= 1) { + fApplyMultiNode(node); + }else{ + for (var i in graphcanvas.selected_nodes) { + fApplyMultiNode(graphcanvas.selected_nodes[i]); + } + } } return false; @@ -12973,7 +12853,7 @@ LGraphNode.prototype.executeAction = function(action) values.push({ value: null, content: - "No color" + "No color", }); for (var i in LGraphCanvas.node_colors) { @@ -12987,7 +12867,7 @@ LGraphNode.prototype.executeAction = function(action) color.bgcolor + "'>" + i + - "" + "", }; values.push(value); } @@ -12995,7 +12875,7 @@ LGraphNode.prototype.executeAction = function(action) event: e, callback: inner_clicked, parentMenu: menu, - node: node + node: node, }); function inner_clicked(v) { @@ -13004,29 +12884,29 @@ LGraphNode.prototype.executeAction = function(action) } var color = v.value ? LGraphCanvas.node_colors[v.value] : null; - - var fApplyColor = function(node){ - if (color) { - if (node.constructor === LiteGraph.LGraphGroup) { - node.color = color.groupcolor; - } else { - node.color = color.color; - node.bgcolor = color.bgcolor; - } - } else { - delete node.color; - delete node.bgcolor; - } - } - - var graphcanvas = LGraphCanvas.active_canvas; - if (!graphcanvas.selected_nodes || Object.keys(graphcanvas.selected_nodes).length <= 1){ - fApplyColor(node); - }else{ - for (var i in graphcanvas.selected_nodes) { - fApplyColor(graphcanvas.selected_nodes[i]); - } - } + + var fApplyColor = function(node) { + if (color) { + if (node.constructor === LiteGraph.LGraphGroup) { + node.color = color.groupcolor; + } else { + node.color = color.color; + node.bgcolor = color.bgcolor; + } + } else { + delete node.color; + delete node.bgcolor; + } + } + + var graphcanvas = LGraphCanvas.active_canvas; + if (!graphcanvas.selected_nodes || Object.keys(graphcanvas.selected_nodes).length <= 1) { + fApplyColor(node); + }else{ + for (var i in graphcanvas.selected_nodes) { + fApplyColor(graphcanvas.selected_nodes[i]); + } + } node.setDirtyCanvas(true, true); } @@ -13042,29 +12922,29 @@ LGraphNode.prototype.executeAction = function(action) event: e, callback: inner_clicked, parentMenu: menu, - node: node + node: node, }); function inner_clicked(v) { if (!node) { return; } - node.graph.beforeChange(/*?*/); //node - - var fApplyMultiNode = function(node){ - node.shape = v; - } + node.graph.beforeChange(/* ?*/); // node - var graphcanvas = LGraphCanvas.active_canvas; - if (!graphcanvas.selected_nodes || Object.keys(graphcanvas.selected_nodes).length <= 1){ - fApplyMultiNode(node); - }else{ - for (var i in graphcanvas.selected_nodes) { - fApplyMultiNode(graphcanvas.selected_nodes[i]); - } - } - - node.graph.afterChange(/*?*/); //node + var fApplyMultiNode = function(node) { + node.shape = v; + } + + var graphcanvas = LGraphCanvas.active_canvas; + if (!graphcanvas.selected_nodes || Object.keys(graphcanvas.selected_nodes).length <= 1) { + fApplyMultiNode(node); + }else{ + for (var i in graphcanvas.selected_nodes) { + fApplyMultiNode(graphcanvas.selected_nodes[i]); + } + } + + node.graph.afterChange(/* ?*/); // node node.setDirtyCanvas(true); } @@ -13076,83 +12956,83 @@ LGraphNode.prototype.executeAction = function(action) throw "no node passed"; } - var graph = node.graph; - graph.beforeChange(); - - - var fApplyMultiNode = function(node){ - if (node.removable === false) { - return; - } - graph.remove(node); - } + var graph = node.graph; + graph.beforeChange(); - var graphcanvas = LGraphCanvas.active_canvas; - if (!graphcanvas.selected_nodes || Object.keys(graphcanvas.selected_nodes).length <= 1){ - fApplyMultiNode(node); - }else{ - for (var i in graphcanvas.selected_nodes) { - fApplyMultiNode(graphcanvas.selected_nodes[i]); - } - } - - graph.afterChange(); + + var fApplyMultiNode = function(node) { + if (node.removable === false) { + return; + } + graph.remove(node); + } + + var graphcanvas = LGraphCanvas.active_canvas; + if (!graphcanvas.selected_nodes || Object.keys(graphcanvas.selected_nodes).length <= 1) { + fApplyMultiNode(node); + }else{ + for (var i in graphcanvas.selected_nodes) { + fApplyMultiNode(graphcanvas.selected_nodes[i]); + } + } + + graph.afterChange(); node.setDirtyCanvas(true, true); }; LGraphCanvas.onMenuNodeToSubgraph = function(value, options, e, menu, node) { - var graph = node.graph; - var graphcanvas = LGraphCanvas.active_canvas; - if(!graphcanvas) //?? - return; + var graph = node.graph; + var graphcanvas = LGraphCanvas.active_canvas; + if(!graphcanvas) // ?? + return; - var nodes_list = Object.values( graphcanvas.selected_nodes || {} ); - if( !nodes_list.length ) - nodes_list = [ node ]; + var nodes_list = Object.values( graphcanvas.selected_nodes || {} ); + if( !nodes_list.length ) + nodes_list = [ node ]; - var subgraph_node = LiteGraph.createNode("graph/subgraph"); - subgraph_node.pos = node.pos.concat(); - graph.add(subgraph_node); + var subgraph_node = LiteGraph.createNode("graph/subgraph"); + subgraph_node.pos = node.pos.concat(); + graph.add(subgraph_node); - subgraph_node.buildFromNodes( nodes_list ); + subgraph_node.buildFromNodes( nodes_list ); - graphcanvas.deselectAllNodes(); + graphcanvas.deselectAllNodes(); node.setDirtyCanvas(true, true); }; LGraphCanvas.onMenuNodeClone = function(value, options, e, menu, node) { - - node.graph.beforeChange(); - - var newSelected = {}; - - var fApplyMultiNode = function(node){ - if (node.clonable === false) { - return; - } - var newnode = node.clone(); - if (!newnode) { - return; - } - newnode.pos = [node.pos[0] + 5, node.pos[1] + 5]; - node.graph.add(newnode); - newSelected[newnode.id] = newnode; - } - var graphcanvas = LGraphCanvas.active_canvas; - if (!graphcanvas.selected_nodes || Object.keys(graphcanvas.selected_nodes).length <= 1){ - fApplyMultiNode(node); - }else{ - for (var i in graphcanvas.selected_nodes) { - fApplyMultiNode(graphcanvas.selected_nodes[i]); - } - } - - if(Object.keys(newSelected).length){ - graphcanvas.selectNodes(newSelected); - } - - node.graph.afterChange(); + node.graph.beforeChange(); + + var newSelected = {}; + + var fApplyMultiNode = function(node) { + if (node.clonable === false) { + return; + } + var newnode = node.clone(); + if (!newnode) { + return; + } + newnode.pos = [node.pos[0] + 5, node.pos[1] + 5]; + node.graph.add(newnode); + newSelected[newnode.id] = newnode; + } + + var graphcanvas = LGraphCanvas.active_canvas; + if (!graphcanvas.selected_nodes || Object.keys(graphcanvas.selected_nodes).length <= 1) { + fApplyMultiNode(node); + }else{ + for (var i in graphcanvas.selected_nodes) { + fApplyMultiNode(graphcanvas.selected_nodes[i]); + } + } + + if(Object.keys(newSelected).length) { + graphcanvas.selectNodes(newSelected); + } + + node.graph.afterChange(); node.setDirtyCanvas(true, true); }; @@ -13165,17 +13045,17 @@ LGraphNode.prototype.executeAction = function(action) pale_blue: { color: "#2a363b", bgcolor: "#3f5159", - groupcolor: "#3f789e" + groupcolor: "#3f789e", }, cyan: { color: "#233", bgcolor: "#355", groupcolor: "#8AA" }, purple: { color: "#323", bgcolor: "#535", groupcolor: "#a1309b" }, yellow: { color: "#432", bgcolor: "#653", groupcolor: "#b58b2a" }, - black: { color: "#222", bgcolor: "#000", groupcolor: "#444" } + black: { color: "#222", bgcolor: "#000", groupcolor: "#444" }, }; LGraphCanvas.prototype.getCanvasMenuOptions = function() { var options = null; - var that = this; + var that = this; if (this.getMenuOptions) { options = this.getMenuOptions(); } else { @@ -13183,13 +13063,13 @@ LGraphNode.prototype.executeAction = function(action) { content: "Add Node", has_submenu: true, - callback: LGraphCanvas.onMenuAdd + callback: LGraphCanvas.onMenuAdd, }, { content: "Add Group", callback: LGraphCanvas.onGroupAdd }, - //{ content: "Arrange", callback: that.graph.arrange }, - //{content:"Collapse All", callback: LGraphCanvas.onMenuCollapseAll } + // { content: "Arrange", callback: that.graph.arrange }, + // {content:"Collapse All", callback: LGraphCanvas.onMenuCollapseAll } ]; - /*if (LiteGraph.showCanvasOptions){ + /* if (LiteGraph.showCanvasOptions){ options.push({ content: "Options", callback: that.showShowGraphOptionsPanel }); }*/ @@ -13204,7 +13084,7 @@ LGraphNode.prototype.executeAction = function(action) if (this._graph_stack && this._graph_stack.length > 0) { options.push(null, { content: "Close subgraph", - callback: this.closeSubgraph.bind(this) + callback: this.closeSubgraph.bind(this), }); } } @@ -13219,7 +13099,7 @@ LGraphNode.prototype.executeAction = function(action) return options; }; - //called by processContextMenu to extract the menu list + // called by processContextMenu to extract the menu list LGraphCanvas.prototype.getNodeMenuOptions = function(node) { var options = null; @@ -13231,52 +13111,50 @@ LGraphNode.prototype.executeAction = function(action) content: "Inputs", has_submenu: true, disabled: true, - callback: LGraphCanvas.showMenuNodeOptionalInputs + callback: LGraphCanvas.showMenuNodeOptionalInputs, }, { content: "Outputs", has_submenu: true, disabled: true, - callback: LGraphCanvas.showMenuNodeOptionalOutputs + callback: LGraphCanvas.showMenuNodeOptionalOutputs, }, null, { content: "Properties", has_submenu: true, - callback: LGraphCanvas.onShowMenuNodeProperties + callback: LGraphCanvas.onShowMenuNodeProperties, }, null, { content: "Title", - callback: LGraphCanvas.onShowPropertyEditor + callback: LGraphCanvas.onShowPropertyEditor, }, { content: "Mode", has_submenu: true, - callback: LGraphCanvas.onMenuNodeMode + callback: LGraphCanvas.onMenuNodeMode, }]; - if(node.resizable !== false){ - options.push({ - content: "Resize", callback: LGraphCanvas.onMenuResizeNode - }); + if(node.resizable !== false) { + options.push({content: "Resize", callback: LGraphCanvas.onMenuResizeNode}); } options.push( { content: "Collapse", - callback: LGraphCanvas.onMenuNodeCollapse + callback: LGraphCanvas.onMenuNodeCollapse, }, { content: "Pin", callback: LGraphCanvas.onMenuNodePin }, { content: "Colors", has_submenu: true, - callback: LGraphCanvas.onMenuNodeColors + callback: LGraphCanvas.onMenuNodeColors, }, { content: "Shapes", has_submenu: true, - callback: LGraphCanvas.onMenuNodeShapes + callback: LGraphCanvas.onMenuNodeShapes, }, - null + null, ); } @@ -13305,16 +13183,16 @@ LGraphNode.prototype.executeAction = function(action) if (node.clonable !== false) { options.push({ content: "Clone", - callback: LGraphCanvas.onMenuNodeClone + callback: LGraphCanvas.onMenuNodeClone, }); } - - if(0) //TODO - options.push({ - content: "To Subgraph", - callback: LGraphCanvas.onMenuNodeToSubgraph - }); - + /* + if(0) // TODO + options.push({ + content: "To Subgraph", + callback: LGraphCanvas.onMenuNodeToSubgraph, + }); + */ if (Object.keys(this.selected_nodes).length > 1) { options.push({ content: "Align Selected To", @@ -13323,11 +13201,11 @@ LGraphNode.prototype.executeAction = function(action) }) } - options.push(null, { - content: "Remove", - disabled: !(node.removable !== false && !node.block_delete ), - callback: LGraphCanvas.onMenuNodeRemove - }); + options.push(null, { + content: "Remove", + disabled: !(node.removable !== false && !node.block_delete ), + callback: LGraphCanvas.onMenuNodeRemove, + }); if (node.graph && node.graph.onGetNodeMenuOptions) { node.graph.onGetNodeMenuOptions(options, node); @@ -13342,16 +13220,16 @@ LGraphNode.prototype.executeAction = function(action) { content: "Color", has_submenu: true, - callback: LGraphCanvas.onMenuNodeColors + callback: LGraphCanvas.onMenuNodeColors, }, { content: "Font size", property: "font_size", type: "Number", - callback: LGraphCanvas.onShowPropertyEditor + callback: LGraphCanvas.onShowPropertyEditor, }, null, - { content: "Remove", callback: LGraphCanvas.onMenuNodeRemove } + { content: "Remove", callback: LGraphCanvas.onMenuNodeRemove }, ]; return o; @@ -13366,13 +13244,13 @@ LGraphNode.prototype.executeAction = function(action) var options = { event: event, callback: inner_option_clicked, - extra: node + extra: node, }; - if(node) - options.title = node.type; + if(node) + options.title = node.type; - //check if mouse is in input + // check if mouse is in input var slot = null; if (node) { slot = node.getSlotInPosition(event.canvasX, event.canvasY); @@ -13380,7 +13258,7 @@ LGraphNode.prototype.executeAction = function(action) } if (slot) { - //on slot + // on slot menu_info = []; if (node.getSlotMenuOptions) { menu_info = node.getSlotMenuOptions(slot); @@ -13394,17 +13272,15 @@ LGraphNode.prototype.executeAction = function(action) menu_info.push({ content: "Disconnect Links", slot: slot }); } var _slot = slot.input || slot.output; - if (_slot.removable){ - menu_info.push( - _slot.locked - ? "Cannot remove" - : { content: "Remove Slot", slot: slot } - ); - } - if (!_slot.nameLocked){ - menu_info.push({ content: "Rename Slot", slot: slot }); - } - + if (_slot.removable) { + menu_info.push(_slot.locked + ? "Cannot remove" + : { content: "Remove Slot", slot: slot }); + } + if (!_slot.nameLocked) { + menu_info.push({ content: "Rename Slot", slot: slot }); + } + } options.title = (slot.input ? slot.input.type : slot.output.type) || "*"; @@ -13416,30 +13292,30 @@ LGraphNode.prototype.executeAction = function(action) } } else { if (node) { - //on node + // on node menu_info = this.getNodeMenuOptions(node); } else { menu_info = this.getCanvasMenuOptions(); var group = this.graph.getGroupOnPos( event.canvasX, - event.canvasY + event.canvasY, ); if (group) { - //on group + // on group menu_info.push(null, { content: "Edit Group", has_submenu: true, submenu: { title: "Group", extra: group, - options: this.getGroupMenuOptions(group) - } + options: this.getGroupMenuOptions(group), + }, }); } } } - //show menu + // show menu if (!menu_info) { return; } @@ -13478,14 +13354,14 @@ LGraphNode.prototype.executeAction = function(action) : node.getOutputInfo(info.slot); var dialog = that.createDialog( "Name", - options + options, ); var input = dialog.querySelector("input"); if (input && slot_info) { input.value = slot_info.label || ""; } - var inner = function(){ - node.graph.beforeChange(); + var inner = function() { + node.graph.beforeChange(); if (input.value) { if (slot_info) { slot_info.label = input.value; @@ -13499,7 +13375,7 @@ LGraphNode.prototype.executeAction = function(action) input.addEventListener("keydown", function(e) { dialog.is_modified = true; if (e.keyCode == 27) { - //ESC + // ESC dialog.close(); } else if (e.keyCode == 13) { inner(); // save @@ -13512,12 +13388,12 @@ LGraphNode.prototype.executeAction = function(action) input.focus(); } - //if(v.callback) + // if(v.callback) // return v.callback.call(that, node, options, e, menu, that, event ); } }; - //API ************************************************* + // API ************************************************* function compareObjects(a, b) { for (var i in a) { if (a[i] != b[i]) { @@ -13529,9 +13405,7 @@ LGraphNode.prototype.executeAction = function(action) LiteGraph.compareObjects = compareObjects; function distance(a, b) { - return Math.sqrt( - (b[0] - a[0]) * (b[0] - a[0]) + (b[1] - a[1]) * (b[1] - a[1]) - ); + return Math.sqrt((b[0] - a[0]) * (b[0] - a[0]) + (b[1] - a[1]) * (b[1] - a[1])); } LiteGraph.distance = distance; @@ -13558,7 +13432,7 @@ LGraphNode.prototype.executeAction = function(action) } LiteGraph.isInsideRectangle = isInsideRectangle; - //[minx,miny,maxx,maxy] + // [minx,miny,maxx,maxy] function growBounding(bounding, x, y) { if (x < bounding[0]) { bounding[0] = x; @@ -13574,7 +13448,7 @@ LGraphNode.prototype.executeAction = function(action) } LiteGraph.growBounding = growBounding; - //point inside bounding box + // point inside bounding box function isInsideBounding(p, bb) { if ( p[0] < bb[0][0] || @@ -13588,7 +13462,7 @@ LGraphNode.prototype.executeAction = function(action) } LiteGraph.isInsideBounding = isInsideBounding; - //bounding overlap, format: [ startx, starty, width, height ] + // bounding overlap, format: [ startx, starty, width, height ] function overlapBounding(a, b) { var A_end_x = a[0] + a[2]; var A_end_y = a[1] + a[3]; @@ -13607,13 +13481,13 @@ LGraphNode.prototype.executeAction = function(action) } LiteGraph.overlapBounding = overlapBounding; - //Convert a hex value to its decimal value - the inputted hex must be in the + // Convert a hex value to its decimal value - the inputted hex must be in the // format of a hex triplet - the kind we use for HTML colours. The function // will return an array with three values. function hex2num(hex) { if (hex.charAt(0) == "#") { hex = hex.slice(1); - } //Remove the '#' char - if there is one. + } // Remove the '#' char - if there is one. hex = hex.toUpperCase(); var hex_alphabets = "0123456789ABCDEF"; var value = new Array(3); @@ -13630,7 +13504,7 @@ LGraphNode.prototype.executeAction = function(action) LiteGraph.hex2num = hex2num; - //Give a array with three values as the argument and the function will return + // Give a array with three values as the argument and the function will return // the corresponding hex triplet. function num2hex(triplet) { var hex_alphabets = "0123456789ABCDEF"; @@ -13666,12 +13540,10 @@ LGraphNode.prototype.executeAction = function(action) this.options = options; var that = this; - //to link a menu with its parent + // to link a menu with its parent if (options.parentMenu) { if (options.parentMenu.constructor !== this.constructor) { - console.error( - "parentMenu must be of class ContextMenu, ignoring it" - ); + console.error("parentMenu must be of class ContextMenu, ignoring it"); options.parentMenu = null; } else { this.parentMenu = options.parentMenu; @@ -13680,16 +13552,14 @@ LGraphNode.prototype.executeAction = function(action) } } - var eventClass = null; - if(options.event) //use strings because comparing classes between windows doesnt work - eventClass = options.event.constructor.name; + var eventClass = null; + if(options.event) // use strings because comparing classes between windows doesnt work + eventClass = options.event.constructor.name; if ( eventClass !== "MouseEvent" && eventClass !== "CustomEvent" && eventClass !== "PointerEvent" ) { - console.error( - "Event passed to ContextMenu is not of type MouseEvent or CustomEvent. Ignoring it. ("+eventClass+")" - ); + console.error("Event passed to ContextMenu is not of type MouseEvent or CustomEvent. Ignoring it. ("+eventClass+")"); options.event = null; } @@ -13703,40 +13573,42 @@ LGraphNode.prototype.executeAction = function(action) root.style.pointerEvents = "none"; setTimeout(function() { root.style.pointerEvents = "auto"; - }, 100); //delay so the mouse up event is not caught by this element + }, 100); // delay so the mouse up event is not caught by this element - //this prevents the default context browser menu to open in case this menu was created when pressing right button - LiteGraph.pointerListenerAdd(root,"up", + // this prevents the default context browser menu to open in case this menu was created when pressing right button + LiteGraph.pointerListenerAdd( + root,"up", function(e) { - //console.log("pointerevents: ContextMenu up root prevent"); + // console.log("pointerevents: ContextMenu up root prevent"); e.preventDefault(); return true; }, - true + true, ); root.addEventListener( "contextmenu", function(e) { if (e.button != 2) { - //right button + // right button return false; } e.preventDefault(); return false; }, - true + true, ); - LiteGraph.pointerListenerAdd(root,"down", + LiteGraph.pointerListenerAdd( + root,"down", function(e) { - //console.log("pointerevents: ContextMenu down"); + // console.log("pointerevents: ContextMenu down"); if (e.button == 2) { that.close(); e.preventDefault(); return true; } }, - true + true, ); function on_mouse_wheel(e) { @@ -13756,7 +13628,7 @@ LGraphNode.prototype.executeAction = function(action) this.root = root; - //title + // title if (options.title) { var element = document.createElement("div"); element.className = "litemenu-title"; @@ -13764,7 +13636,7 @@ LGraphNode.prototype.executeAction = function(action) root.appendChild(element); } - //entries + // entries var num = 0; for (var i=0; i < values.length; i++) { var name = values.constructor == Array ? values[i] : i; @@ -13776,8 +13648,8 @@ LGraphNode.prototype.executeAction = function(action) num++; } - //close on leave? touch enabled devices won't work TODO use a global device detector and condition on that - /*LiteGraph.pointerListenerAdd(root,"leave", function(e) { + // close on leave? touch enabled devices won't work TODO use a global device detector and condition on that + /* LiteGraph.pointerListenerAdd(root,"leave", function(e) { console.log("pointerevents: ContextMenu leave"); if (that.lock) { return; @@ -13789,14 +13661,14 @@ LGraphNode.prototype.executeAction = function(action) //that.close(e); });*/ - LiteGraph.pointerListenerAdd(root,"enter", function(e) { - //console.log("pointerevents: ContextMenu enter"); + LiteGraph.pointerListenerAdd(root,"enter", function(e) { + // console.log("pointerevents: ContextMenu enter"); if (root.closing_timer) { clearTimeout(root.closing_timer); } }); - //insert before checking position + // insert before checking position var root_document = document; if (options.event) { root_document = options.event.target.ownerDocument; @@ -13806,12 +13678,12 @@ LGraphNode.prototype.executeAction = function(action) root_document = document; } - if( root_document.fullscreenElement ) - root_document.fullscreenElement.appendChild(root); - else - root_document.body.appendChild(root); + if( root_document.fullscreenElement ) + root_document.fullscreenElement.appendChild(root); + else + root_document.body.appendChild(root); - //compute best position + // compute best position var left = options.left || 0; var top = options.top || 0; if (options.event) { @@ -13828,8 +13700,8 @@ LGraphNode.prototype.executeAction = function(action) var body_rect = document.body.getBoundingClientRect(); var root_rect = root.getBoundingClientRect(); - if(body_rect.height == 0) - console.error("document.body height is 0. That is dangerous, set html,body { height: 100%; }"); + if(body_rect.height == 0) + console.error("document.body height is 0. That is dangerous, set html,body { height: 100%; }"); if (body_rect.width && left > body_rect.width - root_rect.width - 10) { left = body_rect.width - root_rect.width - 10; @@ -13858,8 +13730,8 @@ LGraphNode.prototype.executeAction = function(action) if (value === null) { element.classList.add("separator"); - //element.innerHTML = "
" - //continue; + // element.innerHTML = "
" + // continue; } else { element.innerHTML = value && value.title ? value.title : name; element.value = value; @@ -13891,7 +13763,7 @@ LGraphNode.prototype.executeAction = function(action) element.addEventListener("click", inner_onclick); } if (!disabled && options.autoopen) { - LiteGraph.pointerListenerAdd(element,"enter",inner_over); + LiteGraph.pointerListenerAdd(element,"enter",inner_over); } function inner_over(e) { @@ -13899,11 +13771,11 @@ LGraphNode.prototype.executeAction = function(action) if (!value || !value.has_submenu) { return; } - //if it is a submenu, autoopen like the item was clicked + // if it is a submenu, autoopen like the item was clicked inner_onclick.call(this, e); } - //menu option clicked + // menu option clicked function inner_onclick(e) { var value = this.value; var close_parent = true; @@ -13912,7 +13784,7 @@ LGraphNode.prototype.executeAction = function(action) that.current_submenu.close(e); } - //global callback + // global callback if (options.callback) { var r = options.callback.call( this, @@ -13920,28 +13792,28 @@ LGraphNode.prototype.executeAction = function(action) options, e, that, - options.node + options.node, ); if (r === true) { close_parent = false; } } - //special cases + // special cases if (value) { if ( value.callback && !options.ignore_item_callbacks && value.disabled !== true ) { - //item callback + // item callback var r = value.callback.call( this, value, options, e, that, - options.extra + options.extra, ); if (r === true) { close_parent = false; @@ -13959,7 +13831,7 @@ LGraphNode.prototype.executeAction = function(action) value.submenu.ignore_item_callbacks, title: value.submenu.title, extra: value.submenu.extra, - autoopen: options.autoopen + autoopen: options.autoopen, }); close_parent = false; } @@ -13996,26 +13868,26 @@ LGraphNode.prototype.executeAction = function(action) if (this.root.closing_timer) { clearTimeout(this.root.closing_timer); } - + // TODO implement : LiteGraph.contextMenuClosed(); :: keep track of opened / closed / current ContextMenu // on key press, allow filtering/selecting the context menu elements }; - //this code is used to trigger events easily (used in the context menu mouseleave + // this code is used to trigger events easily (used in the context menu mouseleave ContextMenu.trigger = function(element, event_name, params, origin) { var evt = document.createEvent("CustomEvent"); - evt.initCustomEvent(event_name, true, true, params); //canBubble, cancelable, detail + evt.initCustomEvent(event_name, true, true, params); // canBubble, cancelable, detail evt.srcElement = origin; if (element.dispatchEvent) { element.dispatchEvent(evt); } else if (element.__events) { element.__events.dispatchEvent(evt); } - //else nothing seems binded here so nothing to do + // else nothing seems binded here so nothing to do return evt; }; - //returns the top most menu + // returns the top most menu ContextMenu.prototype.getTopMenu = function() { if (this.options.parentMenu) { return this.options.parentMenu.getTopMenu(); @@ -14074,7 +13946,7 @@ LGraphNode.prototype.executeAction = function(action) LiteGraph.extendClass = function(target, origin) { for (var i in origin) { - //copy class properties + // copy class properties if (target.hasOwnProperty(i)) { continue; } @@ -14082,3044 +13954,2969 @@ LGraphNode.prototype.executeAction = function(action) } if (origin.prototype) { - //copy prototype properties + // copy prototype properties for (var i in origin.prototype) { - //only enumerable + // only enumerable if (!origin.prototype.hasOwnProperty(i)) { continue; } if (target.prototype.hasOwnProperty(i)) { - //avoid overwriting existing ones + // avoid overwriting existing ones continue; } - //copy getters + // copy getters if (origin.prototype.__lookupGetter__(i)) { target.prototype.__defineGetter__( i, - origin.prototype.__lookupGetter__(i) + origin.prototype.__lookupGetter__(i), ); } else { target.prototype[i] = origin.prototype[i]; } - //and setters + // and setters if (origin.prototype.__lookupSetter__(i)) { target.prototype.__defineSetter__( i, - origin.prototype.__lookupSetter__(i) + origin.prototype.__lookupSetter__(i), ); } } } }; - //used by some widgets to render a curve editor - function CurveEditor( points ) - { - this.points = points; - this.selected = -1; - this.nearest = -1; - this.size = null; //stores last size used - this.must_update = true; - this.margin = 5; - } - - CurveEditor.sampleCurve = function(f,points) - { - if(!points) - return; - for(var i = 0; i < points.length - 1; ++i) - { - var p = points[i]; - var pn = points[i+1]; - if(pn[0] < f) - continue; - var r = (pn[0] - p[0]); - if( Math.abs(r) < 0.00001 ) - return p[1]; - var local_f = (f - p[0]) / r; - return p[1] * (1.0 - local_f) + pn[1] * local_f; - } - return 0; - } + // used by some widgets to render a curve editor + function CurveEditor( points ) { + this.points = points; + this.selected = -1; + this.nearest = -1; + this.size = null; // stores last size used + this.must_update = true; + this.margin = 5; + } - CurveEditor.prototype.draw = function( ctx, size, graphcanvas, background_color, line_color, inactive ) - { - var points = this.points; - if(!points) - return; - this.size = size; - var w = size[0] - this.margin * 2; - var h = size[1] - this.margin * 2; + CurveEditor.sampleCurve = function(f,points) { + if(!points) + return; + for(var i = 0; i < points.length - 1; ++i) { + var p = points[i]; + var pn = points[i+1]; + if(pn[0] < f) + continue; + var r = (pn[0] - p[0]); + if( Math.abs(r) < 0.00001 ) + return p[1]; + var local_f = (f - p[0]) / r; + return p[1] * (1.0 - local_f) + pn[1] * local_f; + } + return 0; + } - line_color = line_color || "#666"; + CurveEditor.prototype.draw = function( ctx, size, graphcanvas, background_color, line_color, inactive ) { + var points = this.points; + if(!points) + return; + this.size = size; + var w = size[0] - this.margin * 2; + var h = size[1] - this.margin * 2; - ctx.save(); - ctx.translate(this.margin,this.margin); + line_color = line_color || "#666"; - if(background_color) - { - ctx.fillStyle = "#111"; - ctx.fillRect(0,0,w,h); - ctx.fillStyle = "#222"; - ctx.fillRect(w*0.5,0,1,h); - ctx.strokeStyle = "#333"; - ctx.strokeRect(0,0,w,h); - } - ctx.strokeStyle = line_color; - if(inactive) - ctx.globalAlpha = 0.5; - ctx.beginPath(); - for(var i = 0; i < points.length; ++i) - { - var p = points[i]; - ctx.lineTo( p[0] * w, (1.0 - p[1]) * h ); - } - ctx.stroke(); - ctx.globalAlpha = 1; - if(!inactive) - for(var i = 0; i < points.length; ++i) - { - var p = points[i]; - ctx.fillStyle = this.selected == i ? "#FFF" : (this.nearest == i ? "#DDD" : "#AAA"); - ctx.beginPath(); - ctx.arc( p[0] * w, (1.0 - p[1]) * h, 2, 0, Math.PI * 2 ); - ctx.fill(); - } - ctx.restore(); - } + ctx.save(); + ctx.translate(this.margin,this.margin); - //localpos is mouse in curve editor space - CurveEditor.prototype.onMouseDown = function( localpos, graphcanvas ) - { - var points = this.points; - if(!points) - return; - if( localpos[1] < 0 ) - return; - - //this.captureInput(true); - var w = this.size[0] - this.margin * 2; - var h = this.size[1] - this.margin * 2; - var x = localpos[0] - this.margin; - var y = localpos[1] - this.margin; - var pos = [x,y]; - var max_dist = 30 / graphcanvas.ds.scale; - //search closer one - this.selected = this.getCloserPoint(pos, max_dist); - //create one - if(this.selected == -1) - { - var point = [x / w, 1 - y / h]; - points.push(point); - points.sort(function(a,b){ return a[0] - b[0]; }); - this.selected = points.indexOf(point); - this.must_update = true; - } - if(this.selected != -1) - return true; - } + if(background_color) { + ctx.fillStyle = "#111"; + ctx.fillRect(0,0,w,h); + ctx.fillStyle = "#222"; + ctx.fillRect(w*0.5,0,1,h); + ctx.strokeStyle = "#333"; + ctx.strokeRect(0,0,w,h); + } + ctx.strokeStyle = line_color; + if(inactive) + ctx.globalAlpha = 0.5; + ctx.beginPath(); + for(var i = 0; i < points.length; ++i) { + var p = points[i]; + ctx.lineTo( p[0] * w, (1.0 - p[1]) * h ); + } + ctx.stroke(); + ctx.globalAlpha = 1; + if(!inactive) + for(var i = 0; i < points.length; ++i) { + var p = points[i]; + ctx.fillStyle = this.selected == i ? "#FFF" : (this.nearest == i ? "#DDD" : "#AAA"); + ctx.beginPath(); + ctx.arc( p[0] * w, (1.0 - p[1]) * h, 2, 0, Math.PI * 2 ); + ctx.fill(); + } + ctx.restore(); + } - CurveEditor.prototype.onMouseMove = function( localpos, graphcanvas ) - { - var points = this.points; - if(!points) - return; - var s = this.selected; - if(s < 0) - return; - var x = (localpos[0] - this.margin) / (this.size[0] - this.margin * 2 ); - var y = (localpos[1] - this.margin) / (this.size[1] - this.margin * 2 ); - var curvepos = [(localpos[0] - this.margin),(localpos[1] - this.margin)]; - var max_dist = 30 / graphcanvas.ds.scale; - this._nearest = this.getCloserPoint(curvepos, max_dist); - var point = points[s]; - if(point) - { - var is_edge_point = s == 0 || s == points.length - 1; - if( !is_edge_point && (localpos[0] < -10 || localpos[0] > this.size[0] + 10 || localpos[1] < -10 || localpos[1] > this.size[1] + 10) ) - { - points.splice(s,1); - this.selected = -1; - return; - } - if( !is_edge_point ) //not edges - point[0] = clamp(x, 0, 1); - else - point[0] = s == 0 ? 0 : 1; - point[1] = 1.0 - clamp(y, 0, 1); - points.sort(function(a,b){ return a[0] - b[0]; }); - this.selected = points.indexOf(point); - this.must_update = true; - } - } + // localpos is mouse in curve editor space + CurveEditor.prototype.onMouseDown = function( localpos, graphcanvas ) { + var points = this.points; + if(!points) + return; + if( localpos[1] < 0 ) + return; - CurveEditor.prototype.onMouseUp = function( localpos, graphcanvas ) - { - this.selected = -1; - return false; - } + // this.captureInput(true); + var w = this.size[0] - this.margin * 2; + var h = this.size[1] - this.margin * 2; + var x = localpos[0] - this.margin; + var y = localpos[1] - this.margin; + var pos = [x,y]; + var max_dist = 30 / graphcanvas.ds.scale; + // search closer one + this.selected = this.getCloserPoint(pos, max_dist); + // create one + if(this.selected == -1) { + var point = [x / w, 1 - y / h]; + points.push(point); + points.sort(function(a,b) { + return a[0] - b[0]; + }); + this.selected = points.indexOf(point); + this.must_update = true; + } + if(this.selected != -1) + return true; + } - CurveEditor.prototype.getCloserPoint = function(pos, max_dist) - { - var points = this.points; - if(!points) - return -1; - max_dist = max_dist || 30; - var w = (this.size[0] - this.margin * 2); - var h = (this.size[1] - this.margin * 2); - var num = points.length; - var p2 = [0,0]; - var min_dist = 1000000; - var closest = -1; - var last_valid = -1; - for(var i = 0; i < num; ++i) - { - var p = points[i]; - p2[0] = p[0] * w; - p2[1] = (1.0 - p[1]) * h; - if(p2[0] < pos[0]) - last_valid = i; - var dist = vec2.distance(pos,p2); - if(dist > min_dist || dist > max_dist) - continue; - closest = i; - min_dist = dist; - } - return closest; - } + CurveEditor.prototype.onMouseMove = function( localpos, graphcanvas ) { + var points = this.points; + if(!points) + return; + var s = this.selected; + if(s < 0) + return; + var x = (localpos[0] - this.margin) / (this.size[0] - this.margin * 2 ); + var y = (localpos[1] - this.margin) / (this.size[1] - this.margin * 2 ); + var curvepos = [(localpos[0] - this.margin),(localpos[1] - this.margin)]; + var max_dist = 30 / graphcanvas.ds.scale; + this._nearest = this.getCloserPoint(curvepos, max_dist); + var point = points[s]; + if(point) { + var is_edge_point = s == 0 || s == points.length - 1; + if( !is_edge_point && (localpos[0] < -10 || localpos[0] > this.size[0] + 10 || localpos[1] < -10 || localpos[1] > this.size[1] + 10) ) { + points.splice(s,1); + this.selected = -1; + return; + } + if( !is_edge_point ) // not edges + point[0] = clamp(x, 0, 1); + else + point[0] = s == 0 ? 0 : 1; + point[1] = 1.0 - clamp(y, 0, 1); + points.sort(function(a,b) { + return a[0] - b[0]; + }); + this.selected = points.indexOf(point); + this.must_update = true; + } + } - LiteGraph.CurveEditor = CurveEditor; + CurveEditor.prototype.onMouseUp = function( localpos, graphcanvas ) { + this.selected = -1; + return false; + } + + CurveEditor.prototype.getCloserPoint = function(pos, max_dist) { + var points = this.points; + if(!points) + return -1; + max_dist = max_dist || 30; + var w = (this.size[0] - this.margin * 2); + var h = (this.size[1] - this.margin * 2); + var num = points.length; + var p2 = [0,0]; + var min_dist = 1000000; + var closest = -1; + var last_valid = -1; + for(var i = 0; i < num; ++i) { + var p = points[i]; + p2[0] = p[0] * w; + p2[1] = (1.0 - p[1]) * h; + if(p2[0] < pos[0]) + last_valid = i; + var dist = vec2.distance(pos,p2); + if(dist > min_dist || dist > max_dist) + continue; + closest = i; + min_dist = dist; + } + return closest; + } + + LiteGraph.CurveEditor = CurveEditor; + + // used to create nodes from wrapping functions + LiteGraph.getParameterNames = function(func) { + return (func + "") + .replace(/[/][/].*$/gm, "") // strip single-line comments + .replace(/\s+/g, "") // strip white space + .replace(/[/][*][^/*]*[*][/]/g, "") // strip multi-line comments /**/ + .split("){", 1)[0] + .replace(/^[^(]*[(]/, "") // extract the parameters + .replace(/=[^,]+/g, "") // strip any ES6 defaults + .split(",") + .filter(Boolean); // split & filter [""] + }; + + /* helper for interaction: pointer, touch, mouse Listeners + used by LGraphCanvas DragAndScale ContextMenu*/ + LiteGraph.pointerListenerAdd = function(oDOM, sEvIn, fCall, capture=false) { + if (!oDOM || !oDOM.addEventListener || !sEvIn || typeof fCall!=="function") { + // console.log("cant pointerListenerAdd "+oDOM+", "+sEvent+", "+fCall); + return; // -- break -- + } + + var sMethod = LiteGraph.pointerevents_method; + var sEvent = sEvIn; + + // UNDER CONSTRUCTION + // convert pointerevents to touch event when not available + if (sMethod=="pointer" && !window.PointerEvent) { + console.warn("sMethod=='pointer' && !window.PointerEvent"); + console.log("Converting pointer["+sEvent+"] : down move up cancel enter TO touchstart touchmove touchend, etc .."); + switch(sEvent) { + case "down":{ + sMethod = "touch"; + sEvent = "start"; + break; + } + case "move":{ + sMethod = "touch"; + // sEvent = "move"; + break; + } + case "up":{ + sMethod = "touch"; + sEvent = "end"; + break; + } + case "cancel":{ + sMethod = "touch"; + // sEvent = "cancel"; + break; + } + case "enter":{ + console.log("debug: Should I send a move event?"); // ??? + break; + } + // case "over": case "out": not used at now + default:{ + console.warn("PointerEvent not available in this browser ? The event "+sEvent+" would not be called"); + } + } + } + + switch(sEvent) { + // both pointer and move events + case "down": case "up": case "move": case "over": case "out": case "enter": + { + oDOM.addEventListener(sMethod+sEvent, fCall, capture); + } + break; + // only pointerevents + case "leave": case "cancel": case "gotpointercapture": case "lostpointercapture": + { + if (sMethod!="mouse") { + return oDOM.addEventListener(sMethod+sEvent, fCall, capture); + } + } + break; + // not "pointer" || "mouse" + default: + return oDOM.addEventListener(sEvent, fCall, capture); + } + } + LiteGraph.pointerListenerRemove = function(oDOM, sEvent, fCall, capture=false) { + if (!oDOM || !oDOM.removeEventListener || !sEvent || typeof fCall!=="function") { + // console.log("cant pointerListenerRemove "+oDOM+", "+sEvent+", "+fCall); + return; // -- break -- + } + switch(sEvent) { + // both pointer and move events + case "down": case "up": case "move": case "over": case "out": case "enter": + { + if (LiteGraph.pointerevents_method=="pointer" || LiteGraph.pointerevents_method=="mouse") { + oDOM.removeEventListener(LiteGraph.pointerevents_method+sEvent, fCall, capture); + } + } + break; + // only pointerevents + case "leave": case "cancel": case "gotpointercapture": case "lostpointercapture": + { + if (LiteGraph.pointerevents_method=="pointer") { + return oDOM.removeEventListener(LiteGraph.pointerevents_method+sEvent, fCall, capture); + } + } + break; + // not "pointer" || "mouse" + default: + return oDOM.removeEventListener(sEvent, fCall, capture); + } + } + + function clamp(v, a, b) { + return a > v ? a : b < v ? b : v; + } + global.clamp = clamp; + + if (typeof window != "undefined" && !window["requestAnimationFrame"]) { + window.requestAnimationFrame = + window.webkitRequestAnimationFrame || + window.mozRequestAnimationFrame || + function(callback) { + window.setTimeout(callback, 1000 / 60); + }; + } +})(this); + +if (typeof exports != "undefined") { + exports.LiteGraph = this.LiteGraph; + exports.LGraph = this.LGraph; + exports.LLink = this.LLink; + exports.LGraphNode = this.LGraphNode; + exports.LGraphGroup = this.LGraphGroup; + exports.DragAndScale = this.DragAndScale; + exports.LGraphCanvas = this.LGraphCanvas; + exports.ContextMenu = this.ContextMenu; +} + + +// basic nodes +(function(global) { + var LiteGraph = global.LiteGraph; + + // Constant + function Time() { + this.addOutput("in ms", "number"); + this.addOutput("in sec", "number"); + } + + Time.title = "Time"; + Time.desc = "Time"; + + Time.prototype.onExecute = function() { + this.setOutputData(0, this.graph.globaltime * 1000); + this.setOutputData(1, this.graph.globaltime); + }; + + LiteGraph.registerNodeType("basic/time", Time); + + // Subgraph: a node that contains a graph + function Subgraph() { + var that = this; + this.size = [140, 80]; + this.properties = { enabled: true }; + this.enabled = true; + + // create inner graph + this.subgraph = new LiteGraph.LGraph(); + this.subgraph._subgraph_node = this; + this.subgraph._is_subgraph = true; + + this.subgraph.onTrigger = this.onSubgraphTrigger.bind(this); + + // nodes input node added inside + this.subgraph.onInputAdded = this.onSubgraphNewInput.bind(this); + this.subgraph.onInputRenamed = this.onSubgraphRenamedInput.bind(this); + this.subgraph.onInputTypeChanged = this.onSubgraphTypeChangeInput.bind(this); + this.subgraph.onInputRemoved = this.onSubgraphRemovedInput.bind(this); + + this.subgraph.onOutputAdded = this.onSubgraphNewOutput.bind(this); + this.subgraph.onOutputRenamed = this.onSubgraphRenamedOutput.bind(this); + this.subgraph.onOutputTypeChanged = this.onSubgraphTypeChangeOutput.bind(this); + this.subgraph.onOutputRemoved = this.onSubgraphRemovedOutput.bind(this); + } + + Subgraph.title = "Subgraph"; + Subgraph.desc = "Graph inside a node"; + Subgraph.title_color = "#334"; + + Subgraph.prototype.onGetInputs = function() { + return [["enabled", "boolean"]]; + }; + + /* + Subgraph.prototype.onDrawTitle = function(ctx) { + if (this.flags.collapsed) { + return; + } + + ctx.fillStyle = "#555"; + var w = LiteGraph.NODE_TITLE_HEIGHT; + var x = this.size[0] - w; + ctx.fillRect(x, -w, w, w); + ctx.fillStyle = "#333"; + ctx.beginPath(); + ctx.moveTo(x + w * 0.2, -w * 0.6); + ctx.lineTo(x + w * 0.8, -w * 0.6); + ctx.lineTo(x + w * 0.5, -w * 0.3); + ctx.fill(); + }; + */ + + Subgraph.prototype.onDblClick = function(e, pos, graphcanvas) { + var that = this; + setTimeout(function() { + graphcanvas.openSubgraph(that.subgraph); + }, 10); + }; + + /* + Subgraph.prototype.onMouseDown = function(e, pos, graphcanvas) { + if ( + !this.flags.collapsed && + pos[0] > this.size[0] - LiteGraph.NODE_TITLE_HEIGHT && + pos[1] < 0 + ) { + var that = this; + setTimeout(function() { + graphcanvas.openSubgraph(that.subgraph); + }, 10); + } + }; + */ + + Subgraph.prototype.onAction = function(action, param) { + this.subgraph.onAction(action, param); + }; + + Subgraph.prototype.onExecute = function() { + this.enabled = this.getInputOrProperty("enabled"); + if (!this.enabled) { + return; + } + + // send inputs to subgraph global inputs + if (this.inputs) { + for (var i = 0; i < this.inputs.length; i++) { + var input = this.inputs[i]; + var value = this.getInputData(i); + this.subgraph.setInputData(input.name, value); + } + } + + // execute + this.subgraph.runStep(); + + // send subgraph global outputs to outputs + if (this.outputs) { + for (var i = 0; i < this.outputs.length; i++) { + var output = this.outputs[i]; + var value = this.subgraph.getOutputData(output.name); + this.setOutputData(i, value); + } + } + }; + + Subgraph.prototype.sendEventToAllNodes = function(eventname, param, mode) { + if (this.enabled) { + this.subgraph.sendEventToAllNodes(eventname, param, mode); + } + }; + + Subgraph.prototype.onDrawBackground = function (ctx, graphcanvas, canvas, pos) { + if (this.flags.collapsed) + return; + var y = this.size[1] - LiteGraph.NODE_TITLE_HEIGHT + 0.5; + // button + var over = LiteGraph.isInsideRectangle(pos[0], pos[1], this.pos[0], this.pos[1] + y, this.size[0], LiteGraph.NODE_TITLE_HEIGHT); + let overleft = LiteGraph.isInsideRectangle(pos[0], pos[1], this.pos[0], this.pos[1] + y, this.size[0] / 2, LiteGraph.NODE_TITLE_HEIGHT) + ctx.fillStyle = over ? "#555" : "#222"; + ctx.beginPath(); + if (this._shape == LiteGraph.BOX_SHAPE) { + if (overleft) { + ctx.rect(0, y, this.size[0] / 2 + 1, LiteGraph.NODE_TITLE_HEIGHT); + } else { + ctx.rect(this.size[0] / 2, y, this.size[0] / 2 + 1, LiteGraph.NODE_TITLE_HEIGHT); + } + } else { + if (overleft) { + ctx.roundRect(0, y, this.size[0] / 2 + 1, LiteGraph.NODE_TITLE_HEIGHT, [0,0, 8,8]); + } else { + ctx.roundRect(this.size[0] / 2, y, this.size[0] / 2 + 1, LiteGraph.NODE_TITLE_HEIGHT, [0,0, 8,8]); + } + } + if (over) { + ctx.fill(); + } else { + ctx.fillRect(0, y, this.size[0] + 1, LiteGraph.NODE_TITLE_HEIGHT); + } + // button + ctx.textAlign = "center"; + ctx.font = "24px Arial"; + ctx.fillStyle = over ? "#DDD" : "#999"; + ctx.fillText("+", this.size[0] * 0.25, y + 24); + ctx.fillText("+", this.size[0] * 0.75, y + 24); + } + + // Subgraph.prototype.onMouseDown = function(e, localpos, graphcanvas) + // { + // var y = this.size[1] - LiteGraph.NODE_TITLE_HEIGHT + 0.5; + // if(localpos[1] > y) + // { + // graphcanvas.showSubgraphPropertiesDialog(this); + // } + // } + Subgraph.prototype.onMouseDown = function (e, localpos, graphcanvas) { + var y = this.size[1] - LiteGraph.NODE_TITLE_HEIGHT + 0.5; + console.log(0) + if (localpos[1] > y) { + if (localpos[0] < this.size[0] / 2) { + console.log(1) + graphcanvas.showSubgraphPropertiesDialog(this); + } else { + console.log(2) + graphcanvas.showSubgraphPropertiesDialogRight(this); + } + } + } + Subgraph.prototype.computeSize = function() { + var num_inputs = this.inputs ? this.inputs.length : 0; + var num_outputs = this.outputs ? this.outputs.length : 0; + return [ 200, Math.max(num_inputs,num_outputs) * LiteGraph.NODE_SLOT_HEIGHT + LiteGraph.NODE_TITLE_HEIGHT ]; + } + + //* *** INPUTS *********************************** + Subgraph.prototype.onSubgraphTrigger = function(event, param) { + var slot = this.findOutputSlot(event); + if (slot != -1) { + this.triggerSlot(slot); + } + }; + + Subgraph.prototype.onSubgraphNewInput = function(name, type) { + var slot = this.findInputSlot(name); + if (slot == -1) { + // add input to the node + this.addInput(name, type); + } + }; + + Subgraph.prototype.onSubgraphRenamedInput = function(oldname, name) { + var slot = this.findInputSlot(oldname); + if (slot == -1) { + return; + } + var info = this.getInputInfo(slot); + info.name = name; + }; + + Subgraph.prototype.onSubgraphTypeChangeInput = function(name, type) { + var slot = this.findInputSlot(name); + if (slot == -1) { + return; + } + var info = this.getInputInfo(slot); + info.type = type; + }; + + Subgraph.prototype.onSubgraphRemovedInput = function(name) { + var slot = this.findInputSlot(name); + if (slot == -1) { + return; + } + this.removeInput(slot); + }; + + //* *** OUTPUTS *********************************** + Subgraph.prototype.onSubgraphNewOutput = function(name, type) { + var slot = this.findOutputSlot(name); + if (slot == -1) { + this.addOutput(name, type); + } + }; + + Subgraph.prototype.onSubgraphRenamedOutput = function(oldname, name) { + var slot = this.findOutputSlot(oldname); + if (slot == -1) { + return; + } + var info = this.getOutputInfo(slot); + info.name = name; + }; + + Subgraph.prototype.onSubgraphTypeChangeOutput = function(name, type) { + var slot = this.findOutputSlot(name); + if (slot == -1) { + return; + } + var info = this.getOutputInfo(slot); + info.type = type; + }; + + Subgraph.prototype.onSubgraphRemovedOutput = function(name) { + var slot = this.findOutputSlot(name); + if (slot == -1) { + return; + } + this.removeOutput(slot); + }; + // ***************************************************** + + Subgraph.prototype.getExtraMenuOptions = function(graphcanvas) { + var that = this; + return [ + { + content: "Open", + callback: function() { + graphcanvas.openSubgraph(that.subgraph); + }, + }, + ]; + }; + + Subgraph.prototype.onResize = function(size) { + size[1] += 20; + }; + + Subgraph.prototype.serialize = function() { + var data = LiteGraph.LGraphNode.prototype.serialize.call(this); + data.subgraph = this.subgraph.serialize(); + return data; + }; + // no need to define node.configure, the default method detects node.subgraph and passes the object to node.subgraph.configure() + + Subgraph.prototype.reassignSubgraphUUIDs = function(graph) { + const idMap = { nodeIDs: {}, linkIDs: {} } + + for (const node of graph.nodes) { + const oldID = node.id + const newID = LiteGraph.uuidv4() + node.id = newID + + if (idMap.nodeIDs[oldID] || idMap.nodeIDs[newID]) { + throw new Error(`New/old node UUID wasn't unique in changed map! ${oldID} ${newID}`) + } + + idMap.nodeIDs[oldID] = newID + idMap.nodeIDs[newID] = oldID + } + + for (const link of graph.links) { + const oldID = link[0] + const newID = LiteGraph.uuidv4(); + link[0] = newID + + if (idMap.linkIDs[oldID] || idMap.linkIDs[newID]) { + throw new Error(`New/old link UUID wasn't unique in changed map! ${oldID} ${newID}`) + } + + idMap.linkIDs[oldID] = newID + idMap.linkIDs[newID] = oldID + + const nodeFrom = link[1] + const nodeTo = link[3] + + if (!idMap.nodeIDs[nodeFrom]) { + throw new Error(`Old node UUID not found in mapping! ${nodeFrom}`) + } + + link[1] = idMap.nodeIDs[nodeFrom] + + if (!idMap.nodeIDs[nodeTo]) { + throw new Error(`Old node UUID not found in mapping! ${nodeTo}`) + } + + link[3] = idMap.nodeIDs[nodeTo] + } + + // Reconnect links + for (const node of graph.nodes) { + if (node.inputs) { + for (const input of node.inputs) { + if (input.link) { + input.link = idMap.linkIDs[input.link] + } + } + } + if (node.outputs) { + for (const output of node.outputs) { + if (output.links) { + output.links = output.links.map((l) => idMap.linkIDs[l]); + } + } + } + } + + // Recurse! + for (const node of graph.nodes) { + if (node.type === "graph/subgraph") { + const merge = reassignGraphUUIDs(node.subgraph); + idMap.nodeIDs.assign(merge.nodeIDs) + idMap.linkIDs.assign(merge.linkIDs) + } + } + }; + + Subgraph.prototype.clone = function() { + var node = LiteGraph.createNode(this.type); + var data = this.serialize(); + + if (LiteGraph.use_uuids) { + // LGraph.serialize() seems to reuse objects in the original graph. But we + // need to change node IDs here, so clone it first. + const subgraph = LiteGraph.cloneObject(data.subgraph) + + this.reassignSubgraphUUIDs(subgraph); + + data.subgraph = subgraph; + } + + delete data["id"]; + delete data["inputs"]; + delete data["outputs"]; + node.configure(data); + return node; + }; + + Subgraph.prototype.buildFromNodes = function(nodes) { + // clear all? + // TODO + + // nodes that connect data between parent graph and subgraph + var subgraph_inputs = []; + var subgraph_outputs = []; + + // mark inner nodes + var ids = {}; + var min_x = 0; + var max_x = 0; + for(var i = 0; i < nodes.length; ++i) { + var node = nodes[i]; + ids[node.id] = node; + min_x = Math.min( node.pos[0], min_x ); + max_x = Math.max( node.pos[0], min_x ); + } + + var last_input_y = 0; + var last_output_y = 0; + + for(var i = 0; i < nodes.length; ++i) { + var node = nodes[i]; + // check inputs + if( node.inputs ) + for(var j = 0; j < node.inputs.length; ++j) { + var input = node.inputs[j]; + if( !input || !input.link ) + continue; + var link = node.graph.links[input.link]; + if(!link) + continue; + if( ids[link.origin_id] ) + continue; + // this.addInput(input.name,link.type); + this.subgraph.addInput(input.name,link.type); + /* + var input_node = LiteGraph.createNode("graph/input"); + this.subgraph.add( input_node ); + input_node.pos = [min_x - 200, last_input_y ]; + last_input_y += 100; + */ + } + + // check outputs + if( node.outputs ) + for(var j = 0; j < node.outputs.length; ++j) { + var output = node.outputs[j]; + if( !output || !output.links || !output.links.length ) + continue; + var is_external = false; + for(var k = 0; k < output.links.length; ++k) { + var link = node.graph.links[output.links[k]]; + if(!link) + continue; + if( ids[link.target_id] ) + continue; + is_external = true; + break; + } + if(!is_external) + continue; + // this.addOutput(output.name,output.type); + /* + var output_node = LiteGraph.createNode("graph/output"); + this.subgraph.add( output_node ); + output_node.pos = [max_x + 50, last_output_y ]; + last_output_y += 100; + */ + } + } + + // detect inputs and outputs + // split every connection in two data_connection nodes + // keep track of internal connections + // connect external connections + + // clone nodes inside subgraph and try to reconnect them + + // connect edge subgraph nodes to extarnal connections nodes + } + + LiteGraph.Subgraph = Subgraph; + LiteGraph.registerNodeType("graph/subgraph", Subgraph); + + // Input for a subgraph + function GraphInput() { + this.addOutput("", "number"); + + this.name_in_graph = ""; + this.properties = { + name: "", + type: "number", + value: 0, + }; + + var that = this; + + this.name_widget = this.addWidget( + "text", + "Name", + this.properties.name, + function(v) { + if (!v) { + return; + } + that.setProperty("name",v); + }, + ); + this.type_widget = this.addWidget( + "text", + "Type", + this.properties.type, + function(v) { + that.setProperty("type",v); + }, + ); + + this.value_widget = this.addWidget( + "number", + "Value", + this.properties.value, + function(v) { + that.setProperty("value",v); + }, + ); + + this.widgets_up = true; + this.size = [180, 90]; + } + + GraphInput.title = "Input"; + GraphInput.desc = "Input of the graph"; + + GraphInput.prototype.onConfigure = function() { + this.updateType(); + } + + // ensures the type in the node output and the type in the associated graph input are the same + GraphInput.prototype.updateType = function() { + var type = this.properties.type; + this.type_widget.value = type; + + // update output + if(this.outputs[0].type != type) { + if (!LiteGraph.isValidConnection(this.outputs[0].type,type)) + this.disconnectOutput(0); + this.outputs[0].type = type; + } + + // update widget + if(type == "number") { + this.value_widget.type = "number"; + this.value_widget.value = 0; + } else if(type == "boolean") { + this.value_widget.type = "toggle"; + this.value_widget.value = true; + } else if(type == "string") { + this.value_widget.type = "text"; + this.value_widget.value = ""; + } else { + this.value_widget.type = null; + this.value_widget.value = null; + } + this.properties.value = this.value_widget.value; + + // update graph + if (this.graph && this.name_in_graph) { + this.graph.changeInputType(this.name_in_graph, type); + } + } + + // this is executed AFTER the property has changed + GraphInput.prototype.onPropertyChanged = function(name,v) { + if( name == "name" ) { + if (v == "" || v == this.name_in_graph || v == "enabled") { + return false; + } + if(this.graph) { + if (this.name_in_graph) { + // already added + this.graph.renameInput( this.name_in_graph, v ); + } else { + this.graph.addInput( v, this.properties.type ); + } + } // what if not?! + this.name_widget.value = v; + this.name_in_graph = v; + } else if( name == "type" ) { + this.updateType(); + } + } + + GraphInput.prototype.getTitle = function() { + if (this.flags.collapsed) { + return this.properties.name; + } + return this.title; + }; + + GraphInput.prototype.onAction = function(action, param) { + if (this.properties.type == LiteGraph.EVENT) { + this.triggerSlot(0, param); + } + }; + + GraphInput.prototype.onExecute = function() { + var name = this.properties.name; + // read from global input + var data = this.graph.inputs[name]; + if (!data) { + this.setOutputData(0, this.properties.value ); + return; + } + + this.setOutputData(0, data.value !== undefined ? data.value : this.properties.value ); + }; + + GraphInput.prototype.onRemoved = function() { + if (this.name_in_graph) { + this.graph.removeInput(this.name_in_graph); + } + }; + + LiteGraph.GraphInput = GraphInput; + LiteGraph.registerNodeType("graph/input", GraphInput); + + // Output for a subgraph + function GraphOutput() { + this.addInput("", ""); + + this.name_in_graph = ""; + this.properties = { name: "", type: "" }; + var that = this; + + // Object.defineProperty(this.properties, "name", { + // get: function() { + // return that.name_in_graph; + // }, + // set: function(v) { + // if (v == "" || v == that.name_in_graph) { + // return; + // } + // if (that.name_in_graph) { + // //already added + // that.graph.renameOutput(that.name_in_graph, v); + // } else { + // that.graph.addOutput(v, that.properties.type); + // } + // that.name_widget.value = v; + // that.name_in_graph = v; + // }, + // enumerable: true + // }); + + // Object.defineProperty(this.properties, "type", { + // get: function() { + // return that.inputs[0].type; + // }, + // set: function(v) { + // if (v == "action" || v == "event") { + // v = LiteGraph.ACTION; + // } + // if (!LiteGraph.isValidConnection(that.inputs[0].type,v)) + // that.disconnectInput(0); + // that.inputs[0].type = v; + // if (that.name_in_graph) { + // //already added + // that.graph.changeOutputType( + // that.name_in_graph, + // that.inputs[0].type + // ); + // } + // that.type_widget.value = v || ""; + // }, + // enumerable: true + // }); + + this.name_widget = this.addWidget("text","Name",this.properties.name,"name"); + this.type_widget = this.addWidget("text","Type",this.properties.type,"type"); + this.widgets_up = true; + this.size = [180, 60]; + } + + GraphOutput.title = "Output"; + GraphOutput.desc = "Output of the graph"; + + GraphOutput.prototype.onPropertyChanged = function (name, v) { + if (name == "name") { + if (v == "" || v == this.name_in_graph || v == "enabled") { + return false; + } + if (this.graph) { + if (this.name_in_graph) { + // already added + this.graph.renameOutput(this.name_in_graph, v); + } else { + this.graph.addOutput(v, this.properties.type); + } + } // what if not?! + this.name_widget.value = v; + this.name_in_graph = v; + } else if (name == "type") { + this.updateType(); + } + } + + GraphOutput.prototype.updateType = function () { + var type = this.properties.type; + if (this.type_widget) + this.type_widget.value = type; + + // update output + if (this.inputs[0].type != type) { + + if ( type == "action" || type == "event") + type = LiteGraph.EVENT; + if (!LiteGraph.isValidConnection(this.inputs[0].type, type)) + this.disconnectInput(0); + this.inputs[0].type = type; + } + + // update graph + if (this.graph && this.name_in_graph) { + this.graph.changeOutputType(this.name_in_graph, type); + } + } + + + + GraphOutput.prototype.onExecute = function() { + this._value = this.getInputData(0); + this.graph.setOutputData(this.properties.name, this._value); + }; + + GraphOutput.prototype.onAction = function(action, param) { + if (this.properties.type == LiteGraph.ACTION) { + this.graph.trigger( this.properties.name, param ); + } + }; + + GraphOutput.prototype.onRemoved = function() { + if (this.name_in_graph) { + this.graph.removeOutput(this.name_in_graph); + } + }; + + GraphOutput.prototype.getTitle = function() { + if (this.flags.collapsed) { + return this.properties.name; + } + return this.title; + }; + + LiteGraph.GraphOutput = GraphOutput; + LiteGraph.registerNodeType("graph/output", GraphOutput); + + // Constant + function ConstantNumber() { + this.addOutput("value", "number"); + this.addProperty("value", 1.0); + this.widget = this.addWidget("number","value",1,"value"); + this.widgets_up = true; + this.size = [180, 30]; + } + + ConstantNumber.title = "Const Number"; + ConstantNumber.desc = "Constant number"; + + ConstantNumber.prototype.onExecute = function() { + this.setOutputData(0, parseFloat(this.properties["value"])); + }; + + ConstantNumber.prototype.getTitle = function() { + if (this.flags.collapsed) { + return this.properties.value; + } + return this.title; + }; + + ConstantNumber.prototype.setValue = function(v) { + this.setProperty("value",v); + } + + ConstantNumber.prototype.onDrawBackground = function(ctx) { + // show the current value + this.outputs[0].label = this.properties["value"].toFixed(3); + }; + + LiteGraph.registerNodeType("basic/const", ConstantNumber); + + function ConstantBoolean() { + this.addOutput("bool", "boolean"); + this.addProperty("value", true); + this.widget = this.addWidget("toggle","value",true,"value"); + this.serialize_widgets = true; + this.widgets_up = true; + this.size = [140, 30]; + } + + ConstantBoolean.title = "Const Boolean"; + ConstantBoolean.desc = "Constant boolean"; + ConstantBoolean.prototype.getTitle = ConstantNumber.prototype.getTitle; + + ConstantBoolean.prototype.onExecute = function() { + this.setOutputData(0, this.properties["value"]); + }; + + ConstantBoolean.prototype.setValue = ConstantNumber.prototype.setValue; + + ConstantBoolean.prototype.onGetInputs = function() { + return [["toggle", LiteGraph.ACTION]]; + }; + + ConstantBoolean.prototype.onAction = function(action) { + this.setValue( !this.properties.value ); + } + + LiteGraph.registerNodeType("basic/boolean", ConstantBoolean); + + function ConstantString() { + this.addOutput("string", "string"); + this.addProperty("value", ""); + this.widget = this.addWidget("text","value","","value"); // link to property value + this.widgets_up = true; + this.size = [180, 30]; + } + + ConstantString.title = "Const String"; + ConstantString.desc = "Constant string"; + + ConstantString.prototype.getTitle = ConstantNumber.prototype.getTitle; + + ConstantString.prototype.onExecute = function() { + this.setOutputData(0, this.properties["value"]); + }; + + ConstantString.prototype.setValue = ConstantNumber.prototype.setValue; + + ConstantString.prototype.onDropFile = function(file) { + var that = this; + var reader = new FileReader(); + reader.onload = function(e) { + that.setProperty("value",e.target.result); + } + reader.readAsText(file); + } + + LiteGraph.registerNodeType("basic/string", ConstantString); + + function ConstantObject() { + this.addOutput("obj", "object"); + this.size = [120, 30]; + this._object = {}; + } + + ConstantObject.title = "Const Object"; + ConstantObject.desc = "Constant Object"; + + ConstantObject.prototype.onExecute = function() { + this.setOutputData(0, this._object); + }; + + LiteGraph.registerNodeType( "basic/object", ConstantObject ); + + function ConstantFile() { + this.addInput("url", "string"); + this.addOutput("file", "string"); + this.addProperty("url", ""); + this.addProperty("type", "text"); + this.widget = this.addWidget("text","url","","url"); + this._data = null; + } + + ConstantFile.title = "Const File"; + ConstantFile.desc = "Fetches a file from an url"; + ConstantFile["@type"] = { type: "enum", values: ["text","arraybuffer","blob","json"] }; + + ConstantFile.prototype.onPropertyChanged = function(name, value) { + if (name == "url") { + if( value == null || value == "") + this._data = null; + else { + this.fetchFile(value); + } + } + } + + ConstantFile.prototype.onExecute = function() { + var url = this.getInputData(0) || this.properties.url; + if(url && (url != this._url || this._type != this.properties.type)) + this.fetchFile(url); + this.setOutputData(0, this._data ); + }; + + ConstantFile.prototype.setValue = ConstantNumber.prototype.setValue; + + ConstantFile.prototype.fetchFile = function(url) { + var that = this; + if(!url || url.constructor !== String) { + that._data = null; + that.boxcolor = null; + return; + } + + this._url = url; + this._type = this.properties.type; + if (url.substr(0, 4) == "http" && LiteGraph.proxy) { + url = LiteGraph.proxy + url.substr(url.indexOf(":") + 3); + } + fetch(url) + .then(function(response) { + if(!response.ok) + throw new Error("File not found"); + + if(that.properties.type == "arraybuffer") + return response.arrayBuffer(); + else if(that.properties.type == "text") + return response.text(); + else if(that.properties.type == "json") + return response.json(); + else if(that.properties.type == "blob") + return response.blob(); + }) + .then(function(data) { + that._data = data; + that.boxcolor = "#AEA"; + }) + .catch(function(error) { + that._data = null; + that.boxcolor = "red"; + console.error("error fetching file:",url); + }); + }; + + ConstantFile.prototype.onDropFile = function(file) { + var that = this; + this._url = file.name; + this._type = this.properties.type; + this.properties.url = file.name; + var reader = new FileReader(); + reader.onload = function(e) { + that.boxcolor = "#AEA"; + var v = e.target.result; + if( that.properties.type == "json" ) + v = JSON.parse(v); + that._data = v; + } + if(that.properties.type == "arraybuffer") + reader.readAsArrayBuffer(file); + else if(that.properties.type == "text" || that.properties.type == "json") + reader.readAsText(file); + else if(that.properties.type == "blob") + return reader.readAsBinaryString(file); + } + + LiteGraph.registerNodeType("basic/file", ConstantFile); + + + // to store json objects + function JSONParse() { + this.addInput("parse", LiteGraph.ACTION); + this.addInput("json", "string"); + this.addOutput("done", LiteGraph.EVENT); + this.addOutput("object", "object"); + this.widget = this.addWidget("button","parse","",this.parse.bind(this)); + this._str = null; + this._obj = null; + } + + JSONParse.title = "JSON Parse"; + JSONParse.desc = "Parses JSON String into object"; + + JSONParse.prototype.parse = function() { + if(!this._str) + return; + + try { + this._str = this.getInputData(1); + this._obj = JSON.parse(this._str); + this.boxcolor = "#AEA"; + this.triggerSlot(0); + } catch (err) { + this.boxcolor = "red"; + } + } + + JSONParse.prototype.onExecute = function() { + this._str = this.getInputData(1); + this.setOutputData(1, this._obj); + }; + + JSONParse.prototype.onAction = function(name) { + if(name == "parse") + this.parse(); + } + + LiteGraph.registerNodeType("basic/jsonparse", JSONParse); + + // to store json objects + function ConstantData() { + this.addOutput("data", "object"); + this.addProperty("value", ""); + this.widget = this.addWidget("text","json","","value"); + this.widgets_up = true; + this.size = [140, 30]; + this._value = null; + } + + ConstantData.title = "Const Data"; + ConstantData.desc = "Constant Data"; + + ConstantData.prototype.onPropertyChanged = function(name, value) { + this.widget.value = value; + if (value == null || value == "") { + return; + } + + try { + this._value = JSON.parse(value); + this.boxcolor = "#AEA"; + } catch (err) { + this.boxcolor = "red"; + } + }; + + ConstantData.prototype.onExecute = function() { + this.setOutputData(0, this._value); + }; + + ConstantData.prototype.setValue = ConstantNumber.prototype.setValue; + + LiteGraph.registerNodeType("basic/data", ConstantData); + + // to store json objects + function ConstantArray() { + this._value = []; + this.addInput("json", ""); + this.addOutput("arrayOut", "array"); + this.addOutput("length", "number"); + this.addProperty("value", "[]"); + this.widget = this.addWidget("text","array",this.properties.value,"value"); + this.widgets_up = true; + this.size = [140, 50]; + } + + ConstantArray.title = "Const Array"; + ConstantArray.desc = "Constant Array"; + + ConstantArray.prototype.onPropertyChanged = function(name, value) { + this.widget.value = value; + if (value == null || value == "") { + return; + } + + try { + if(value[0] != "[") + this._value = JSON.parse("[" + value + "]"); + else + this._value = JSON.parse(value); + this.boxcolor = "#AEA"; + } catch (err) { + this.boxcolor = "red"; + } + }; + + ConstantArray.prototype.onExecute = function() { + var v = this.getInputData(0); + if(v && v.length) { // clone + if(!this._value) + this._value = new Array(); + this._value.length = v.length; + for(var i = 0; i < v.length; ++i) + this._value[i] = v[i]; + } + this.setOutputData(0, this._value); + this.setOutputData(1, this._value ? ( this._value.length || 0) : 0 ); + }; + + ConstantArray.prototype.setValue = ConstantNumber.prototype.setValue; + + LiteGraph.registerNodeType("basic/array", ConstantArray); + + function SetArray() { + this.addInput("arr", "array"); + this.addInput("value", ""); + this.addOutput("arr", "array"); + this.properties = { index: 0 }; + this.widget = this.addWidget("number","i",this.properties.index,"index",{precision: 0, step: 10, min: 0}); + } + + SetArray.title = "Set Array"; + SetArray.desc = "Sets index of array"; + + SetArray.prototype.onExecute = function() { + var arr = this.getInputData(0); + if(!arr) + return; + var v = this.getInputData(1); + if(v === undefined ) + return; + if(this.properties.index) + arr[Math.floor(this.properties.index)] = v; + this.setOutputData(0,arr); + }; + + LiteGraph.registerNodeType("basic/set_array", SetArray ); + + function ArrayElement() { + this.addInput("array", "array,table,string"); + this.addInput("index", "number"); + this.addOutput("value", ""); + this.addProperty("index",0); + } + + ArrayElement.title = "Array[i]"; + ArrayElement.desc = "Returns an element from an array"; + + ArrayElement.prototype.onExecute = function() { + var array = this.getInputData(0); + var index = this.getInputData(1); + if(index == null) + index = this.properties.index; + if(array == null || index == null ) + return; + this.setOutputData(0, array[Math.floor(Number(index))] ); + }; + + LiteGraph.registerNodeType("basic/array[]", ArrayElement); + + function TableElement() { + this.addInput("table", "table"); + this.addInput("row", "number"); + this.addInput("col", "number"); + this.addOutput("value", ""); + this.addProperty("row",0); + this.addProperty("column",0); + } + + TableElement.title = "Table[row][col]"; + TableElement.desc = "Returns an element from a table"; + + TableElement.prototype.onExecute = function() { + var table = this.getInputData(0); + var row = this.getInputData(1); + var col = this.getInputData(2); + if(row == null) + row = this.properties.row; + if(col == null) + col = this.properties.column; + if(table == null || row == null || col == null) + return; + var row = table[Math.floor(Number(row))]; + if(row) + this.setOutputData(0, row[Math.floor(Number(col))] ); + else + this.setOutputData(0, null ); + }; + + LiteGraph.registerNodeType("basic/table[][]", TableElement); + + function ObjectProperty() { + this.addInput("obj", "object"); + this.addOutput("property", 0); + this.addProperty("value", 0); + this.widget = this.addWidget("text","prop.","",this.setValue.bind(this) ); + this.widgets_up = true; + this.size = [140, 30]; + this._value = null; + } + + ObjectProperty.title = "Object property"; + ObjectProperty.desc = "Outputs the property of an object"; + + ObjectProperty.prototype.setValue = function(v) { + this.properties.value = v; + this.widget.value = v; + }; + + ObjectProperty.prototype.getTitle = function() { + if (this.flags.collapsed) { + return "in." + this.properties.value; + } + return this.title; + }; + + ObjectProperty.prototype.onPropertyChanged = function(name, value) { + this.widget.value = value; + }; + + ObjectProperty.prototype.onExecute = function() { + var data = this.getInputData(0); + if (data != null) { + this.setOutputData(0, data[this.properties.value]); + } + }; + + LiteGraph.registerNodeType("basic/object_property", ObjectProperty); + + function ObjectKeys() { + this.addInput("obj", ""); + this.addOutput("keys", "array"); + this.size = [140, 30]; + } + + ObjectKeys.title = "Object keys"; + ObjectKeys.desc = "Outputs an array with the keys of an object"; + + ObjectKeys.prototype.onExecute = function() { + var data = this.getInputData(0); + if (data != null) { + this.setOutputData(0, Object.keys(data) ); + } + }; + + LiteGraph.registerNodeType("basic/object_keys", ObjectKeys); + + + function SetObject() { + this.addInput("obj", ""); + this.addInput("value", ""); + this.addOutput("obj", ""); + this.properties = { property: "" }; + this.name_widget = this.addWidget("text","prop.",this.properties.property,"property"); + } + + SetObject.title = "Set Object"; + SetObject.desc = "Adds propertiesrty to object"; + + SetObject.prototype.onExecute = function() { + var obj = this.getInputData(0); + if(!obj) + return; + var v = this.getInputData(1); + if(v === undefined ) + return; + if(this.properties.property) + obj[this.properties.property] = v; + this.setOutputData(0,obj); + }; + + LiteGraph.registerNodeType("basic/set_object", SetObject ); + + + function MergeObjects() { + this.addInput("A", "object"); + this.addInput("B", "object"); + this.addOutput("out", "object"); + this._result = {}; + var that = this; + this.addWidget("button","clear","",function() { + that._result = {}; + }); + this.size = this.computeSize(); + } + + MergeObjects.title = "Merge Objects"; + MergeObjects.desc = "Creates an object copying properties from others"; + + MergeObjects.prototype.onExecute = function() { + var A = this.getInputData(0); + var B = this.getInputData(1); + var C = this._result; + if(A) + for(var i in A) + C[i] = A[i]; + if(B) + for(var i in B) + C[i] = B[i]; + this.setOutputData(0,C); + }; + + LiteGraph.registerNodeType("basic/merge_objects", MergeObjects ); + + // Store as variable + function Variable() { + this.size = [60, 30]; + this.addInput("in"); + this.addOutput("out"); + this.properties = { varname: "myname", container: Variable.LITEGRAPH }; + this.value = null; + } + + Variable.title = "Variable"; + Variable.desc = "store/read variable value"; + + Variable.LITEGRAPH = 0; // between all graphs + Variable.GRAPH = 1; // only inside this graph + Variable.GLOBALSCOPE = 2; // attached to Window + + Variable["@container"] = { type: "enum", values: {"litegraph": Variable.LITEGRAPH, "graph": Variable.GRAPH,"global": Variable.GLOBALSCOPE} }; + + Variable.prototype.onExecute = function() { + var container = this.getContainer(); + + if(this.isInputConnected(0)) { + this.value = this.getInputData(0); + container[this.properties.varname] = this.value; + this.setOutputData(0, this.value ); + return; + } + + this.setOutputData( 0, container[this.properties.varname] ); + }; + + Variable.prototype.getContainer = function() { + switch(this.properties.container) { + case Variable.GRAPH: + if(this.graph) + return this.graph.vars; + return {}; + case Variable.GLOBALSCOPE: + return global; + case Variable.LITEGRAPH: + default: + return LiteGraph.Globals; + } + } + + Variable.prototype.getTitle = function() { + return this.properties.varname; + }; + + LiteGraph.registerNodeType("basic/variable", Variable); + + function length(v) { + if(v && v.length != null) + return Number(v.length); + return 0; + } + + LiteGraph.wrapFunctionAsNode( + "basic/length", + length, + [""], + "number", + ); + + LiteGraph.wrapFunctionAsNode( + "basic/not", + function(a) { + return !a; + }, + [""], + "boolean", + ); + + function DownloadData() { + this.size = [60, 30]; + this.addInput("data", 0 ); + this.addInput("download", LiteGraph.ACTION ); + this.properties = { filename: "data.json" }; + this.value = null; + var that = this; + this.addWidget("button","Download","", function(v) { + if(!that.value) + return; + that.downloadAsFile(); + }); + } + + DownloadData.title = "Download"; + DownloadData.desc = "Download some data"; + + DownloadData.prototype.downloadAsFile = function() { + if(this.value == null) + return; + + var str = null; + if(this.value.constructor === String) + str = this.value; + else + str = JSON.stringify(this.value); + + var file = new Blob([str]); + var url = URL.createObjectURL( file ); + var element = document.createElement("a"); + element.setAttribute('href', url); + element.setAttribute('download', this.properties.filename ); + element.style.display = 'none'; + document.body.appendChild(element); + element.click(); + document.body.removeChild(element); + setTimeout( function() { + URL.revokeObjectURL( url ); + }, 1000*60 ); // wait one minute to revoke url + } + + DownloadData.prototype.onAction = function(action, param) { + var that = this; + setTimeout( function() { + that.downloadAsFile(); + }, 100); // deferred to avoid blocking the renderer with the popup + } + + DownloadData.prototype.onExecute = function() { + if (this.inputs[0]) { + this.value = this.getInputData(0); + } + }; + + DownloadData.prototype.getTitle = function() { + if (this.flags.collapsed) { + return this.properties.filename; + } + return this.title; + }; + + LiteGraph.registerNodeType("basic/download", DownloadData); + + + + // Watch a value in the editor + function Watch() { + this.size = [60, 30]; + this.addInput("value", 0, { label: "" }); + this.value = 0; + } + + Watch.title = "Watch"; + Watch.desc = "Show value of input"; + + Watch.prototype.onExecute = function() { + if (this.inputs[0]) { + this.value = this.getInputData(0); + } + }; + + Watch.prototype.getTitle = function() { + if (this.flags.collapsed) { + return this.inputs[0].label; + } + return this.title; + }; + + Watch.toString = function(o) { + if (o == null) { + return "null"; + } else if (o.constructor === Number) { + return o.toFixed(3); + } else if (o.constructor === Array) { + var str = "["; + for (var i = 0; i < o.length; ++i) { + str += Watch.toString(o[i]) + (i + 1 != o.length ? "," : ""); + } + str += "]"; + return str; + } else { + return String(o); + } + }; + + Watch.prototype.onDrawBackground = function(ctx) { + // show the current value + this.inputs[0].label = Watch.toString(this.value); + }; + + LiteGraph.registerNodeType("basic/watch", Watch); + + // in case one type doesnt match other type but you want to connect them anyway + function Cast() { + this.addInput("in", 0); + this.addOutput("out", 0); + this.size = [40, 30]; + } + + Cast.title = "Cast"; + Cast.desc = "Allows to connect different types"; + + Cast.prototype.onExecute = function() { + this.setOutputData(0, this.getInputData(0)); + }; + + LiteGraph.registerNodeType("basic/cast", Cast); + + // Show value inside the debug console + function Console() { + this.mode = LiteGraph.ON_EVENT; + this.size = [80, 30]; + this.addProperty("msg", ""); + this.addInput("log", LiteGraph.EVENT); + this.addInput("msg", 0); + } + + Console.title = "Console"; + Console.desc = "Show value inside the console"; + + Console.prototype.onAction = function(action, param) { + // param is the action + var msg = this.getInputData(1); // getInputDataByName("msg"); + // if (msg == null || typeof msg == "undefined") return; + if (!msg) msg = this.properties.msg; + if (!msg) msg = "Event: "+param; // msg is undefined if the slot is lost? + if (action == "log") { + console.log(msg); + } else if (action == "warn") { + console.warn(msg); + } else if (action == "error") { + console.error(msg); + } + }; + + Console.prototype.onExecute = function() { + var msg = this.getInputData(1); // getInputDataByName("msg"); + if (!msg) msg = this.properties.msg; + if (msg != null && typeof msg != "undefined") { + this.properties.msg = msg; + console.log(msg); + } + }; + + Console.prototype.onGetInputs = function() { + return [ + ["log", LiteGraph.ACTION], + ["warn", LiteGraph.ACTION], + ["error", LiteGraph.ACTION], + ]; + }; + + LiteGraph.registerNodeType("basic/console", Console); + + // Show value inside the debug console + function Alert() { + this.mode = LiteGraph.ON_EVENT; + this.addProperty("msg", ""); + this.addInput("", LiteGraph.EVENT); + var that = this; + this.widget = this.addWidget("text", "Text", "", "msg"); + this.widgets_up = true; + this.size = [200, 30]; + } + + Alert.title = "Alert"; + Alert.desc = "Show an alert window"; + Alert.color = "#510"; + + Alert.prototype.onConfigure = function(o) { + this.widget.value = o.properties.msg; + }; + + Alert.prototype.onAction = function(action, param) { + var msg = this.properties.msg; + setTimeout(function() { + alert(msg); + }, 10); + }; + + LiteGraph.registerNodeType("basic/alert", Alert); + + // Execites simple code + function NodeScript() { + this.size = [60, 30]; + this.addProperty("onExecute", "return A;"); + this.addInput("A", 0); + this.addInput("B", 0); + this.addOutput("out", 0); + + this._func = null; + this.data = {}; + } + + NodeScript.prototype.onConfigure = function(o) { + if (o.properties.onExecute && LiteGraph.allow_scripts) + this.compileCode(o.properties.onExecute); + else + console.warn("Script not compiled, LiteGraph.allow_scripts is false"); + }; + + NodeScript.title = "Script"; + NodeScript.desc = "executes a code (max 256 characters)"; + + NodeScript.widgets_info = {onExecute: { type: "code" }}; + + NodeScript.prototype.onPropertyChanged = function(name, value) { + if (name == "onExecute" && LiteGraph.allow_scripts) + this.compileCode(value); + else + console.warn("Script not compiled, LiteGraph.allow_scripts is false"); + }; + + NodeScript.prototype.compileCode = function(code) { + this._func = null; + if (code.length > 256) { + console.warn("Script too long, max 256 chars"); + } else { + var code_low = code.toLowerCase(); + var forbidden_words = [ + "script", + "body", + "document", + "eval", + "nodescript", + "function", + ]; // bad security solution + for (var i = 0; i < forbidden_words.length; ++i) { + if (code_low.indexOf(forbidden_words[i]) != -1) { + console.warn("invalid script"); + return; + } + } + try { + this._func = new Function("A", "B", "C", "DATA", "node", code); + } catch (err) { + console.error("Error parsing script"); + console.error(err); + } + } + }; + + NodeScript.prototype.onExecute = function() { + if (!this._func) { + return; + } + + try { + var A = this.getInputData(0); + var B = this.getInputData(1); + var C = this.getInputData(2); + this.setOutputData(0, this._func(A, B, C, this.data, this)); + } catch (err) { + console.error("Error in script"); + console.error(err); + } + }; + + NodeScript.prototype.onGetOutputs = function() { + return [["C", ""]]; + }; + + LiteGraph.registerNodeType("basic/script", NodeScript); + + + function GenericCompare() { + this.addInput("A", 0); + this.addInput("B", 0); + this.addOutput("true", "boolean"); + this.addOutput("false", "boolean"); + this.addProperty("A", 1); + this.addProperty("B", 1); + this.addProperty("OP", "==", "enum", { values: GenericCompare.values }); + this.addWidget("combo","Op.",this.properties.OP,{ property: "OP", values: GenericCompare.values } ); + + this.size = [80, 60]; + } + + GenericCompare.values = ["==", "!="]; // [">", "<", "==", "!=", "<=", ">=", "||", "&&" ]; + GenericCompare["@OP"] = { + type: "enum", + title: "operation", + values: GenericCompare.values, + }; + + GenericCompare.title = "Compare *"; + GenericCompare.desc = "evaluates condition between A and B"; + + GenericCompare.prototype.getTitle = function() { + return "*A " + this.properties.OP + " *B"; + }; + + GenericCompare.prototype.onExecute = function() { + var A = this.getInputData(0); + if (A === undefined) { + A = this.properties.A; + } else { + this.properties.A = A; + } + + var B = this.getInputData(1); + if (B === undefined) { + B = this.properties.B; + } else { + this.properties.B = B; + } + + var result = false; + if (typeof A == typeof B) { + switch (this.properties.OP) { + case "==": + case "!=": + // traverse both objects.. consider that this is not a true deep check! consider underscore or other library for thath :: _isEqual() + result = true; + switch(typeof A) { + case "object": + var aProps = Object.getOwnPropertyNames(A); + var bProps = Object.getOwnPropertyNames(B); + if (aProps.length != bProps.length) { + result = false; + break; + } + for (var i = 0; i < aProps.length; i++) { + var propName = aProps[i]; + if (A[propName] !== B[propName]) { + result = false; + break; + } + } + break; + default: + result = A == B; + } + if (this.properties.OP == "!=") result = !result; + break; + /* case ">": + result = A > B; + break; + case "<": + result = A < B; + break; + case "<=": + result = A <= B; + break; + case ">=": + result = A >= B; + break; + case "||": + result = A || B; + break; + case "&&": + result = A && B; + break;*/ + } + } + this.setOutputData(0, result); + this.setOutputData(1, !result); + }; + + LiteGraph.registerNodeType("basic/CompareValues", GenericCompare); + +})(this); + +// event related nodes +(function(global) { + var LiteGraph = global.LiteGraph; + + // Show value inside the debug console + function LogEvent() { + this.size = [60, 30]; + this.addInput("event", LiteGraph.ACTION); + } + + LogEvent.title = "Log Event"; + LogEvent.desc = "Log event in console"; + + LogEvent.prototype.onAction = function(action, param, options) { + console.log(action, param); + }; + + LiteGraph.registerNodeType("events/log", LogEvent); + + // convert to Event if the value is true + function TriggerEvent() { + this.size = [60, 30]; + this.addInput("if", ""); + this.addOutput("true", LiteGraph.EVENT); + this.addOutput("change", LiteGraph.EVENT); + this.addOutput("false", LiteGraph.EVENT); + this.properties = { only_on_change: true }; + this.prev = 0; + } + + TriggerEvent.title = "TriggerEvent"; + TriggerEvent.desc = "Triggers event if input evaluates to true"; + + TriggerEvent.prototype.onExecute = function( param, options) { + var v = this.getInputData(0); + var changed = (v != this.prev); + if(this.prev === 0) + changed = false; + var must_resend = (changed && this.properties.only_on_change) || (!changed && !this.properties.only_on_change); + if(v && must_resend ) + this.triggerSlot(0, param, null, options); + if(!v && must_resend) + this.triggerSlot(2, param, null, options); + if(changed) + this.triggerSlot(1, param, null, options); + this.prev = v; + }; + + LiteGraph.registerNodeType("events/trigger", TriggerEvent); + + // Sequence of events + function Sequence() { + var that = this; + this.addInput("", LiteGraph.ACTION); + this.addInput("", LiteGraph.ACTION); + this.addInput("", LiteGraph.ACTION); + this.addOutput("", LiteGraph.EVENT); + this.addOutput("", LiteGraph.EVENT); + this.addOutput("", LiteGraph.EVENT); + this.addWidget("button","+",null,function() { + that.addInput("", LiteGraph.ACTION); + that.addOutput("", LiteGraph.EVENT); + }); + this.size = [90, 70]; + this.flags = { horizontal: true, render_box: false }; + } + + Sequence.title = "Sequence"; + Sequence.desc = "Triggers a sequence of events when an event arrives"; + + Sequence.prototype.getTitle = function() { + return ""; + }; + + Sequence.prototype.onAction = function(action, param, options) { + if (this.outputs) { + options = options || {}; + for (var i = 0; i < this.outputs.length; ++i) { + var output = this.outputs[i]; + // needs more info about this... + if( options.action_call ) // CREATE A NEW ID FOR THE ACTION + options.action_call = options.action_call + "_seq_" + i; + else + options.action_call = this.id + "_" + (action ? action : "action")+"_seq_"+i+"_"+Math.floor(Math.random()*9999); + this.triggerSlot(i, param, null, options); + } + } + }; + + LiteGraph.registerNodeType("events/sequence", Sequence); + + + // Sequence of events + function WaitAll() { + var that = this; + this.addInput("", LiteGraph.ACTION); + this.addInput("", LiteGraph.ACTION); + this.addOutput("", LiteGraph.EVENT); + this.addWidget("button","+",null,function() { + that.addInput("", LiteGraph.ACTION); + that.size[0] = 90; + }); + this.size = [90, 70]; + this.ready = []; + } + + WaitAll.title = "WaitAll"; + WaitAll.desc = "Wait until all input events arrive then triggers output"; + + WaitAll.prototype.getTitle = function() { + return ""; + }; + + WaitAll.prototype.onDrawBackground = function(ctx) { + if (this.flags.collapsed) { + return; + } + for(var i = 0; i < this.inputs.length; ++i) { + var y = i * LiteGraph.NODE_SLOT_HEIGHT + 10; + ctx.fillStyle = this.ready[i] ? "#AFB" : "#000"; + ctx.fillRect(20, y, 10, 10); + } + } + + WaitAll.prototype.onAction = function(action, param, options, slot_index) { + if(slot_index == null) + return; + + // check all + this.ready.length = this.outputs.length; + this.ready[slot_index] = true; + for(var i = 0; i < this.ready.length;++i) + if(!this.ready[i]) + return; + // pass + this.reset(); + this.triggerSlot(0); + }; + + WaitAll.prototype.reset = function() { + this.ready.length = 0; + } + + LiteGraph.registerNodeType("events/waitAll", WaitAll); + + + // Sequencer for events + function Stepper() { + var that = this; + this.properties = { index: 0 }; + this.addInput("index", "number"); + this.addInput("step", LiteGraph.ACTION); + this.addInput("reset", LiteGraph.ACTION); + this.addOutput("index", "number"); + this.addOutput("", LiteGraph.EVENT); + this.addOutput("", LiteGraph.EVENT); + this.addOutput("", LiteGraph.EVENT,{removable: true}); + this.addWidget("button","+",null,function() { + that.addOutput("", LiteGraph.EVENT, {removable: true}); + }); + this.size = [120, 120]; + this.flags = { render_box: false }; + } + + Stepper.title = "Stepper"; + Stepper.desc = "Trigger events sequentially when an tick arrives"; + + Stepper.prototype.onDrawBackground = function(ctx) { + if (this.flags.collapsed) { + return; + } + var index = this.properties.index || 0; + ctx.fillStyle = "#AFB"; + var w = this.size[0]; + var y = (index + 1)* LiteGraph.NODE_SLOT_HEIGHT + 4; + ctx.beginPath(); + ctx.moveTo(w - 30, y); + ctx.lineTo(w - 30, y + LiteGraph.NODE_SLOT_HEIGHT); + ctx.lineTo(w - 15, y + LiteGraph.NODE_SLOT_HEIGHT * 0.5); + ctx.fill(); + } + + Stepper.prototype.onExecute = function() { + var index = this.getInputData(0); + if(index != null) { + index = Math.floor(index); + index = clamp( index, 0, this.outputs ? (this.outputs.length - 2) : 0 ); + if( index != this.properties.index ) { + this.properties.index = index; + this.triggerSlot( index+1 ); + } + } + + this.setOutputData(0, this.properties.index ); + } + + Stepper.prototype.onAction = function(action, param) { + if(action == "reset") + this.properties.index = 0; + else if(action == "step") { + this.triggerSlot(this.properties.index+1, param); + var n = this.outputs ? this.outputs.length - 1 : 0; + this.properties.index = (this.properties.index + 1) % n; + } + }; + + LiteGraph.registerNodeType("events/stepper", Stepper); + + // Filter events + function FilterEvent() { + this.size = [60, 30]; + this.addInput("event", LiteGraph.ACTION); + this.addOutput("event", LiteGraph.EVENT); + this.properties = { + equal_to: "", + has_property: "", + property_equal_to: "", + }; + } + + FilterEvent.title = "Filter Event"; + FilterEvent.desc = "Blocks events that do not match the filter"; + + FilterEvent.prototype.onAction = function(action, param, options) { + if (param == null) { + return; + } + + if (this.properties.equal_to && this.properties.equal_to != param) { + return; + } + + if (this.properties.has_property) { + var prop = param[this.properties.has_property]; + if (prop == null) { + return; + } + + if ( + this.properties.property_equal_to && + this.properties.property_equal_to != prop + ) { + return; + } + } + + this.triggerSlot(0, param, null, options); + }; + + LiteGraph.registerNodeType("events/filter", FilterEvent); + + + function EventBranch() { + this.addInput("in", LiteGraph.ACTION); + this.addInput("cond", "boolean"); + this.addOutput("true", LiteGraph.EVENT); + this.addOutput("false", LiteGraph.EVENT); + this.size = [120, 60]; + this._value = false; + } + + EventBranch.title = "Branch"; + EventBranch.desc = "If condition is true, outputs triggers true, otherwise false"; + + EventBranch.prototype.onExecute = function() { + this._value = this.getInputData(1); + } + + EventBranch.prototype.onAction = function(action, param, options) { + this._value = this.getInputData(1); + this.triggerSlot(this._value ? 0 : 1, param, null, options); + } + + LiteGraph.registerNodeType("events/branch", EventBranch); + + // Show value inside the debug console + function EventCounter() { + this.addInput("inc", LiteGraph.ACTION); + this.addInput("dec", LiteGraph.ACTION); + this.addInput("reset", LiteGraph.ACTION); + this.addOutput("change", LiteGraph.EVENT); + this.addOutput("num", "number"); + this.addProperty("doCountExecution", false, "boolean", {name: "Count Executions"}); + this.addWidget("toggle","Count Exec.",this.properties.doCountExecution,"doCountExecution"); + this.num = 0; + } + + EventCounter.title = "Counter"; + EventCounter.desc = "Counts events"; + + EventCounter.prototype.getTitle = function() { + if (this.flags.collapsed) { + return String(this.num); + } + return this.title; + }; - //used to create nodes from wrapping functions - LiteGraph.getParameterNames = function(func) { - return (func + "") - .replace(/[/][/].*$/gm, "") // strip single-line comments - .replace(/\s+/g, "") // strip white space - .replace(/[/][*][^/*]*[*][/]/g, "") // strip multi-line comments /**/ - .split("){", 1)[0] - .replace(/^[^(]*[(]/, "") // extract the parameters - .replace(/=[^,]+/g, "") // strip any ES6 defaults - .split(",") - .filter(Boolean); // split & filter [""] + EventCounter.prototype.onAction = function(action, param, options) { + var v = this.num; + if (action == "inc") { + this.num += 1; + } else if (action == "dec") { + this.num -= 1; + } else if (action == "reset") { + this.num = 0; + } + if (this.num != v) { + this.trigger("change", this.num); + } }; - /* helper for interaction: pointer, touch, mouse Listeners - used by LGraphCanvas DragAndScale ContextMenu*/ - LiteGraph.pointerListenerAdd = function(oDOM, sEvIn, fCall, capture=false) { - if (!oDOM || !oDOM.addEventListener || !sEvIn || typeof fCall!=="function"){ - //console.log("cant pointerListenerAdd "+oDOM+", "+sEvent+", "+fCall); - return; // -- break -- - } - - var sMethod = LiteGraph.pointerevents_method; - var sEvent = sEvIn; - - // UNDER CONSTRUCTION - // convert pointerevents to touch event when not available - if (sMethod=="pointer" && !window.PointerEvent){ - console.warn("sMethod=='pointer' && !window.PointerEvent"); - console.log("Converting pointer["+sEvent+"] : down move up cancel enter TO touchstart touchmove touchend, etc .."); - switch(sEvent){ - case "down":{ - sMethod = "touch"; - sEvent = "start"; - break; - } - case "move":{ - sMethod = "touch"; - //sEvent = "move"; - break; - } - case "up":{ - sMethod = "touch"; - sEvent = "end"; - break; - } - case "cancel":{ - sMethod = "touch"; - //sEvent = "cancel"; - break; - } - case "enter":{ - console.log("debug: Should I send a move event?"); // ??? - break; - } - // case "over": case "out": not used at now - default:{ - console.warn("PointerEvent not available in this browser ? The event "+sEvent+" would not be called"); - } - } - } + EventCounter.prototype.onDrawBackground = function(ctx) { + if (this.flags.collapsed) { + return; + } + ctx.fillStyle = "#AAA"; + ctx.font = "20px Arial"; + ctx.textAlign = "center"; + ctx.fillText(this.num, this.size[0] * 0.5, this.size[1] * 0.5); + }; - switch(sEvent){ - //both pointer and move events - case "down": case "up": case "move": case "over": case "out": case "enter": - { - oDOM.addEventListener(sMethod+sEvent, fCall, capture); - } - // only pointerevents - case "leave": case "cancel": case "gotpointercapture": case "lostpointercapture": - { - if (sMethod!="mouse"){ - return oDOM.addEventListener(sMethod+sEvent, fCall, capture); - } - } - // not "pointer" || "mouse" - default: - return oDOM.addEventListener(sEvent, fCall, capture); - } - } - LiteGraph.pointerListenerRemove = function(oDOM, sEvent, fCall, capture=false) { - if (!oDOM || !oDOM.removeEventListener || !sEvent || typeof fCall!=="function"){ - //console.log("cant pointerListenerRemove "+oDOM+", "+sEvent+", "+fCall); - return; // -- break -- - } - switch(sEvent){ - //both pointer and move events - case "down": case "up": case "move": case "over": case "out": case "enter": - { - if (LiteGraph.pointerevents_method=="pointer" || LiteGraph.pointerevents_method=="mouse"){ - oDOM.removeEventListener(LiteGraph.pointerevents_method+sEvent, fCall, capture); - } - } - // only pointerevents - case "leave": case "cancel": case "gotpointercapture": case "lostpointercapture": - { - if (LiteGraph.pointerevents_method=="pointer"){ - return oDOM.removeEventListener(LiteGraph.pointerevents_method+sEvent, fCall, capture); - } - } - // not "pointer" || "mouse" - default: - return oDOM.removeEventListener(sEvent, fCall, capture); - } - } + EventCounter.prototype.onExecute = function() { + if(this.properties.doCountExecution) { + this.num += 1; + } + this.setOutputData(1, this.num); + }; - function clamp(v, a, b) { - return a > v ? a : b < v ? b : v; + LiteGraph.registerNodeType("events/counter", EventCounter); + + // Show value inside the debug console + function DelayEvent() { + this.size = [60, 30]; + this.addProperty("time_in_ms", 1000); + this.addInput("event", LiteGraph.ACTION); + this.addOutput("on_time", LiteGraph.EVENT); + + this._pending = []; + } + + DelayEvent.title = "Delay"; + DelayEvent.desc = "Delays one event"; + + DelayEvent.prototype.onAction = function(action, param, options) { + var time = this.properties.time_in_ms; + if (time <= 0) { + this.trigger(null, param, options); + } else { + this._pending.push([time, param]); + } }; - global.clamp = clamp; - if (typeof window != "undefined" && !window["requestAnimationFrame"]) { - window.requestAnimationFrame = - window.webkitRequestAnimationFrame || - window.mozRequestAnimationFrame || - function(callback) { - window.setTimeout(callback, 1000 / 60); - }; + DelayEvent.prototype.onExecute = function(param, options) { + var dt = this.graph.elapsed_time * 1000; // in ms + + if (this.isInputConnected(1)) { + this.properties.time_in_ms = this.getInputData(1); + } + + for (var i = 0; i < this._pending.length; ++i) { + var actionPass = this._pending[i]; + actionPass[0] -= dt; + if (actionPass[0] > 0) { + continue; + } + + // remove + this._pending.splice(i, 1); + --i; + + // trigger + this.trigger(null, actionPass[1], options); + } + }; + + DelayEvent.prototype.onGetInputs = function() { + return [["event", LiteGraph.ACTION], ["time_in_ms", "number"]]; + }; + + LiteGraph.registerNodeType("events/delay", DelayEvent); + + // Show value inside the debug console + function TimerEvent() { + this.addProperty("interval", 1000); + this.addProperty("event", "tick"); + this.addOutput("on_tick", LiteGraph.EVENT); + this.time = 0; + this.last_interval = 1000; + this.triggered = false; + } + + TimerEvent.title = "Timer"; + TimerEvent.desc = "Sends an event every N milliseconds"; + + TimerEvent.prototype.onStart = function() { + this.time = 0; + }; + + TimerEvent.prototype.getTitle = function() { + return "Timer: " + this.last_interval.toString() + "ms"; + }; + + TimerEvent.on_color = "#AAA"; + TimerEvent.off_color = "#222"; + + TimerEvent.prototype.onDrawBackground = function() { + this.boxcolor = this.triggered + ? TimerEvent.on_color + : TimerEvent.off_color; + this.triggered = false; + }; + + TimerEvent.prototype.onExecute = function() { + var dt = this.graph.elapsed_time * 1000; // in ms + + var trigger = this.time == 0; + + this.time += dt; + this.last_interval = Math.max( + 1, + this.getInputOrProperty("interval") | 0, + ); + + if ( + !trigger && + (this.time < this.last_interval || isNaN(this.last_interval)) + ) { + if (this.inputs && this.inputs.length > 1 && this.inputs[1]) { + this.setOutputData(1, false); + } + return; + } + + this.triggered = true; + this.time = this.time % this.last_interval; + this.trigger("on_tick", this.properties.event); + if (this.inputs && this.inputs.length > 1 && this.inputs[1]) { + this.setOutputData(1, true); + } + }; + + TimerEvent.prototype.onGetInputs = function() { + return [["interval", "number"]]; + }; + + TimerEvent.prototype.onGetOutputs = function() { + return [["tick", "boolean"]]; + }; + + LiteGraph.registerNodeType("events/timer", TimerEvent); + + + + function SemaphoreEvent() { + this.addInput("go", LiteGraph.ACTION ); + this.addInput("green", LiteGraph.ACTION ); + this.addInput("red", LiteGraph.ACTION ); + this.addOutput("continue", LiteGraph.EVENT ); + this.addOutput("blocked", LiteGraph.EVENT ); + this.addOutput("is_green", "boolean" ); + this._ready = false; + this.properties = {}; + var that = this; + this.addWidget("button","reset","",function() { + that._ready = false; + }); + } + + SemaphoreEvent.title = "Semaphore Event"; + SemaphoreEvent.desc = "Until both events are not triggered, it doesnt continue."; + + SemaphoreEvent.prototype.onExecute = function() { + this.setOutputData(1,this._ready); + this.boxcolor = this._ready ? "#9F9" : "#FA5"; + } + + SemaphoreEvent.prototype.onAction = function(action, param) { + if( action == "go" ) + this.triggerSlot( this._ready ? 0 : 1 ); + else if( action == "green" ) + this._ready = true; + else if( action == "red" ) + this._ready = false; + }; + + LiteGraph.registerNodeType("events/semaphore", SemaphoreEvent); + + function OnceEvent() { + this.addInput("in", LiteGraph.ACTION ); + this.addInput("reset", LiteGraph.ACTION ); + this.addOutput("out", LiteGraph.EVENT ); + this._once = false; + this.properties = {}; + var that = this; + this.addWidget("button","reset","",function() { + that._once = false; + }); + } + + OnceEvent.title = "Once"; + OnceEvent.desc = "Only passes an event once, then gets locked"; + + OnceEvent.prototype.onAction = function(action, param) { + if( action == "in" && !this._once ) { + this._once = true; + this.triggerSlot( 0, param ); + } else if( action == "reset" ) + this._once = false; + }; + + LiteGraph.registerNodeType("events/once", OnceEvent); + + function DataStore() { + this.addInput("data", 0); + this.addInput("assign", LiteGraph.ACTION); + this.addOutput("data", 0); + this._last_value = null; + this.properties = { data: null, serialize: true }; + var that = this; + this.addWidget("button","store","",function() { + that.properties.data = that._last_value; + }); + } + + DataStore.title = "Data Store"; + DataStore.desc = "Stores data and only changes when event is received"; + + DataStore.prototype.onExecute = function() { + this._last_value = this.getInputData(0); + this.setOutputData(0, this.properties.data ); + } + + DataStore.prototype.onAction = function(action, param, options) { + this.properties.data = this._last_value; + }; + + DataStore.prototype.onSerialize = function(o) { + if(o.data == null) + return; + if(this.properties.serialize == false || (o.data.constructor !== String && o.data.constructor !== Number && o.data.constructor !== Boolean && o.data.constructor !== Array && o.data.constructor !== Object )) + o.data = null; } + + LiteGraph.registerNodeType("basic/data_store", DataStore); + + + })(this); -if (typeof exports != "undefined") { - exports.LiteGraph = this.LiteGraph; - exports.LGraph = this.LGraph; - exports.LLink = this.LLink; - exports.LGraphNode = this.LGraphNode; - exports.LGraphGroup = this.LGraphGroup; - exports.DragAndScale = this.DragAndScale; - exports.LGraphCanvas = this.LGraphCanvas; - exports.ContextMenu = this.ContextMenu; -} +(function(global) { + var LiteGraph = global.LiteGraph; + + function GamepadInput() { + this.addOutput("left_x_axis", "number"); + this.addOutput("left_y_axis", "number"); + this.addOutput("button_pressed", LiteGraph.EVENT); + this.properties = { gamepad_index: 0, threshold: 0.1 }; + + this._left_axis = new Float32Array(2); + this._right_axis = new Float32Array(2); + this._triggers = new Float32Array(2); + this._previous_buttons = new Uint8Array(17); + this._current_buttons = new Uint8Array(17); + } + + GamepadInput.title = "Gamepad"; + GamepadInput.desc = "gets the input of the gamepad"; + + GamepadInput.CENTER = 0; + GamepadInput.LEFT = 1; + GamepadInput.RIGHT = 2; + GamepadInput.UP = 4; + GamepadInput.DOWN = 8; + + GamepadInput.zero = new Float32Array(2); + GamepadInput.buttons = [ + "a", + "b", + "x", + "y", + "lb", + "rb", + "lt", + "rt", + "back", + "start", + "ls", + "rs", + "home", + ]; + + GamepadInput.prototype.onExecute = function() { + // get gamepad + var gamepad = this.getGamepad(); + var threshold = this.properties.threshold || 0.0; + + if (gamepad) { + this._left_axis[0] = + Math.abs(gamepad.xbox.axes["lx"]) > threshold + ? gamepad.xbox.axes["lx"] + : 0; + this._left_axis[1] = + Math.abs(gamepad.xbox.axes["ly"]) > threshold + ? gamepad.xbox.axes["ly"] + : 0; + this._right_axis[0] = + Math.abs(gamepad.xbox.axes["rx"]) > threshold + ? gamepad.xbox.axes["rx"] + : 0; + this._right_axis[1] = + Math.abs(gamepad.xbox.axes["ry"]) > threshold + ? gamepad.xbox.axes["ry"] + : 0; + this._triggers[0] = + Math.abs(gamepad.xbox.axes["ltrigger"]) > threshold + ? gamepad.xbox.axes["ltrigger"] + : 0; + this._triggers[1] = + Math.abs(gamepad.xbox.axes["rtrigger"]) > threshold + ? gamepad.xbox.axes["rtrigger"] + : 0; + } + + if (this.outputs) { + for (var i = 0; i < this.outputs.length; i++) { + var output = this.outputs[i]; + if (!output.links || !output.links.length) { + continue; + } + var v = null; + + if (gamepad) { + switch (output.name) { + case "left_axis": + v = this._left_axis; + break; + case "right_axis": + v = this._right_axis; + break; + case "left_x_axis": + v = this._left_axis[0]; + break; + case "left_y_axis": + v = this._left_axis[1]; + break; + case "right_x_axis": + v = this._right_axis[0]; + break; + case "right_y_axis": + v = this._right_axis[1]; + break; + case "trigger_left": + v = this._triggers[0]; + break; + case "trigger_right": + v = this._triggers[1]; + break; + case "a_button": + v = gamepad.xbox.buttons["a"] ? 1 : 0; + break; + case "b_button": + v = gamepad.xbox.buttons["b"] ? 1 : 0; + break; + case "x_button": + v = gamepad.xbox.buttons["x"] ? 1 : 0; + break; + case "y_button": + v = gamepad.xbox.buttons["y"] ? 1 : 0; + break; + case "lb_button": + v = gamepad.xbox.buttons["lb"] ? 1 : 0; + break; + case "rb_button": + v = gamepad.xbox.buttons["rb"] ? 1 : 0; + break; + case "ls_button": + v = gamepad.xbox.buttons["ls"] ? 1 : 0; + break; + case "rs_button": + v = gamepad.xbox.buttons["rs"] ? 1 : 0; + break; + case "hat_left": + v = gamepad.xbox.hatmap & GamepadInput.LEFT; + break; + case "hat_right": + v = gamepad.xbox.hatmap & GamepadInput.RIGHT; + break; + case "hat_up": + v = gamepad.xbox.hatmap & GamepadInput.UP; + break; + case "hat_down": + v = gamepad.xbox.hatmap & GamepadInput.DOWN; + break; + case "hat": + v = gamepad.xbox.hatmap; + break; + case "start_button": + v = gamepad.xbox.buttons["start"] ? 1 : 0; + break; + case "back_button": + v = gamepad.xbox.buttons["back"] ? 1 : 0; + break; + case "button_pressed": + for ( + var j = 0; + j < this._current_buttons.length; + ++j + ) { + if ( + this._current_buttons[j] && + !this._previous_buttons[j] + ) { + this.triggerSlot( + i, + GamepadInput.buttons[j], + ); + } + } + break; + default: + break; + } + } else { + // if no gamepad is connected, output 0 + switch (output.name) { + case "button_pressed": + break; + case "left_axis": + case "right_axis": + v = GamepadInput.zero; + break; + default: + v = 0; + } + } + this.setOutputData(i, v); + } + } + }; + + GamepadInput.mapping = {a: 0,b: 1,x: 2,y: 3,lb: 4,rb: 5,lt: 6,rt: 7,back: 8,start: 9,ls: 10,rs: 11 }; + GamepadInput.mapping_array = ["a","b","x","y","lb","rb","lt","rt","back","start","ls","rs"]; + + GamepadInput.prototype.getGamepad = function() { + var getGamepads = + navigator.getGamepads || + navigator.webkitGetGamepads || + navigator.mozGetGamepads; + if (!getGamepads) { + return null; + } + var gamepads = getGamepads.call(navigator); + var gamepad = null; + + this._previous_buttons.set(this._current_buttons); + + // pick the first connected + for (var i = this.properties.gamepad_index; i < 4; i++) { + if (!gamepads[i]) { + continue; + } + gamepad = gamepads[i]; + + // xbox controller mapping + var xbox = this.xbox_mapping; + if (!xbox) { + xbox = this.xbox_mapping = { + axes: [], + buttons: {}, + hat: "", + hatmap: GamepadInput.CENTER, + }; + } + + xbox.axes["lx"] = gamepad.axes[0]; + xbox.axes["ly"] = gamepad.axes[1]; + xbox.axes["rx"] = gamepad.axes[2]; + xbox.axes["ry"] = gamepad.axes[3]; + xbox.axes["ltrigger"] = gamepad.buttons[6].value; + xbox.axes["rtrigger"] = gamepad.buttons[7].value; + xbox.hat = ""; + xbox.hatmap = GamepadInput.CENTER; + + for (var j = 0; j < gamepad.buttons.length; j++) { + this._current_buttons[j] = gamepad.buttons[j].pressed; + + if(j < 12) { + xbox.buttons[GamepadInput.mapping_array[j]] = gamepad.buttons[j].pressed; + if(gamepad.buttons[j].was_pressed) + this.trigger( GamepadInput.mapping_array[j] + "_button_event" ); + } else // mapping of XBOX + switch ( j ) { // I use a switch to ensure that a player with another gamepad could play + case 12: + if (gamepad.buttons[j].pressed) { + xbox.hat += "up"; + xbox.hatmap |= GamepadInput.UP; + } + break; + case 13: + if (gamepad.buttons[j].pressed) { + xbox.hat += "down"; + xbox.hatmap |= GamepadInput.DOWN; + } + break; + case 14: + if (gamepad.buttons[j].pressed) { + xbox.hat += "left"; + xbox.hatmap |= GamepadInput.LEFT; + } + break; + case 15: + if (gamepad.buttons[j].pressed) { + xbox.hat += "right"; + xbox.hatmap |= GamepadInput.RIGHT; + } + break; + case 16: + xbox.buttons["home"] = gamepad.buttons[j].pressed; + break; + default: + } + } + gamepad.xbox = xbox; + return gamepad; + } + }; + + GamepadInput.prototype.onDrawBackground = function(ctx) { + if (this.flags.collapsed) { + return; + } + + // render gamepad state? + var la = this._left_axis; + var ra = this._right_axis; + ctx.strokeStyle = "#88A"; + ctx.strokeRect( + (la[0] + 1) * 0.5 * this.size[0] - 4, + (la[1] + 1) * 0.5 * this.size[1] - 4, + 8, + 8, + ); + ctx.strokeStyle = "#8A8"; + ctx.strokeRect( + (ra[0] + 1) * 0.5 * this.size[0] - 4, + (ra[1] + 1) * 0.5 * this.size[1] - 4, + 8, + 8, + ); + var h = this.size[1] / this._current_buttons.length; + ctx.fillStyle = "#AEB"; + for (var i = 0; i < this._current_buttons.length; ++i) { + if (this._current_buttons[i]) { + ctx.fillRect(0, h * i, 6, h); + } + } + }; + + GamepadInput.prototype.onGetOutputs = function() { + return [ + ["left_axis", "vec2"], + ["right_axis", "vec2"], + ["left_x_axis", "number"], + ["left_y_axis", "number"], + ["right_x_axis", "number"], + ["right_y_axis", "number"], + ["trigger_left", "number"], + ["trigger_right", "number"], + ["a_button", "number"], + ["b_button", "number"], + ["x_button", "number"], + ["y_button", "number"], + ["lb_button", "number"], + ["rb_button", "number"], + ["ls_button", "number"], + ["rs_button", "number"], + ["start_button", "number"], + ["back_button", "number"], + ["a_button_event", LiteGraph.EVENT ], + ["b_button_event", LiteGraph.EVENT ], + ["x_button_event", LiteGraph.EVENT ], + ["y_button_event", LiteGraph.EVENT ], + ["lb_button_event", LiteGraph.EVENT ], + ["rb_button_event", LiteGraph.EVENT ], + ["ls_button_event", LiteGraph.EVENT ], + ["rs_button_event", LiteGraph.EVENT ], + ["start_button_event", LiteGraph.EVENT ], + ["back_button_event", LiteGraph.EVENT ], + ["hat_left", "number"], + ["hat_right", "number"], + ["hat_up", "number"], + ["hat_down", "number"], + ["hat", "number"], + ["button_pressed", LiteGraph.EVENT], + ]; + }; + LiteGraph.registerNodeType("input/gamepad", GamepadInput); -//basic nodes -(function(global) { - var LiteGraph = global.LiteGraph; - - //Constant - function Time() { - this.addOutput("in ms", "number"); - this.addOutput("in sec", "number"); - } - - Time.title = "Time"; - Time.desc = "Time"; - - Time.prototype.onExecute = function() { - this.setOutputData(0, this.graph.globaltime * 1000); - this.setOutputData(1, this.graph.globaltime); - }; - - LiteGraph.registerNodeType("basic/time", Time); - - //Subgraph: a node that contains a graph - function Subgraph() { - var that = this; - this.size = [140, 80]; - this.properties = { enabled: true }; - this.enabled = true; - - //create inner graph - this.subgraph = new LiteGraph.LGraph(); - this.subgraph._subgraph_node = this; - this.subgraph._is_subgraph = true; - - this.subgraph.onTrigger = this.onSubgraphTrigger.bind(this); - - //nodes input node added inside - this.subgraph.onInputAdded = this.onSubgraphNewInput.bind(this); - this.subgraph.onInputRenamed = this.onSubgraphRenamedInput.bind(this); - this.subgraph.onInputTypeChanged = this.onSubgraphTypeChangeInput.bind(this); - this.subgraph.onInputRemoved = this.onSubgraphRemovedInput.bind(this); - - this.subgraph.onOutputAdded = this.onSubgraphNewOutput.bind(this); - this.subgraph.onOutputRenamed = this.onSubgraphRenamedOutput.bind(this); - this.subgraph.onOutputTypeChanged = this.onSubgraphTypeChangeOutput.bind(this); - this.subgraph.onOutputRemoved = this.onSubgraphRemovedOutput.bind(this); - } - - Subgraph.title = "Subgraph"; - Subgraph.desc = "Graph inside a node"; - Subgraph.title_color = "#334"; - - Subgraph.prototype.onGetInputs = function() { - return [["enabled", "boolean"]]; - }; - - /* - Subgraph.prototype.onDrawTitle = function(ctx) { - if (this.flags.collapsed) { - return; - } - - ctx.fillStyle = "#555"; - var w = LiteGraph.NODE_TITLE_HEIGHT; - var x = this.size[0] - w; - ctx.fillRect(x, -w, w, w); - ctx.fillStyle = "#333"; - ctx.beginPath(); - ctx.moveTo(x + w * 0.2, -w * 0.6); - ctx.lineTo(x + w * 0.8, -w * 0.6); - ctx.lineTo(x + w * 0.5, -w * 0.3); - ctx.fill(); - }; - */ - - Subgraph.prototype.onDblClick = function(e, pos, graphcanvas) { - var that = this; - setTimeout(function() { - graphcanvas.openSubgraph(that.subgraph); - }, 10); - }; - - /* - Subgraph.prototype.onMouseDown = function(e, pos, graphcanvas) { - if ( - !this.flags.collapsed && - pos[0] > this.size[0] - LiteGraph.NODE_TITLE_HEIGHT && - pos[1] < 0 - ) { - var that = this; - setTimeout(function() { - graphcanvas.openSubgraph(that.subgraph); - }, 10); - } - }; - */ - - Subgraph.prototype.onAction = function(action, param) { - this.subgraph.onAction(action, param); - }; - - Subgraph.prototype.onExecute = function() { - this.enabled = this.getInputOrProperty("enabled"); - if (!this.enabled) { - return; - } - - //send inputs to subgraph global inputs - if (this.inputs) { - for (var i = 0; i < this.inputs.length; i++) { - var input = this.inputs[i]; - var value = this.getInputData(i); - this.subgraph.setInputData(input.name, value); - } - } - - //execute - this.subgraph.runStep(); - - //send subgraph global outputs to outputs - if (this.outputs) { - for (var i = 0; i < this.outputs.length; i++) { - var output = this.outputs[i]; - var value = this.subgraph.getOutputData(output.name); - this.setOutputData(i, value); - } - } - }; - - Subgraph.prototype.sendEventToAllNodes = function(eventname, param, mode) { - if (this.enabled) { - this.subgraph.sendEventToAllNodes(eventname, param, mode); - } - }; - - Subgraph.prototype.onDrawBackground = function (ctx, graphcanvas, canvas, pos) { - if (this.flags.collapsed) - return; - var y = this.size[1] - LiteGraph.NODE_TITLE_HEIGHT + 0.5; - // button - var over = LiteGraph.isInsideRectangle(pos[0], pos[1], this.pos[0], this.pos[1] + y, this.size[0], LiteGraph.NODE_TITLE_HEIGHT); - let overleft = LiteGraph.isInsideRectangle(pos[0], pos[1], this.pos[0], this.pos[1] + y, this.size[0] / 2, LiteGraph.NODE_TITLE_HEIGHT) - ctx.fillStyle = over ? "#555" : "#222"; - ctx.beginPath(); - if (this._shape == LiteGraph.BOX_SHAPE) { - if (overleft) { - ctx.rect(0, y, this.size[0] / 2 + 1, LiteGraph.NODE_TITLE_HEIGHT); - } else { - ctx.rect(this.size[0] / 2, y, this.size[0] / 2 + 1, LiteGraph.NODE_TITLE_HEIGHT); - } - } - else { - if (overleft) { - ctx.roundRect(0, y, this.size[0] / 2 + 1, LiteGraph.NODE_TITLE_HEIGHT, [0,0, 8,8]); - } else { - ctx.roundRect(this.size[0] / 2, y, this.size[0] / 2 + 1, LiteGraph.NODE_TITLE_HEIGHT, [0,0, 8,8]); - } - } - if (over) { - ctx.fill(); - } else { - ctx.fillRect(0, y, this.size[0] + 1, LiteGraph.NODE_TITLE_HEIGHT); - } - // button - ctx.textAlign = "center"; - ctx.font = "24px Arial"; - ctx.fillStyle = over ? "#DDD" : "#999"; - ctx.fillText("+", this.size[0] * 0.25, y + 24); - ctx.fillText("+", this.size[0] * 0.75, y + 24); - } - - // Subgraph.prototype.onMouseDown = function(e, localpos, graphcanvas) - // { - // var y = this.size[1] - LiteGraph.NODE_TITLE_HEIGHT + 0.5; - // if(localpos[1] > y) - // { - // graphcanvas.showSubgraphPropertiesDialog(this); - // } - // } - Subgraph.prototype.onMouseDown = function (e, localpos, graphcanvas) { - var y = this.size[1] - LiteGraph.NODE_TITLE_HEIGHT + 0.5; - console.log(0) - if (localpos[1] > y) { - if (localpos[0] < this.size[0] / 2) { - console.log(1) - graphcanvas.showSubgraphPropertiesDialog(this); - } else { - console.log(2) - graphcanvas.showSubgraphPropertiesDialogRight(this); - } - } - } - Subgraph.prototype.computeSize = function() - { - var num_inputs = this.inputs ? this.inputs.length : 0; - var num_outputs = this.outputs ? this.outputs.length : 0; - return [ 200, Math.max(num_inputs,num_outputs) * LiteGraph.NODE_SLOT_HEIGHT + LiteGraph.NODE_TITLE_HEIGHT ]; - } - - //**** INPUTS *********************************** - Subgraph.prototype.onSubgraphTrigger = function(event, param) { - var slot = this.findOutputSlot(event); - if (slot != -1) { - this.triggerSlot(slot); - } - }; - - Subgraph.prototype.onSubgraphNewInput = function(name, type) { - var slot = this.findInputSlot(name); - if (slot == -1) { - //add input to the node - this.addInput(name, type); - } - }; - - Subgraph.prototype.onSubgraphRenamedInput = function(oldname, name) { - var slot = this.findInputSlot(oldname); - if (slot == -1) { - return; - } - var info = this.getInputInfo(slot); - info.name = name; - }; - - Subgraph.prototype.onSubgraphTypeChangeInput = function(name, type) { - var slot = this.findInputSlot(name); - if (slot == -1) { - return; - } - var info = this.getInputInfo(slot); - info.type = type; - }; - - Subgraph.prototype.onSubgraphRemovedInput = function(name) { - var slot = this.findInputSlot(name); - if (slot == -1) { - return; - } - this.removeInput(slot); - }; - - //**** OUTPUTS *********************************** - Subgraph.prototype.onSubgraphNewOutput = function(name, type) { - var slot = this.findOutputSlot(name); - if (slot == -1) { - this.addOutput(name, type); - } - }; - - Subgraph.prototype.onSubgraphRenamedOutput = function(oldname, name) { - var slot = this.findOutputSlot(oldname); - if (slot == -1) { - return; - } - var info = this.getOutputInfo(slot); - info.name = name; - }; - - Subgraph.prototype.onSubgraphTypeChangeOutput = function(name, type) { - var slot = this.findOutputSlot(name); - if (slot == -1) { - return; - } - var info = this.getOutputInfo(slot); - info.type = type; - }; - - Subgraph.prototype.onSubgraphRemovedOutput = function(name) { - var slot = this.findOutputSlot(name); - if (slot == -1) { - return; - } - this.removeOutput(slot); - }; - // ***************************************************** - - Subgraph.prototype.getExtraMenuOptions = function(graphcanvas) { - var that = this; - return [ - { - content: "Open", - callback: function() { - graphcanvas.openSubgraph(that.subgraph); - } - } - ]; - }; - - Subgraph.prototype.onResize = function(size) { - size[1] += 20; - }; - - Subgraph.prototype.serialize = function() { - var data = LiteGraph.LGraphNode.prototype.serialize.call(this); - data.subgraph = this.subgraph.serialize(); - return data; - }; - //no need to define node.configure, the default method detects node.subgraph and passes the object to node.subgraph.configure() - - Subgraph.prototype.reassignSubgraphUUIDs = function(graph) { - const idMap = { nodeIDs: {}, linkIDs: {} } - - for (const node of graph.nodes) { - const oldID = node.id - const newID = LiteGraph.uuidv4() - node.id = newID - - if (idMap.nodeIDs[oldID] || idMap.nodeIDs[newID]) { - throw new Error(`New/old node UUID wasn't unique in changed map! ${oldID} ${newID}`) - } - - idMap.nodeIDs[oldID] = newID - idMap.nodeIDs[newID] = oldID - } - - for (const link of graph.links) { - const oldID = link[0] - const newID = LiteGraph.uuidv4(); - link[0] = newID - - if (idMap.linkIDs[oldID] || idMap.linkIDs[newID]) { - throw new Error(`New/old link UUID wasn't unique in changed map! ${oldID} ${newID}`) - } - - idMap.linkIDs[oldID] = newID - idMap.linkIDs[newID] = oldID - - const nodeFrom = link[1] - const nodeTo = link[3] - - if (!idMap.nodeIDs[nodeFrom]) { - throw new Error(`Old node UUID not found in mapping! ${nodeFrom}`) - } - - link[1] = idMap.nodeIDs[nodeFrom] - - if (!idMap.nodeIDs[nodeTo]) { - throw new Error(`Old node UUID not found in mapping! ${nodeTo}`) - } - - link[3] = idMap.nodeIDs[nodeTo] - } - - // Reconnect links - for (const node of graph.nodes) { - if (node.inputs) { - for (const input of node.inputs) { - if (input.link) { - input.link = idMap.linkIDs[input.link] - } - } - } - if (node.outputs) { - for (const output of node.outputs) { - if (output.links) { - output.links = output.links.map(l => idMap.linkIDs[l]); - } - } - } - } - - // Recurse! - for (const node of graph.nodes) { - if (node.type === "graph/subgraph") { - const merge = reassignGraphUUIDs(node.subgraph); - idMap.nodeIDs.assign(merge.nodeIDs) - idMap.linkIDs.assign(merge.linkIDs) - } - } - }; - - Subgraph.prototype.clone = function() { - var node = LiteGraph.createNode(this.type); - var data = this.serialize(); - - if (LiteGraph.use_uuids) { - // LGraph.serialize() seems to reuse objects in the original graph. But we - // need to change node IDs here, so clone it first. - const subgraph = LiteGraph.cloneObject(data.subgraph) - - this.reassignSubgraphUUIDs(subgraph); - - data.subgraph = subgraph; - } - - delete data["id"]; - delete data["inputs"]; - delete data["outputs"]; - node.configure(data); - return node; - }; - - Subgraph.prototype.buildFromNodes = function(nodes) - { - //clear all? - //TODO - - //nodes that connect data between parent graph and subgraph - var subgraph_inputs = []; - var subgraph_outputs = []; - - //mark inner nodes - var ids = {}; - var min_x = 0; - var max_x = 0; - for(var i = 0; i < nodes.length; ++i) - { - var node = nodes[i]; - ids[ node.id ] = node; - min_x = Math.min( node.pos[0], min_x ); - max_x = Math.max( node.pos[0], min_x ); - } - - var last_input_y = 0; - var last_output_y = 0; - - for(var i = 0; i < nodes.length; ++i) - { - var node = nodes[i]; - //check inputs - if( node.inputs ) - for(var j = 0; j < node.inputs.length; ++j) - { - var input = node.inputs[j]; - if( !input || !input.link ) - continue; - var link = node.graph.links[ input.link ]; - if(!link) - continue; - if( ids[ link.origin_id ] ) - continue; - //this.addInput(input.name,link.type); - this.subgraph.addInput(input.name,link.type); - /* - var input_node = LiteGraph.createNode("graph/input"); - this.subgraph.add( input_node ); - input_node.pos = [min_x - 200, last_input_y ]; - last_input_y += 100; - */ - } - - //check outputs - if( node.outputs ) - for(var j = 0; j < node.outputs.length; ++j) - { - var output = node.outputs[j]; - if( !output || !output.links || !output.links.length ) - continue; - var is_external = false; - for(var k = 0; k < output.links.length; ++k) - { - var link = node.graph.links[ output.links[k] ]; - if(!link) - continue; - if( ids[ link.target_id ] ) - continue; - is_external = true; - break; - } - if(!is_external) - continue; - //this.addOutput(output.name,output.type); - /* - var output_node = LiteGraph.createNode("graph/output"); - this.subgraph.add( output_node ); - output_node.pos = [max_x + 50, last_output_y ]; - last_output_y += 100; - */ - } - } - - //detect inputs and outputs - //split every connection in two data_connection nodes - //keep track of internal connections - //connect external connections - - //clone nodes inside subgraph and try to reconnect them - - //connect edge subgraph nodes to extarnal connections nodes - } - - LiteGraph.Subgraph = Subgraph; - LiteGraph.registerNodeType("graph/subgraph", Subgraph); - - //Input for a subgraph - function GraphInput() { - this.addOutput("", "number"); - - this.name_in_graph = ""; - this.properties = { - name: "", - type: "number", - value: 0 - }; - - var that = this; - - this.name_widget = this.addWidget( - "text", - "Name", - this.properties.name, - function(v) { - if (!v) { - return; - } - that.setProperty("name",v); - } - ); - this.type_widget = this.addWidget( - "text", - "Type", - this.properties.type, - function(v) { - that.setProperty("type",v); - } - ); - - this.value_widget = this.addWidget( - "number", - "Value", - this.properties.value, - function(v) { - that.setProperty("value",v); - } - ); - - this.widgets_up = true; - this.size = [180, 90]; - } - - GraphInput.title = "Input"; - GraphInput.desc = "Input of the graph"; - - GraphInput.prototype.onConfigure = function() - - { - this.updateType(); - } - - //ensures the type in the node output and the type in the associated graph input are the same - GraphInput.prototype.updateType = function() - { - var type = this.properties.type; - this.type_widget.value = type; - - //update output - if(this.outputs[0].type != type) - { - if (!LiteGraph.isValidConnection(this.outputs[0].type,type)) - this.disconnectOutput(0); - this.outputs[0].type = type; - } - - //update widget - if(type == "number") - { - this.value_widget.type = "number"; - this.value_widget.value = 0; - } - else if(type == "boolean") - { - this.value_widget.type = "toggle"; - this.value_widget.value = true; - } - else if(type == "string") - { - this.value_widget.type = "text"; - this.value_widget.value = ""; - } - else - { - this.value_widget.type = null; - this.value_widget.value = null; - } - this.properties.value = this.value_widget.value; - - //update graph - if (this.graph && this.name_in_graph) { - this.graph.changeInputType(this.name_in_graph, type); - } - } - - //this is executed AFTER the property has changed - GraphInput.prototype.onPropertyChanged = function(name,v) - { - if( name == "name" ) - { - if (v == "" || v == this.name_in_graph || v == "enabled") { - return false; - } - if(this.graph) - { - if (this.name_in_graph) { - //already added - this.graph.renameInput( this.name_in_graph, v ); - } else { - this.graph.addInput( v, this.properties.type ); - } - } //what if not?! - this.name_widget.value = v; - this.name_in_graph = v; - } - else if( name == "type" ) - { - this.updateType(); - } - else if( name == "value" ) - { - } - } - - GraphInput.prototype.getTitle = function() { - if (this.flags.collapsed) { - return this.properties.name; - } - return this.title; - }; - - GraphInput.prototype.onAction = function(action, param) { - if (this.properties.type == LiteGraph.EVENT) { - this.triggerSlot(0, param); - } - }; - - GraphInput.prototype.onExecute = function() { - var name = this.properties.name; - //read from global input - var data = this.graph.inputs[name]; - if (!data) { - this.setOutputData(0, this.properties.value ); - return; - } - - this.setOutputData(0, data.value !== undefined ? data.value : this.properties.value ); - }; - - GraphInput.prototype.onRemoved = function() { - if (this.name_in_graph) { - this.graph.removeInput(this.name_in_graph); - } - }; - - LiteGraph.GraphInput = GraphInput; - LiteGraph.registerNodeType("graph/input", GraphInput); - - //Output for a subgraph - function GraphOutput() { - this.addInput("", ""); - - this.name_in_graph = ""; - this.properties = { name: "", type: "" }; - var that = this; - - // Object.defineProperty(this.properties, "name", { - // get: function() { - // return that.name_in_graph; - // }, - // set: function(v) { - // if (v == "" || v == that.name_in_graph) { - // return; - // } - // if (that.name_in_graph) { - // //already added - // that.graph.renameOutput(that.name_in_graph, v); - // } else { - // that.graph.addOutput(v, that.properties.type); - // } - // that.name_widget.value = v; - // that.name_in_graph = v; - // }, - // enumerable: true - // }); - - // Object.defineProperty(this.properties, "type", { - // get: function() { - // return that.inputs[0].type; - // }, - // set: function(v) { - // if (v == "action" || v == "event") { - // v = LiteGraph.ACTION; - // } - // if (!LiteGraph.isValidConnection(that.inputs[0].type,v)) - // that.disconnectInput(0); - // that.inputs[0].type = v; - // if (that.name_in_graph) { - // //already added - // that.graph.changeOutputType( - // that.name_in_graph, - // that.inputs[0].type - // ); - // } - // that.type_widget.value = v || ""; - // }, - // enumerable: true - // }); - - this.name_widget = this.addWidget("text","Name",this.properties.name,"name"); - this.type_widget = this.addWidget("text","Type",this.properties.type,"type"); - this.widgets_up = true; - this.size = [180, 60]; - } - - GraphOutput.title = "Output"; - GraphOutput.desc = "Output of the graph"; - - GraphOutput.prototype.onPropertyChanged = function (name, v) { - if (name == "name") { - if (v == "" || v == this.name_in_graph || v == "enabled") { - return false; - } - if (this.graph) { - if (this.name_in_graph) { - //already added - this.graph.renameOutput(this.name_in_graph, v); - } else { - this.graph.addOutput(v, this.properties.type); - } - } //what if not?! - this.name_widget.value = v; - this.name_in_graph = v; - } - else if (name == "type") { - this.updateType(); - } - else if (name == "value") { - } - } - - GraphOutput.prototype.updateType = function () { - var type = this.properties.type; - if (this.type_widget) - this.type_widget.value = type; - - //update output - if (this.inputs[0].type != type) { - - if ( type == "action" || type == "event") - type = LiteGraph.EVENT; - if (!LiteGraph.isValidConnection(this.inputs[0].type, type)) - this.disconnectInput(0); - this.inputs[0].type = type; - } - - //update graph - if (this.graph && this.name_in_graph) { - this.graph.changeOutputType(this.name_in_graph, type); - } - } - - - - GraphOutput.prototype.onExecute = function() { - this._value = this.getInputData(0); - this.graph.setOutputData(this.properties.name, this._value); - }; - - GraphOutput.prototype.onAction = function(action, param) { - if (this.properties.type == LiteGraph.ACTION) { - this.graph.trigger( this.properties.name, param ); - } - }; - - GraphOutput.prototype.onRemoved = function() { - if (this.name_in_graph) { - this.graph.removeOutput(this.name_in_graph); - } - }; - - GraphOutput.prototype.getTitle = function() { - if (this.flags.collapsed) { - return this.properties.name; - } - return this.title; - }; - - LiteGraph.GraphOutput = GraphOutput; - LiteGraph.registerNodeType("graph/output", GraphOutput); - - //Constant - function ConstantNumber() { - this.addOutput("value", "number"); - this.addProperty("value", 1.0); - this.widget = this.addWidget("number","value",1,"value"); - this.widgets_up = true; - this.size = [180, 30]; - } - - ConstantNumber.title = "Const Number"; - ConstantNumber.desc = "Constant number"; - - ConstantNumber.prototype.onExecute = function() { - this.setOutputData(0, parseFloat(this.properties["value"])); - }; - - ConstantNumber.prototype.getTitle = function() { - if (this.flags.collapsed) { - return this.properties.value; - } - return this.title; - }; - - ConstantNumber.prototype.setValue = function(v) - { - this.setProperty("value",v); - } - - ConstantNumber.prototype.onDrawBackground = function(ctx) { - //show the current value - this.outputs[0].label = this.properties["value"].toFixed(3); - }; - - LiteGraph.registerNodeType("basic/const", ConstantNumber); - - function ConstantBoolean() { - this.addOutput("bool", "boolean"); - this.addProperty("value", true); - this.widget = this.addWidget("toggle","value",true,"value"); - this.serialize_widgets = true; - this.widgets_up = true; - this.size = [140, 30]; - } - - ConstantBoolean.title = "Const Boolean"; - ConstantBoolean.desc = "Constant boolean"; - ConstantBoolean.prototype.getTitle = ConstantNumber.prototype.getTitle; - - ConstantBoolean.prototype.onExecute = function() { - this.setOutputData(0, this.properties["value"]); - }; - - ConstantBoolean.prototype.setValue = ConstantNumber.prototype.setValue; - - ConstantBoolean.prototype.onGetInputs = function() { - return [["toggle", LiteGraph.ACTION]]; - }; - - ConstantBoolean.prototype.onAction = function(action) - { - this.setValue( !this.properties.value ); - } - - LiteGraph.registerNodeType("basic/boolean", ConstantBoolean); - - function ConstantString() { - this.addOutput("string", "string"); - this.addProperty("value", ""); - this.widget = this.addWidget("text","value","","value"); //link to property value - this.widgets_up = true; - this.size = [180, 30]; - } - - ConstantString.title = "Const String"; - ConstantString.desc = "Constant string"; - - ConstantString.prototype.getTitle = ConstantNumber.prototype.getTitle; - - ConstantString.prototype.onExecute = function() { - this.setOutputData(0, this.properties["value"]); - }; - - ConstantString.prototype.setValue = ConstantNumber.prototype.setValue; - - ConstantString.prototype.onDropFile = function(file) - { - var that = this; - var reader = new FileReader(); - reader.onload = function(e) - { - that.setProperty("value",e.target.result); - } - reader.readAsText(file); - } - - LiteGraph.registerNodeType("basic/string", ConstantString); - - function ConstantObject() { - this.addOutput("obj", "object"); - this.size = [120, 30]; - this._object = {}; - } - - ConstantObject.title = "Const Object"; - ConstantObject.desc = "Constant Object"; - - ConstantObject.prototype.onExecute = function() { - this.setOutputData(0, this._object); - }; - - LiteGraph.registerNodeType( "basic/object", ConstantObject ); - - function ConstantFile() { - this.addInput("url", "string"); - this.addOutput("file", "string"); - this.addProperty("url", ""); - this.addProperty("type", "text"); - this.widget = this.addWidget("text","url","","url"); - this._data = null; - } - - ConstantFile.title = "Const File"; - ConstantFile.desc = "Fetches a file from an url"; - ConstantFile["@type"] = { type: "enum", values: ["text","arraybuffer","blob","json"] }; - - ConstantFile.prototype.onPropertyChanged = function(name, value) { - if (name == "url") - { - if( value == null || value == "") - this._data = null; - else - { - this.fetchFile(value); - } - } - } - - ConstantFile.prototype.onExecute = function() { - var url = this.getInputData(0) || this.properties.url; - if(url && (url != this._url || this._type != this.properties.type)) - this.fetchFile(url); - this.setOutputData(0, this._data ); - }; - - ConstantFile.prototype.setValue = ConstantNumber.prototype.setValue; - - ConstantFile.prototype.fetchFile = function(url) { - var that = this; - if(!url || url.constructor !== String) - { - that._data = null; - that.boxcolor = null; - return; - } - - this._url = url; - this._type = this.properties.type; - if (url.substr(0, 4) == "http" && LiteGraph.proxy) { - url = LiteGraph.proxy + url.substr(url.indexOf(":") + 3); - } - fetch(url) - .then(function(response) { - if(!response.ok) - throw new Error("File not found"); - - if(that.properties.type == "arraybuffer") - return response.arrayBuffer(); - else if(that.properties.type == "text") - return response.text(); - else if(that.properties.type == "json") - return response.json(); - else if(that.properties.type == "blob") - return response.blob(); - }) - .then(function(data) { - that._data = data; - that.boxcolor = "#AEA"; - }) - .catch(function(error) { - that._data = null; - that.boxcolor = "red"; - console.error("error fetching file:",url); - }); - }; - - ConstantFile.prototype.onDropFile = function(file) - { - var that = this; - this._url = file.name; - this._type = this.properties.type; - this.properties.url = file.name; - var reader = new FileReader(); - reader.onload = function(e) - { - that.boxcolor = "#AEA"; - var v = e.target.result; - if( that.properties.type == "json" ) - v = JSON.parse(v); - that._data = v; - } - if(that.properties.type == "arraybuffer") - reader.readAsArrayBuffer(file); - else if(that.properties.type == "text" || that.properties.type == "json") - reader.readAsText(file); - else if(that.properties.type == "blob") - return reader.readAsBinaryString(file); - } - - LiteGraph.registerNodeType("basic/file", ConstantFile); - - -//to store json objects -function JSONParse() { - this.addInput("parse", LiteGraph.ACTION); - this.addInput("json", "string"); - this.addOutput("done", LiteGraph.EVENT); - this.addOutput("object", "object"); - this.widget = this.addWidget("button","parse","",this.parse.bind(this)); - this._str = null; - this._obj = null; -} - -JSONParse.title = "JSON Parse"; -JSONParse.desc = "Parses JSON String into object"; - -JSONParse.prototype.parse = function() -{ - if(!this._str) - return; - - try { - this._str = this.getInputData(1); - this._obj = JSON.parse(this._str); - this.boxcolor = "#AEA"; - this.triggerSlot(0); - } catch (err) { - this.boxcolor = "red"; - } -} - -JSONParse.prototype.onExecute = function() { - this._str = this.getInputData(1); - this.setOutputData(1, this._obj); -}; - -JSONParse.prototype.onAction = function(name) { - if(name == "parse") - this.parse(); -} - -LiteGraph.registerNodeType("basic/jsonparse", JSONParse); - - //to store json objects - function ConstantData() { - this.addOutput("data", "object"); - this.addProperty("value", ""); - this.widget = this.addWidget("text","json","","value"); - this.widgets_up = true; - this.size = [140, 30]; - this._value = null; - } - - ConstantData.title = "Const Data"; - ConstantData.desc = "Constant Data"; - - ConstantData.prototype.onPropertyChanged = function(name, value) { - this.widget.value = value; - if (value == null || value == "") { - return; - } - - try { - this._value = JSON.parse(value); - this.boxcolor = "#AEA"; - } catch (err) { - this.boxcolor = "red"; - } - }; - - ConstantData.prototype.onExecute = function() { - this.setOutputData(0, this._value); - }; - - ConstantData.prototype.setValue = ConstantNumber.prototype.setValue; - - LiteGraph.registerNodeType("basic/data", ConstantData); - - //to store json objects - function ConstantArray() { - this._value = []; - this.addInput("json", ""); - this.addOutput("arrayOut", "array"); - this.addOutput("length", "number"); - this.addProperty("value", "[]"); - this.widget = this.addWidget("text","array",this.properties.value,"value"); - this.widgets_up = true; - this.size = [140, 50]; - } - - ConstantArray.title = "Const Array"; - ConstantArray.desc = "Constant Array"; - - ConstantArray.prototype.onPropertyChanged = function(name, value) { - this.widget.value = value; - if (value == null || value == "") { - return; - } - - try { - if(value[0] != "[") - this._value = JSON.parse("[" + value + "]"); - else - this._value = JSON.parse(value); - this.boxcolor = "#AEA"; - } catch (err) { - this.boxcolor = "red"; - } - }; - - ConstantArray.prototype.onExecute = function() { - var v = this.getInputData(0); - if(v && v.length) //clone - { - if(!this._value) - this._value = new Array(); - this._value.length = v.length; - for(var i = 0; i < v.length; ++i) - this._value[i] = v[i]; - } - this.setOutputData(0, this._value); - this.setOutputData(1, this._value ? ( this._value.length || 0) : 0 ); - }; - - ConstantArray.prototype.setValue = ConstantNumber.prototype.setValue; - - LiteGraph.registerNodeType("basic/array", ConstantArray); - - function SetArray() - { - this.addInput("arr", "array"); - this.addInput("value", ""); - this.addOutput("arr", "array"); - this.properties = { index: 0 }; - this.widget = this.addWidget("number","i",this.properties.index,"index",{precision: 0, step: 10, min: 0}); - } - - SetArray.title = "Set Array"; - SetArray.desc = "Sets index of array"; - - SetArray.prototype.onExecute = function() { - var arr = this.getInputData(0); - if(!arr) - return; - var v = this.getInputData(1); - if(v === undefined ) - return; - if(this.properties.index) - arr[ Math.floor(this.properties.index) ] = v; - this.setOutputData(0,arr); - }; - - LiteGraph.registerNodeType("basic/set_array", SetArray ); - - function ArrayElement() { - this.addInput("array", "array,table,string"); - this.addInput("index", "number"); - this.addOutput("value", ""); - this.addProperty("index",0); - } - - ArrayElement.title = "Array[i]"; - ArrayElement.desc = "Returns an element from an array"; - - ArrayElement.prototype.onExecute = function() { - var array = this.getInputData(0); - var index = this.getInputData(1); - if(index == null) - index = this.properties.index; - if(array == null || index == null ) - return; - this.setOutputData(0, array[Math.floor(Number(index))] ); - }; - - LiteGraph.registerNodeType("basic/array[]", ArrayElement); - - function TableElement() { - this.addInput("table", "table"); - this.addInput("row", "number"); - this.addInput("col", "number"); - this.addOutput("value", ""); - this.addProperty("row",0); - this.addProperty("column",0); - } - - TableElement.title = "Table[row][col]"; - TableElement.desc = "Returns an element from a table"; - - TableElement.prototype.onExecute = function() { - var table = this.getInputData(0); - var row = this.getInputData(1); - var col = this.getInputData(2); - if(row == null) - row = this.properties.row; - if(col == null) - col = this.properties.column; - if(table == null || row == null || col == null) - return; - var row = table[Math.floor(Number(row))]; - if(row) - this.setOutputData(0, row[Math.floor(Number(col))] ); - else - this.setOutputData(0, null ); - }; - - LiteGraph.registerNodeType("basic/table[][]", TableElement); - - function ObjectProperty() { - this.addInput("obj", "object"); - this.addOutput("property", 0); - this.addProperty("value", 0); - this.widget = this.addWidget("text","prop.","",this.setValue.bind(this) ); - this.widgets_up = true; - this.size = [140, 30]; - this._value = null; - } - - ObjectProperty.title = "Object property"; - ObjectProperty.desc = "Outputs the property of an object"; - - ObjectProperty.prototype.setValue = function(v) { - this.properties.value = v; - this.widget.value = v; - }; - - ObjectProperty.prototype.getTitle = function() { - if (this.flags.collapsed) { - return "in." + this.properties.value; - } - return this.title; - }; - - ObjectProperty.prototype.onPropertyChanged = function(name, value) { - this.widget.value = value; - }; - - ObjectProperty.prototype.onExecute = function() { - var data = this.getInputData(0); - if (data != null) { - this.setOutputData(0, data[this.properties.value]); - } - }; - - LiteGraph.registerNodeType("basic/object_property", ObjectProperty); - - function ObjectKeys() { - this.addInput("obj", ""); - this.addOutput("keys", "array"); - this.size = [140, 30]; - } - - ObjectKeys.title = "Object keys"; - ObjectKeys.desc = "Outputs an array with the keys of an object"; - - ObjectKeys.prototype.onExecute = function() { - var data = this.getInputData(0); - if (data != null) { - this.setOutputData(0, Object.keys(data) ); - } - }; - - LiteGraph.registerNodeType("basic/object_keys", ObjectKeys); - - - function SetObject() - { - this.addInput("obj", ""); - this.addInput("value", ""); - this.addOutput("obj", ""); - this.properties = { property: "" }; - this.name_widget = this.addWidget("text","prop.",this.properties.property,"property"); - } - - SetObject.title = "Set Object"; - SetObject.desc = "Adds propertiesrty to object"; - - SetObject.prototype.onExecute = function() { - var obj = this.getInputData(0); - if(!obj) - return; - var v = this.getInputData(1); - if(v === undefined ) - return; - if(this.properties.property) - obj[ this.properties.property ] = v; - this.setOutputData(0,obj); - }; - - LiteGraph.registerNodeType("basic/set_object", SetObject ); - - - function MergeObjects() { - this.addInput("A", "object"); - this.addInput("B", "object"); - this.addOutput("out", "object"); - this._result = {}; - var that = this; - this.addWidget("button","clear","",function(){ - that._result = {}; - }); - this.size = this.computeSize(); - } - - MergeObjects.title = "Merge Objects"; - MergeObjects.desc = "Creates an object copying properties from others"; - - MergeObjects.prototype.onExecute = function() { - var A = this.getInputData(0); - var B = this.getInputData(1); - var C = this._result; - if(A) - for(var i in A) - C[i] = A[i]; - if(B) - for(var i in B) - C[i] = B[i]; - this.setOutputData(0,C); - }; - - LiteGraph.registerNodeType("basic/merge_objects", MergeObjects ); - - //Store as variable - function Variable() { - this.size = [60, 30]; - this.addInput("in"); - this.addOutput("out"); - this.properties = { varname: "myname", container: Variable.LITEGRAPH }; - this.value = null; - } - - Variable.title = "Variable"; - Variable.desc = "store/read variable value"; - - Variable.LITEGRAPH = 0; //between all graphs - Variable.GRAPH = 1; //only inside this graph - Variable.GLOBALSCOPE = 2; //attached to Window - - Variable["@container"] = { type: "enum", values: {"litegraph":Variable.LITEGRAPH, "graph":Variable.GRAPH,"global": Variable.GLOBALSCOPE} }; - - Variable.prototype.onExecute = function() { - var container = this.getContainer(); - - if(this.isInputConnected(0)) - { - this.value = this.getInputData(0); - container[ this.properties.varname ] = this.value; - this.setOutputData(0, this.value ); - return; - } - - this.setOutputData( 0, container[ this.properties.varname ] ); - }; - - Variable.prototype.getContainer = function() - { - switch(this.properties.container) - { - case Variable.GRAPH: - if(this.graph) - return this.graph.vars; - return {}; - break; - case Variable.GLOBALSCOPE: - return global; - break; - case Variable.LITEGRAPH: - default: - return LiteGraph.Globals; - break; - } - } - - Variable.prototype.getTitle = function() { - return this.properties.varname; - }; - - LiteGraph.registerNodeType("basic/variable", Variable); - - function length(v) { - if(v && v.length != null) - return Number(v.length); - return 0; - } - - LiteGraph.wrapFunctionAsNode( - "basic/length", - length, - [""], - "number" - ); - - function length(v) { - if(v && v.length != null) - return Number(v.length); - return 0; - } - - LiteGraph.wrapFunctionAsNode( - "basic/not", - function(a){ return !a; }, - [""], - "boolean" - ); - - function DownloadData() { - this.size = [60, 30]; - this.addInput("data", 0 ); - this.addInput("download", LiteGraph.ACTION ); - this.properties = { filename: "data.json" }; - this.value = null; - var that = this; - this.addWidget("button","Download","", function(v){ - if(!that.value) - return; - that.downloadAsFile(); - }); - } - - DownloadData.title = "Download"; - DownloadData.desc = "Download some data"; - - DownloadData.prototype.downloadAsFile = function() - { - if(this.value == null) - return; - - var str = null; - if(this.value.constructor === String) - str = this.value; - else - str = JSON.stringify(this.value); - - var file = new Blob([str]); - var url = URL.createObjectURL( file ); - var element = document.createElement("a"); - element.setAttribute('href', url); - element.setAttribute('download', this.properties.filename ); - element.style.display = 'none'; - document.body.appendChild(element); - element.click(); - document.body.removeChild(element); - setTimeout( function(){ URL.revokeObjectURL( url ); }, 1000*60 ); //wait one minute to revoke url - } - - DownloadData.prototype.onAction = function(action, param) { - var that = this; - setTimeout( function(){ that.downloadAsFile(); }, 100); //deferred to avoid blocking the renderer with the popup - } - - DownloadData.prototype.onExecute = function() { - if (this.inputs[0]) { - this.value = this.getInputData(0); - } - }; - - DownloadData.prototype.getTitle = function() { - if (this.flags.collapsed) { - return this.properties.filename; - } - return this.title; - }; - - LiteGraph.registerNodeType("basic/download", DownloadData); - - - - //Watch a value in the editor - function Watch() { - this.size = [60, 30]; - this.addInput("value", 0, { label: "" }); - this.value = 0; - } - - Watch.title = "Watch"; - Watch.desc = "Show value of input"; - - Watch.prototype.onExecute = function() { - if (this.inputs[0]) { - this.value = this.getInputData(0); - } - }; - - Watch.prototype.getTitle = function() { - if (this.flags.collapsed) { - return this.inputs[0].label; - } - return this.title; - }; - - Watch.toString = function(o) { - if (o == null) { - return "null"; - } else if (o.constructor === Number) { - return o.toFixed(3); - } else if (o.constructor === Array) { - var str = "["; - for (var i = 0; i < o.length; ++i) { - str += Watch.toString(o[i]) + (i + 1 != o.length ? "," : ""); - } - str += "]"; - return str; - } else { - return String(o); - } - }; - - Watch.prototype.onDrawBackground = function(ctx) { - //show the current value - this.inputs[0].label = Watch.toString(this.value); - }; - - LiteGraph.registerNodeType("basic/watch", Watch); - - //in case one type doesnt match other type but you want to connect them anyway - function Cast() { - this.addInput("in", 0); - this.addOutput("out", 0); - this.size = [40, 30]; - } - - Cast.title = "Cast"; - Cast.desc = "Allows to connect different types"; - - Cast.prototype.onExecute = function() { - this.setOutputData(0, this.getInputData(0)); - }; - - LiteGraph.registerNodeType("basic/cast", Cast); - - //Show value inside the debug console - function Console() { - this.mode = LiteGraph.ON_EVENT; - this.size = [80, 30]; - this.addProperty("msg", ""); - this.addInput("log", LiteGraph.EVENT); - this.addInput("msg", 0); - } - - Console.title = "Console"; - Console.desc = "Show value inside the console"; - - Console.prototype.onAction = function(action, param) { - // param is the action - var msg = this.getInputData(1); //getInputDataByName("msg"); - //if (msg == null || typeof msg == "undefined") return; - if (!msg) msg = this.properties.msg; - if (!msg) msg = "Event: "+param; // msg is undefined if the slot is lost? - if (action == "log") { - console.log(msg); - } else if (action == "warn") { - console.warn(msg); - } else if (action == "error") { - console.error(msg); - } - }; - - Console.prototype.onExecute = function() { - var msg = this.getInputData(1); //getInputDataByName("msg"); - if (!msg) msg = this.properties.msg; - if (msg != null && typeof msg != "undefined") { - this.properties.msg = msg; - console.log(msg); - } - }; - - Console.prototype.onGetInputs = function() { - return [ - ["log", LiteGraph.ACTION], - ["warn", LiteGraph.ACTION], - ["error", LiteGraph.ACTION] - ]; - }; - - LiteGraph.registerNodeType("basic/console", Console); - - //Show value inside the debug console - function Alert() { - this.mode = LiteGraph.ON_EVENT; - this.addProperty("msg", ""); - this.addInput("", LiteGraph.EVENT); - var that = this; - this.widget = this.addWidget("text", "Text", "", "msg"); - this.widgets_up = true; - this.size = [200, 30]; - } - - Alert.title = "Alert"; - Alert.desc = "Show an alert window"; - Alert.color = "#510"; - - Alert.prototype.onConfigure = function(o) { - this.widget.value = o.properties.msg; - }; - - Alert.prototype.onAction = function(action, param) { - var msg = this.properties.msg; - setTimeout(function() { - alert(msg); - }, 10); - }; - - LiteGraph.registerNodeType("basic/alert", Alert); - - //Execites simple code - function NodeScript() { - this.size = [60, 30]; - this.addProperty("onExecute", "return A;"); - this.addInput("A", 0); - this.addInput("B", 0); - this.addOutput("out", 0); - - this._func = null; - this.data = {}; - } - - NodeScript.prototype.onConfigure = function(o) { - if (o.properties.onExecute && LiteGraph.allow_scripts) - this.compileCode(o.properties.onExecute); - else - console.warn("Script not compiled, LiteGraph.allow_scripts is false"); - }; - - NodeScript.title = "Script"; - NodeScript.desc = "executes a code (max 256 characters)"; - - NodeScript.widgets_info = { - onExecute: { type: "code" } - }; - - NodeScript.prototype.onPropertyChanged = function(name, value) { - if (name == "onExecute" && LiteGraph.allow_scripts) - this.compileCode(value); - else - console.warn("Script not compiled, LiteGraph.allow_scripts is false"); - }; - - NodeScript.prototype.compileCode = function(code) { - this._func = null; - if (code.length > 256) { - console.warn("Script too long, max 256 chars"); - } else { - var code_low = code.toLowerCase(); - var forbidden_words = [ - "script", - "body", - "document", - "eval", - "nodescript", - "function" - ]; //bad security solution - for (var i = 0; i < forbidden_words.length; ++i) { - if (code_low.indexOf(forbidden_words[i]) != -1) { - console.warn("invalid script"); - return; - } - } - try { - this._func = new Function("A", "B", "C", "DATA", "node", code); - } catch (err) { - console.error("Error parsing script"); - console.error(err); - } - } - }; - - NodeScript.prototype.onExecute = function() { - if (!this._func) { - return; - } - - try { - var A = this.getInputData(0); - var B = this.getInputData(1); - var C = this.getInputData(2); - this.setOutputData(0, this._func(A, B, C, this.data, this)); - } catch (err) { - console.error("Error in script"); - console.error(err); - } - }; - - NodeScript.prototype.onGetOutputs = function() { - return [["C", ""]]; - }; - - LiteGraph.registerNodeType("basic/script", NodeScript); - - - function GenericCompare() { - this.addInput("A", 0); - this.addInput("B", 0); - this.addOutput("true", "boolean"); - this.addOutput("false", "boolean"); - this.addProperty("A", 1); - this.addProperty("B", 1); - this.addProperty("OP", "==", "enum", { values: GenericCompare.values }); - this.addWidget("combo","Op.",this.properties.OP,{ property: "OP", values: GenericCompare.values } ); - - this.size = [80, 60]; - } - - GenericCompare.values = ["==", "!="]; //[">", "<", "==", "!=", "<=", ">=", "||", "&&" ]; - GenericCompare["@OP"] = { - type: "enum", - title: "operation", - values: GenericCompare.values - }; - - GenericCompare.title = "Compare *"; - GenericCompare.desc = "evaluates condition between A and B"; - - GenericCompare.prototype.getTitle = function() { - return "*A " + this.properties.OP + " *B"; - }; - - GenericCompare.prototype.onExecute = function() { - var A = this.getInputData(0); - if (A === undefined) { - A = this.properties.A; - } else { - this.properties.A = A; - } - - var B = this.getInputData(1); - if (B === undefined) { - B = this.properties.B; - } else { - this.properties.B = B; - } - - var result = false; - if (typeof A == typeof B){ - switch (this.properties.OP) { - case "==": - case "!=": - // traverse both objects.. consider that this is not a true deep check! consider underscore or other library for thath :: _isEqual() - result = true; - switch(typeof A){ - case "object": - var aProps = Object.getOwnPropertyNames(A); - var bProps = Object.getOwnPropertyNames(B); - if (aProps.length != bProps.length){ - result = false; - break; - } - for (var i = 0; i < aProps.length; i++) { - var propName = aProps[i]; - if (A[propName] !== B[propName]) { - result = false; - break; - } - } - break; - default: - result = A == B; - } - if (this.properties.OP == "!=") result = !result; - break; - /*case ">": - result = A > B; - break; - case "<": - result = A < B; - break; - case "<=": - result = A <= B; - break; - case ">=": - result = A >= B; - break; - case "||": - result = A || B; - break; - case "&&": - result = A && B; - break;*/ - } - } - this.setOutputData(0, result); - this.setOutputData(1, !result); - }; - - LiteGraph.registerNodeType("basic/CompareValues", GenericCompare); - -})(this); - -//event related nodes -(function(global) { - var LiteGraph = global.LiteGraph; - - //Show value inside the debug console - function LogEvent() { - this.size = [60, 30]; - this.addInput("event", LiteGraph.ACTION); - } - - LogEvent.title = "Log Event"; - LogEvent.desc = "Log event in console"; - - LogEvent.prototype.onAction = function(action, param, options) { - console.log(action, param); - }; - - LiteGraph.registerNodeType("events/log", LogEvent); - - //convert to Event if the value is true - function TriggerEvent() { - this.size = [60, 30]; - this.addInput("if", ""); - this.addOutput("true", LiteGraph.EVENT); - this.addOutput("change", LiteGraph.EVENT); - this.addOutput("false", LiteGraph.EVENT); - this.properties = { only_on_change: true }; - this.prev = 0; - } - - TriggerEvent.title = "TriggerEvent"; - TriggerEvent.desc = "Triggers event if input evaluates to true"; - - TriggerEvent.prototype.onExecute = function( param, options) { - var v = this.getInputData(0); - var changed = (v != this.prev); - if(this.prev === 0) - changed = false; - var must_resend = (changed && this.properties.only_on_change) || (!changed && !this.properties.only_on_change); - if(v && must_resend ) - this.triggerSlot(0, param, null, options); - if(!v && must_resend) - this.triggerSlot(2, param, null, options); - if(changed) - this.triggerSlot(1, param, null, options); - this.prev = v; - }; - - LiteGraph.registerNodeType("events/trigger", TriggerEvent); - - //Sequence of events - function Sequence() { - var that = this; - this.addInput("", LiteGraph.ACTION); - this.addInput("", LiteGraph.ACTION); - this.addInput("", LiteGraph.ACTION); - this.addOutput("", LiteGraph.EVENT); - this.addOutput("", LiteGraph.EVENT); - this.addOutput("", LiteGraph.EVENT); - this.addWidget("button","+",null,function(){ - that.addInput("", LiteGraph.ACTION); - that.addOutput("", LiteGraph.EVENT); - }); - this.size = [90, 70]; - this.flags = { horizontal: true, render_box: false }; - } - - Sequence.title = "Sequence"; - Sequence.desc = "Triggers a sequence of events when an event arrives"; - - Sequence.prototype.getTitle = function() { - return ""; - }; - - Sequence.prototype.onAction = function(action, param, options) { - if (this.outputs) { - options = options || {}; - for (var i = 0; i < this.outputs.length; ++i) { - var output = this.outputs[i]; - //needs more info about this... - if( options.action_call ) // CREATE A NEW ID FOR THE ACTION - options.action_call = options.action_call + "_seq_" + i; - else - options.action_call = this.id + "_" + (action ? action : "action")+"_seq_"+i+"_"+Math.floor(Math.random()*9999); - this.triggerSlot(i, param, null, options); - } - } - }; - - LiteGraph.registerNodeType("events/sequence", Sequence); - - - //Sequence of events - function WaitAll() { - var that = this; - this.addInput("", LiteGraph.ACTION); - this.addInput("", LiteGraph.ACTION); - this.addOutput("", LiteGraph.EVENT); - this.addWidget("button","+",null,function(){ - that.addInput("", LiteGraph.ACTION); - that.size[0] = 90; - }); - this.size = [90, 70]; - this.ready = []; -} - -WaitAll.title = "WaitAll"; -WaitAll.desc = "Wait until all input events arrive then triggers output"; - -WaitAll.prototype.getTitle = function() { - return ""; -}; - -WaitAll.prototype.onDrawBackground = function(ctx) -{ - if (this.flags.collapsed) { - return; - } - for(var i = 0; i < this.inputs.length; ++i) - { - var y = i * LiteGraph.NODE_SLOT_HEIGHT + 10; - ctx.fillStyle = this.ready[i] ? "#AFB" : "#000"; - ctx.fillRect(20, y, 10, 10); - } -} - -WaitAll.prototype.onAction = function(action, param, options, slot_index) { - if(slot_index == null) - return; - - //check all - this.ready.length = this.outputs.length; - this.ready[slot_index] = true; - for(var i = 0; i < this.ready.length;++i) - if(!this.ready[i]) - return; - //pass - this.reset(); - this.triggerSlot(0); -}; - -WaitAll.prototype.reset = function() -{ - this.ready.length = 0; -} - -LiteGraph.registerNodeType("events/waitAll", WaitAll); - - - //Sequencer for events - function Stepper() { - var that = this; - this.properties = { index: 0 }; - this.addInput("index", "number"); - this.addInput("step", LiteGraph.ACTION); - this.addInput("reset", LiteGraph.ACTION); - this.addOutput("index", "number"); - this.addOutput("", LiteGraph.EVENT); - this.addOutput("", LiteGraph.EVENT); - this.addOutput("", LiteGraph.EVENT,{removable:true}); - this.addWidget("button","+",null,function(){ - that.addOutput("", LiteGraph.EVENT, {removable:true}); - }); - this.size = [120, 120]; - this.flags = { render_box: false }; - } - - Stepper.title = "Stepper"; - Stepper.desc = "Trigger events sequentially when an tick arrives"; - - Stepper.prototype.onDrawBackground = function(ctx) - { - if (this.flags.collapsed) { - return; - } - var index = this.properties.index || 0; - ctx.fillStyle = "#AFB"; - var w = this.size[0]; - var y = (index + 1)* LiteGraph.NODE_SLOT_HEIGHT + 4; - ctx.beginPath(); - ctx.moveTo(w - 30, y); - ctx.lineTo(w - 30, y + LiteGraph.NODE_SLOT_HEIGHT); - ctx.lineTo(w - 15, y + LiteGraph.NODE_SLOT_HEIGHT * 0.5); - ctx.fill(); - } - - Stepper.prototype.onExecute = function() - { - var index = this.getInputData(0); - if(index != null) - { - index = Math.floor(index); - index = clamp( index, 0, this.outputs ? (this.outputs.length - 2) : 0 ); - if( index != this.properties.index ) - { - this.properties.index = index; - this.triggerSlot( index+1 ); - } - } - - this.setOutputData(0, this.properties.index ); - } - - Stepper.prototype.onAction = function(action, param) { - if(action == "reset") - this.properties.index = 0; - else if(action == "step") - { - this.triggerSlot(this.properties.index+1, param); - var n = this.outputs ? this.outputs.length - 1 : 0; - this.properties.index = (this.properties.index + 1) % n; - } - }; - - LiteGraph.registerNodeType("events/stepper", Stepper); - - //Filter events - function FilterEvent() { - this.size = [60, 30]; - this.addInput("event", LiteGraph.ACTION); - this.addOutput("event", LiteGraph.EVENT); - this.properties = { - equal_to: "", - has_property: "", - property_equal_to: "" - }; - } - - FilterEvent.title = "Filter Event"; - FilterEvent.desc = "Blocks events that do not match the filter"; - - FilterEvent.prototype.onAction = function(action, param, options) { - if (param == null) { - return; - } - - if (this.properties.equal_to && this.properties.equal_to != param) { - return; - } - - if (this.properties.has_property) { - var prop = param[this.properties.has_property]; - if (prop == null) { - return; - } - - if ( - this.properties.property_equal_to && - this.properties.property_equal_to != prop - ) { - return; - } - } - - this.triggerSlot(0, param, null, options); - }; - - LiteGraph.registerNodeType("events/filter", FilterEvent); - - - function EventBranch() { - this.addInput("in", LiteGraph.ACTION); - this.addInput("cond", "boolean"); - this.addOutput("true", LiteGraph.EVENT); - this.addOutput("false", LiteGraph.EVENT); - this.size = [120, 60]; - this._value = false; - } - - EventBranch.title = "Branch"; - EventBranch.desc = "If condition is true, outputs triggers true, otherwise false"; - - EventBranch.prototype.onExecute = function() { - this._value = this.getInputData(1); - } - - EventBranch.prototype.onAction = function(action, param, options) { - this._value = this.getInputData(1); - this.triggerSlot(this._value ? 0 : 1, param, null, options); - } - - LiteGraph.registerNodeType("events/branch", EventBranch); - - //Show value inside the debug console - function EventCounter() { - this.addInput("inc", LiteGraph.ACTION); - this.addInput("dec", LiteGraph.ACTION); - this.addInput("reset", LiteGraph.ACTION); - this.addOutput("change", LiteGraph.EVENT); - this.addOutput("num", "number"); - this.addProperty("doCountExecution", false, "boolean", {name: "Count Executions"}); - this.addWidget("toggle","Count Exec.",this.properties.doCountExecution,"doCountExecution"); - this.num = 0; - } - - EventCounter.title = "Counter"; - EventCounter.desc = "Counts events"; - - EventCounter.prototype.getTitle = function() { - if (this.flags.collapsed) { - return String(this.num); - } - return this.title; - }; - - EventCounter.prototype.onAction = function(action, param, options) { - var v = this.num; - if (action == "inc") { - this.num += 1; - } else if (action == "dec") { - this.num -= 1; - } else if (action == "reset") { - this.num = 0; - } - if (this.num != v) { - this.trigger("change", this.num); - } - }; - - EventCounter.prototype.onDrawBackground = function(ctx) { - if (this.flags.collapsed) { - return; - } - ctx.fillStyle = "#AAA"; - ctx.font = "20px Arial"; - ctx.textAlign = "center"; - ctx.fillText(this.num, this.size[0] * 0.5, this.size[1] * 0.5); - }; - - EventCounter.prototype.onExecute = function() { - if(this.properties.doCountExecution){ - this.num += 1; - } - this.setOutputData(1, this.num); - }; - - LiteGraph.registerNodeType("events/counter", EventCounter); - - //Show value inside the debug console - function DelayEvent() { - this.size = [60, 30]; - this.addProperty("time_in_ms", 1000); - this.addInput("event", LiteGraph.ACTION); - this.addOutput("on_time", LiteGraph.EVENT); - - this._pending = []; - } - - DelayEvent.title = "Delay"; - DelayEvent.desc = "Delays one event"; - - DelayEvent.prototype.onAction = function(action, param, options) { - var time = this.properties.time_in_ms; - if (time <= 0) { - this.trigger(null, param, options); - } else { - this._pending.push([time, param]); - } - }; - - DelayEvent.prototype.onExecute = function(param, options) { - var dt = this.graph.elapsed_time * 1000; //in ms - - if (this.isInputConnected(1)) { - this.properties.time_in_ms = this.getInputData(1); - } - - for (var i = 0; i < this._pending.length; ++i) { - var actionPass = this._pending[i]; - actionPass[0] -= dt; - if (actionPass[0] > 0) { - continue; - } - - //remove - this._pending.splice(i, 1); - --i; - - //trigger - this.trigger(null, actionPass[1], options); - } - }; - - DelayEvent.prototype.onGetInputs = function() { - return [["event", LiteGraph.ACTION], ["time_in_ms", "number"]]; - }; - - LiteGraph.registerNodeType("events/delay", DelayEvent); - - //Show value inside the debug console - function TimerEvent() { - this.addProperty("interval", 1000); - this.addProperty("event", "tick"); - this.addOutput("on_tick", LiteGraph.EVENT); - this.time = 0; - this.last_interval = 1000; - this.triggered = false; - } - - TimerEvent.title = "Timer"; - TimerEvent.desc = "Sends an event every N milliseconds"; - - TimerEvent.prototype.onStart = function() { - this.time = 0; - }; - - TimerEvent.prototype.getTitle = function() { - return "Timer: " + this.last_interval.toString() + "ms"; - }; - - TimerEvent.on_color = "#AAA"; - TimerEvent.off_color = "#222"; - - TimerEvent.prototype.onDrawBackground = function() { - this.boxcolor = this.triggered - ? TimerEvent.on_color - : TimerEvent.off_color; - this.triggered = false; - }; - - TimerEvent.prototype.onExecute = function() { - var dt = this.graph.elapsed_time * 1000; //in ms - - var trigger = this.time == 0; - - this.time += dt; - this.last_interval = Math.max( - 1, - this.getInputOrProperty("interval") | 0 - ); - - if ( - !trigger && - (this.time < this.last_interval || isNaN(this.last_interval)) - ) { - if (this.inputs && this.inputs.length > 1 && this.inputs[1]) { - this.setOutputData(1, false); - } - return; - } - - this.triggered = true; - this.time = this.time % this.last_interval; - this.trigger("on_tick", this.properties.event); - if (this.inputs && this.inputs.length > 1 && this.inputs[1]) { - this.setOutputData(1, true); - } - }; - - TimerEvent.prototype.onGetInputs = function() { - return [["interval", "number"]]; - }; - - TimerEvent.prototype.onGetOutputs = function() { - return [["tick", "boolean"]]; - }; - - LiteGraph.registerNodeType("events/timer", TimerEvent); - - - - function SemaphoreEvent() { - this.addInput("go", LiteGraph.ACTION ); - this.addInput("green", LiteGraph.ACTION ); - this.addInput("red", LiteGraph.ACTION ); - this.addOutput("continue", LiteGraph.EVENT ); - this.addOutput("blocked", LiteGraph.EVENT ); - this.addOutput("is_green", "boolean" ); - this._ready = false; - this.properties = {}; - var that = this; - this.addWidget("button","reset","",function(){ - that._ready = false; - }); - } - - SemaphoreEvent.title = "Semaphore Event"; - SemaphoreEvent.desc = "Until both events are not triggered, it doesnt continue."; - - SemaphoreEvent.prototype.onExecute = function() - { - this.setOutputData(1,this._ready); - this.boxcolor = this._ready ? "#9F9" : "#FA5"; - } - - SemaphoreEvent.prototype.onAction = function(action, param) { - if( action == "go" ) - this.triggerSlot( this._ready ? 0 : 1 ); - else if( action == "green" ) - this._ready = true; - else if( action == "red" ) - this._ready = false; - }; - - LiteGraph.registerNodeType("events/semaphore", SemaphoreEvent); - - function OnceEvent() { - this.addInput("in", LiteGraph.ACTION ); - this.addInput("reset", LiteGraph.ACTION ); - this.addOutput("out", LiteGraph.EVENT ); - this._once = false; - this.properties = {}; - var that = this; - this.addWidget("button","reset","",function(){ - that._once = false; - }); - } - - OnceEvent.title = "Once"; - OnceEvent.desc = "Only passes an event once, then gets locked"; - - OnceEvent.prototype.onAction = function(action, param) { - if( action == "in" && !this._once ) - { - this._once = true; - this.triggerSlot( 0, param ); - } - else if( action == "reset" ) - this._once = false; - }; - - LiteGraph.registerNodeType("events/once", OnceEvent); - - function DataStore() { - this.addInput("data", 0); - this.addInput("assign", LiteGraph.ACTION); - this.addOutput("data", 0); - this._last_value = null; - this.properties = { data: null, serialize: true }; - var that = this; - this.addWidget("button","store","",function(){ - that.properties.data = that._last_value; - }); - } - - DataStore.title = "Data Store"; - DataStore.desc = "Stores data and only changes when event is received"; - - DataStore.prototype.onExecute = function() - { - this._last_value = this.getInputData(0); - this.setOutputData(0, this.properties.data ); - } - - DataStore.prototype.onAction = function(action, param, options) { - this.properties.data = this._last_value; - }; - - DataStore.prototype.onSerialize = function(o) - { - if(o.data == null) - return; - if(this.properties.serialize == false || (o.data.constructor !== String && o.data.constructor !== Number && o.data.constructor !== Boolean && o.data.constructor !== Array && o.data.constructor !== Object )) - o.data = null; - } - - LiteGraph.registerNodeType("basic/data_store", DataStore); - - - -})(this); - -(function(global) { - var LiteGraph = global.LiteGraph; - - function GamepadInput() { - this.addOutput("left_x_axis", "number"); - this.addOutput("left_y_axis", "number"); - this.addOutput("button_pressed", LiteGraph.EVENT); - this.properties = { gamepad_index: 0, threshold: 0.1 }; - - this._left_axis = new Float32Array(2); - this._right_axis = new Float32Array(2); - this._triggers = new Float32Array(2); - this._previous_buttons = new Uint8Array(17); - this._current_buttons = new Uint8Array(17); - } - - GamepadInput.title = "Gamepad"; - GamepadInput.desc = "gets the input of the gamepad"; - - GamepadInput.CENTER = 0; - GamepadInput.LEFT = 1; - GamepadInput.RIGHT = 2; - GamepadInput.UP = 4; - GamepadInput.DOWN = 8; - - GamepadInput.zero = new Float32Array(2); - GamepadInput.buttons = [ - "a", - "b", - "x", - "y", - "lb", - "rb", - "lt", - "rt", - "back", - "start", - "ls", - "rs", - "home" - ]; - - GamepadInput.prototype.onExecute = function() { - //get gamepad - var gamepad = this.getGamepad(); - var threshold = this.properties.threshold || 0.0; - - if (gamepad) { - this._left_axis[0] = - Math.abs(gamepad.xbox.axes["lx"]) > threshold - ? gamepad.xbox.axes["lx"] - : 0; - this._left_axis[1] = - Math.abs(gamepad.xbox.axes["ly"]) > threshold - ? gamepad.xbox.axes["ly"] - : 0; - this._right_axis[0] = - Math.abs(gamepad.xbox.axes["rx"]) > threshold - ? gamepad.xbox.axes["rx"] - : 0; - this._right_axis[1] = - Math.abs(gamepad.xbox.axes["ry"]) > threshold - ? gamepad.xbox.axes["ry"] - : 0; - this._triggers[0] = - Math.abs(gamepad.xbox.axes["ltrigger"]) > threshold - ? gamepad.xbox.axes["ltrigger"] - : 0; - this._triggers[1] = - Math.abs(gamepad.xbox.axes["rtrigger"]) > threshold - ? gamepad.xbox.axes["rtrigger"] - : 0; - } - - if (this.outputs) { - for (var i = 0; i < this.outputs.length; i++) { - var output = this.outputs[i]; - if (!output.links || !output.links.length) { - continue; - } - var v = null; - - if (gamepad) { - switch (output.name) { - case "left_axis": - v = this._left_axis; - break; - case "right_axis": - v = this._right_axis; - break; - case "left_x_axis": - v = this._left_axis[0]; - break; - case "left_y_axis": - v = this._left_axis[1]; - break; - case "right_x_axis": - v = this._right_axis[0]; - break; - case "right_y_axis": - v = this._right_axis[1]; - break; - case "trigger_left": - v = this._triggers[0]; - break; - case "trigger_right": - v = this._triggers[1]; - break; - case "a_button": - v = gamepad.xbox.buttons["a"] ? 1 : 0; - break; - case "b_button": - v = gamepad.xbox.buttons["b"] ? 1 : 0; - break; - case "x_button": - v = gamepad.xbox.buttons["x"] ? 1 : 0; - break; - case "y_button": - v = gamepad.xbox.buttons["y"] ? 1 : 0; - break; - case "lb_button": - v = gamepad.xbox.buttons["lb"] ? 1 : 0; - break; - case "rb_button": - v = gamepad.xbox.buttons["rb"] ? 1 : 0; - break; - case "ls_button": - v = gamepad.xbox.buttons["ls"] ? 1 : 0; - break; - case "rs_button": - v = gamepad.xbox.buttons["rs"] ? 1 : 0; - break; - case "hat_left": - v = gamepad.xbox.hatmap & GamepadInput.LEFT; - break; - case "hat_right": - v = gamepad.xbox.hatmap & GamepadInput.RIGHT; - break; - case "hat_up": - v = gamepad.xbox.hatmap & GamepadInput.UP; - break; - case "hat_down": - v = gamepad.xbox.hatmap & GamepadInput.DOWN; - break; - case "hat": - v = gamepad.xbox.hatmap; - break; - case "start_button": - v = gamepad.xbox.buttons["start"] ? 1 : 0; - break; - case "back_button": - v = gamepad.xbox.buttons["back"] ? 1 : 0; - break; - case "button_pressed": - for ( - var j = 0; - j < this._current_buttons.length; - ++j - ) { - if ( - this._current_buttons[j] && - !this._previous_buttons[j] - ) { - this.triggerSlot( - i, - GamepadInput.buttons[j] - ); - } - } - break; - default: - break; - } - } else { - //if no gamepad is connected, output 0 - switch (output.name) { - case "button_pressed": - break; - case "left_axis": - case "right_axis": - v = GamepadInput.zero; - break; - default: - v = 0; - } - } - this.setOutputData(i, v); - } - } - }; - - GamepadInput.mapping = {a:0,b:1,x:2,y:3,lb:4,rb:5,lt:6,rt:7,back:8,start:9,ls:10,rs:11 }; - GamepadInput.mapping_array = ["a","b","x","y","lb","rb","lt","rt","back","start","ls","rs"]; - - GamepadInput.prototype.getGamepad = function() { - var getGamepads = - navigator.getGamepads || - navigator.webkitGetGamepads || - navigator.mozGetGamepads; - if (!getGamepads) { - return null; - } - var gamepads = getGamepads.call(navigator); - var gamepad = null; - - this._previous_buttons.set(this._current_buttons); - - //pick the first connected - for (var i = this.properties.gamepad_index; i < 4; i++) { - if (!gamepads[i]) { - continue; - } - gamepad = gamepads[i]; - - //xbox controller mapping - var xbox = this.xbox_mapping; - if (!xbox) { - xbox = this.xbox_mapping = { - axes: [], - buttons: {}, - hat: "", - hatmap: GamepadInput.CENTER - }; - } - - xbox.axes["lx"] = gamepad.axes[0]; - xbox.axes["ly"] = gamepad.axes[1]; - xbox.axes["rx"] = gamepad.axes[2]; - xbox.axes["ry"] = gamepad.axes[3]; - xbox.axes["ltrigger"] = gamepad.buttons[6].value; - xbox.axes["rtrigger"] = gamepad.buttons[7].value; - xbox.hat = ""; - xbox.hatmap = GamepadInput.CENTER; - - for (var j = 0; j < gamepad.buttons.length; j++) { - this._current_buttons[j] = gamepad.buttons[j].pressed; - - if(j < 12) - { - xbox.buttons[ GamepadInput.mapping_array[j] ] = gamepad.buttons[j].pressed; - if(gamepad.buttons[j].was_pressed) - this.trigger( GamepadInput.mapping_array[j] + "_button_event" ); - } - else //mapping of XBOX - switch ( j ) //I use a switch to ensure that a player with another gamepad could play - { - case 12: - if (gamepad.buttons[j].pressed) { - xbox.hat += "up"; - xbox.hatmap |= GamepadInput.UP; - } - break; - case 13: - if (gamepad.buttons[j].pressed) { - xbox.hat += "down"; - xbox.hatmap |= GamepadInput.DOWN; - } - break; - case 14: - if (gamepad.buttons[j].pressed) { - xbox.hat += "left"; - xbox.hatmap |= GamepadInput.LEFT; - } - break; - case 15: - if (gamepad.buttons[j].pressed) { - xbox.hat += "right"; - xbox.hatmap |= GamepadInput.RIGHT; - } - break; - case 16: - xbox.buttons["home"] = gamepad.buttons[j].pressed; - break; - default: - } - } - gamepad.xbox = xbox; - return gamepad; - } - }; - - GamepadInput.prototype.onDrawBackground = function(ctx) { - if (this.flags.collapsed) { - return; - } - - //render gamepad state? - var la = this._left_axis; - var ra = this._right_axis; - ctx.strokeStyle = "#88A"; - ctx.strokeRect( - (la[0] + 1) * 0.5 * this.size[0] - 4, - (la[1] + 1) * 0.5 * this.size[1] - 4, - 8, - 8 - ); - ctx.strokeStyle = "#8A8"; - ctx.strokeRect( - (ra[0] + 1) * 0.5 * this.size[0] - 4, - (ra[1] + 1) * 0.5 * this.size[1] - 4, - 8, - 8 - ); - var h = this.size[1] / this._current_buttons.length; - ctx.fillStyle = "#AEB"; - for (var i = 0; i < this._current_buttons.length; ++i) { - if (this._current_buttons[i]) { - ctx.fillRect(0, h * i, 6, h); - } - } - }; - - GamepadInput.prototype.onGetOutputs = function() { - return [ - ["left_axis", "vec2"], - ["right_axis", "vec2"], - ["left_x_axis", "number"], - ["left_y_axis", "number"], - ["right_x_axis", "number"], - ["right_y_axis", "number"], - ["trigger_left", "number"], - ["trigger_right", "number"], - ["a_button", "number"], - ["b_button", "number"], - ["x_button", "number"], - ["y_button", "number"], - ["lb_button", "number"], - ["rb_button", "number"], - ["ls_button", "number"], - ["rs_button", "number"], - ["start_button", "number"], - ["back_button", "number"], - ["a_button_event", LiteGraph.EVENT ], - ["b_button_event", LiteGraph.EVENT ], - ["x_button_event", LiteGraph.EVENT ], - ["y_button_event", LiteGraph.EVENT ], - ["lb_button_event", LiteGraph.EVENT ], - ["rb_button_event", LiteGraph.EVENT ], - ["ls_button_event", LiteGraph.EVENT ], - ["rs_button_event", LiteGraph.EVENT ], - ["start_button_event", LiteGraph.EVENT ], - ["back_button_event", LiteGraph.EVENT ], - ["hat_left", "number"], - ["hat_right", "number"], - ["hat_up", "number"], - ["hat_down", "number"], - ["hat", "number"], - ["button_pressed", LiteGraph.EVENT] - ]; - }; - - LiteGraph.registerNodeType("input/gamepad", GamepadInput); - -})(this); +})(this); (function(global) { var LiteGraph = global.LiteGraph; - //Converter + // Converter function Converter() { this.addInput("in", 0); - this.addOutput("out", 0); + this.addOutput("out", 0); this.size = [80, 30]; } @@ -17185,13 +16982,13 @@ LiteGraph.registerNodeType("events/waitAll", WaitAll); ["number", "number"], ["vec2", "vec2"], ["vec3", "vec3"], - ["vec4", "vec4"] + ["vec4", "vec4"], ]; }; LiteGraph.registerNodeType("math/converter", Converter); - //Bypass + // Bypass function Bypass() { this.addInput("in"); this.addOutput("out"); @@ -17268,7 +17065,7 @@ LiteGraph.registerNodeType("events/waitAll", WaitAll); var in_max = this.properties.in_max; var out_min = this.properties.out_min; var out_max = this.properties.out_max; - /* + /* if( in_min > in_max ) { in_min = in_max; @@ -17287,7 +17084,7 @@ LiteGraph.registerNodeType("events/waitAll", WaitAll); }; MathRange.prototype.onDrawBackground = function(ctx) { - //show the current value + // show the current value if (this._last_v) { this.outputs[0].label = this._last_v.toFixed(3); } else { @@ -17300,7 +17097,7 @@ LiteGraph.registerNodeType("events/waitAll", WaitAll); ["in_min", "number"], ["in_max", "number"], ["out_min", "number"], - ["out_max", "number"] + ["out_max", "number"], ]; }; @@ -17335,7 +17132,7 @@ LiteGraph.registerNodeType("events/waitAll", WaitAll); }; MathRand.prototype.onDrawBackground = function(ctx) { - //show the current value + // show the current value this.outputs[0].label = (this._last_v || 0).toFixed(3); }; @@ -17345,7 +17142,7 @@ LiteGraph.registerNodeType("events/waitAll", WaitAll); LiteGraph.registerNodeType("math/rand", MathRand); - //basic continuous noise + // basic continuous noise function MathNoise() { this.addInput("in", "number"); this.addOutput("out", "number"); @@ -17386,22 +17183,21 @@ LiteGraph.registerNodeType("events/waitAll", WaitAll); MathNoise.prototype.onExecute = function() { var f = this.getInputData(0) || 0; - var iterations = this.properties.octaves || 1; - var r = 0; - var amp = 1; - var seed = this.properties.seed || 0; - f += seed; - var speed = this.properties.speed || 1; - var total_amp = 0; - for(var i = 0; i < iterations; ++i) - { - r += MathNoise.getValue(f * (1+i) * speed, this.properties.smooth) * amp; - total_amp += amp; - amp *= this.properties.persistence; - if(amp < 0.001) - break; - } - r /= total_amp; + var iterations = this.properties.octaves || 1; + var r = 0; + var amp = 1; + var seed = this.properties.seed || 0; + f += seed; + var speed = this.properties.speed || 1; + var total_amp = 0; + for(var i = 0; i < iterations; ++i) { + r += MathNoise.getValue(f * (1+i) * speed, this.properties.smooth) * amp; + total_amp += amp; + amp *= this.properties.persistence; + if(amp < 0.001) + break; + } + r /= total_amp; var min = this.properties.min; var max = this.properties.max; this._last_v = r * (max - min) + min; @@ -17409,13 +17205,13 @@ LiteGraph.registerNodeType("events/waitAll", WaitAll); }; MathNoise.prototype.onDrawBackground = function(ctx) { - //show the current value + // show the current value this.outputs[0].label = (this._last_v || 0).toFixed(3); }; LiteGraph.registerNodeType("math/noise", MathNoise); - //generates spikes every random time + // generates spikes every random time function MathSpikes() { this.addOutput("out", "number"); this.addProperty("min_time", 1); @@ -17430,7 +17226,7 @@ LiteGraph.registerNodeType("events/waitAll", WaitAll); MathSpikes.desc = "spike every random time"; MathSpikes.prototype.onExecute = function() { - var dt = this.graph.elapsed_time; //in secs + var dt = this.graph.elapsed_time; // in secs this._remaining_time -= dt; this._blink_time -= dt; @@ -17456,7 +17252,7 @@ LiteGraph.registerNodeType("events/waitAll", WaitAll); LiteGraph.registerNodeType("math/spikes", MathSpikes); - //Math clamp + // Math clamp function MathClamp() { this.addInput("in", "number"); this.addOutput("out", "number"); @@ -17467,7 +17263,7 @@ LiteGraph.registerNodeType("events/waitAll", WaitAll); MathClamp.title = "Clamp"; MathClamp.desc = "Clamp number between min and max"; - //MathClamp.filter = "shader"; + // MathClamp.filter = "shader"; MathClamp.prototype.onExecute = function() { var v = this.getInputData(0); @@ -17494,7 +17290,7 @@ LiteGraph.registerNodeType("events/waitAll", WaitAll); LiteGraph.registerNodeType("math/clamp", MathClamp); - //Math ABS + // Math ABS function MathLerp() { this.properties = { f: 0.5 }; this.addInput("A", "number"); @@ -17532,7 +17328,7 @@ LiteGraph.registerNodeType("events/waitAll", WaitAll); LiteGraph.registerNodeType("math/lerp", MathLerp); - //Math ABS + // Math ABS function MathAbs() { this.addInput("in", "number"); this.addOutput("out", "number"); @@ -17552,7 +17348,7 @@ LiteGraph.registerNodeType("events/waitAll", WaitAll); LiteGraph.registerNodeType("math/abs", MathAbs); - //Math Floor + // Math Floor function MathFloor() { this.addInput("in", "number"); this.addOutput("out", "number"); @@ -17572,7 +17368,7 @@ LiteGraph.registerNodeType("events/waitAll", WaitAll); LiteGraph.registerNodeType("math/floor", MathFloor); - //Math frac + // Math frac function MathFrac() { this.addInput("in", "number"); this.addOutput("out", "number"); @@ -17592,7 +17388,7 @@ LiteGraph.registerNodeType("events/waitAll", WaitAll); LiteGraph.registerNodeType("math/frac", MathFrac); - //Math Floor + // Math Floor function MathSmoothStep() { this.addInput("in", "number"); this.addOutput("out", "number"); @@ -17622,7 +17418,7 @@ LiteGraph.registerNodeType("events/waitAll", WaitAll); LiteGraph.registerNodeType("math/smoothstep", MathSmoothStep); - //Math scale + // Math scale function MathScale() { this.addInput("in", "number", { label: "" }); this.addOutput("out", "number", { label: "" }); @@ -17642,26 +17438,26 @@ LiteGraph.registerNodeType("events/waitAll", WaitAll); LiteGraph.registerNodeType("math/scale", MathScale); - //Gate - function Gate() { - this.addInput("v","boolean"); - this.addInput("A"); - this.addInput("B"); - this.addOutput("out"); - } + // Gate + function Gate() { + this.addInput("v","boolean"); + this.addInput("A"); + this.addInput("B"); + this.addOutput("out"); + } - Gate.title = "Gate"; - Gate.desc = "if v is true, then outputs A, otherwise B"; + Gate.title = "Gate"; + Gate.desc = "if v is true, then outputs A, otherwise B"; - Gate.prototype.onExecute = function() { - var v = this.getInputData(0); - this.setOutputData(0, this.getInputData( v ? 1 : 2 )); - }; + Gate.prototype.onExecute = function() { + var v = this.getInputData(0); + this.setOutputData(0, this.getInputData( v ? 1 : 2 )); + }; - LiteGraph.registerNodeType("math/gate", Gate); + LiteGraph.registerNodeType("math/gate", Gate); - //Math Average + // Math Average function MathAverageFilter() { this.addInput("in", "number"); this.addOutput("out", "number"); @@ -17713,7 +17509,7 @@ LiteGraph.registerNodeType("events/waitAll", WaitAll); LiteGraph.registerNodeType("math/average", MathAverageFilter); - //Math + // Math function MathTendTo() { this.addInput("in", "number"); this.addOutput("out", "number"); @@ -17741,7 +17537,7 @@ LiteGraph.registerNodeType("events/waitAll", WaitAll); LiteGraph.registerNodeType("math/tendTo", MathTendTo); - //Math operation + // Math operation function MathOperation() { this.addInput("A", "number,array,object"); this.addInput("B", "number"); @@ -17749,36 +17545,56 @@ LiteGraph.registerNodeType("events/waitAll", WaitAll); this.addProperty("A", 1); this.addProperty("B", 1); this.addProperty("OP", "+", "enum", { values: MathOperation.values }); - this._func = MathOperation.funcs[this.properties.OP]; - this._result = []; //only used for arrays + this._func = MathOperation.funcs[this.properties.OP]; + this._result = []; // only used for arrays } MathOperation.values = ["+", "-", "*", "/", "%", "^", "max", "min"]; MathOperation.funcs = { - "+": function(A,B) { return A + B; }, - "-": function(A,B) { return A - B; }, - "x": function(A,B) { return A * B; }, - "X": function(A,B) { return A * B; }, - "*": function(A,B) { return A * B; }, - "/": function(A,B) { return A / B; }, - "%": function(A,B) { return A % B; }, - "^": function(A,B) { return Math.pow(A, B); }, - "max": function(A,B) { return Math.max(A, B); }, - "min": function(A,B) { return Math.min(A, B); } - }; - - MathOperation.title = "Operation"; + "+": function(A,B) { + return A + B; + }, + "-": function(A,B) { + return A - B; + }, + "x": function(A,B) { + return A * B; + }, + "X": function(A,B) { + return A * B; + }, + "*": function(A,B) { + return A * B; + }, + "/": function(A,B) { + return A / B; + }, + "%": function(A,B) { + return A % B; + }, + "^": function(A,B) { + return Math.pow(A, B); + }, + "max": function(A,B) { + return Math.max(A, B); + }, + "min": function(A,B) { + return Math.min(A, B); + }, + }; + + MathOperation.title = "Operation"; MathOperation.desc = "Easy math operators"; MathOperation["@OP"] = { type: "enum", title: "operation", - values: MathOperation.values + values: MathOperation.values, }; MathOperation.size = [100, 60]; MathOperation.prototype.getTitle = function() { - if(this.properties.OP == "max" || this.properties.OP == "min") - return this.properties.OP + "(A,B)"; + if(this.properties.OP == "max" || this.properties.OP == "min") + return this.properties.OP + "(A,B)"; return "A " + this.properties.OP + " B"; }; @@ -17789,24 +17605,24 @@ LiteGraph.registerNodeType("events/waitAll", WaitAll); this.properties["value"] = v; }; - MathOperation.prototype.onPropertyChanged = function(name, value) - { - if (name != "OP") - return; + MathOperation.prototype.onPropertyChanged = function(name, value) { + if (name != "OP") + return; this._func = MathOperation.funcs[this.properties.OP]; - if(!this._func) - { + if(!this._func) { console.warn("Unknown operation: " + this.properties.OP); - this._func = function(A) { return A; }; + this._func = function(A) { + return A; + }; } - } + } MathOperation.prototype.onExecute = function() { var A = this.getInputData(0); var B = this.getInputData(1); if ( A != null ) { - if( A.constructor === Number ) - this.properties["A"] = A; + if( A.constructor === Number ) + this.properties["A"] = A; } else { A = this.properties["A"]; } @@ -17819,26 +17635,21 @@ LiteGraph.registerNodeType("events/waitAll", WaitAll); var func = MathOperation.funcs[this.properties.OP]; - var result; - if(A.constructor === Number) - { - result = 0; - result = func(A,B); - } - else if(A.constructor === Array) - { - result = this._result; - result.length = A.length; - for(var i = 0; i < A.length; ++i) - result[i] = func(A[i],B); - } - else - { - result = {}; - for(var i in A) - result[i] = func(A[i],B); - } - this.setOutputData(0, result); + var result; + if(A.constructor === Number) { + result = 0; + result = func(A,B); + } else if(A.constructor === Array) { + result = this._result; + result.length = A.length; + for(var i = 0; i < A.length; ++i) + result[i] = func(A[i],B); + } else { + result = {}; + for(var i in A) + result[i] = func(A[i],B); + } + this.setOutputData(0, result); }; MathOperation.prototype.onDrawBackground = function(ctx) { @@ -17852,7 +17663,7 @@ LiteGraph.registerNodeType("events/waitAll", WaitAll); ctx.fillText( this.properties.OP, this.size[0] * 0.5, - (this.size[1] + LiteGraph.NODE_TITLE_HEIGHT) * 0.5 + (this.size[1] + LiteGraph.NODE_TITLE_HEIGHT) * 0.5, ); ctx.textAlign = "left"; }; @@ -17860,17 +17671,17 @@ LiteGraph.registerNodeType("events/waitAll", WaitAll); LiteGraph.registerNodeType("math/operation", MathOperation); LiteGraph.registerSearchboxExtra("math/operation", "MAX", { - properties: {OP:"max"}, - title: "MAX()" + properties: {OP: "max"}, + title: "MAX()", }); LiteGraph.registerSearchboxExtra("math/operation", "MIN", { - properties: {OP:"min"}, - title: "MIN()" + properties: {OP: "min"}, + title: "MIN()", }); - //Math compare + // Math compare function MathCompare() { this.addInput("A", "number"); this.addInput("B", "number"); @@ -17935,7 +17746,7 @@ LiteGraph.registerNodeType("events/waitAll", WaitAll); ["A>B", "boolean"], ["A=B", "boolean"], - ["A<=B", "boolean"] + ["A<=B", "boolean"], ]; }; @@ -17943,27 +17754,27 @@ LiteGraph.registerNodeType("events/waitAll", WaitAll); LiteGraph.registerSearchboxExtra("math/compare", "==", { outputs: [["A==B", "boolean"]], - title: "A==B" + title: "A==B", }); LiteGraph.registerSearchboxExtra("math/compare", "!=", { outputs: [["A!=B", "boolean"]], - title: "A!=B" + title: "A!=B", }); LiteGraph.registerSearchboxExtra("math/compare", ">", { outputs: [["A>B", "boolean"]], - title: "A>B" + title: "A>B", }); LiteGraph.registerSearchboxExtra("math/compare", "<", { outputs: [["A=", { outputs: [["A>=B", "boolean"]], - title: "A>=B" + title: "A>=B", }); LiteGraph.registerSearchboxExtra("math/compare", "<=", { outputs: [["A<=B", "boolean"]], - title: "A<=B" + title: "A<=B", }); function MathCondition() { @@ -17974,7 +17785,7 @@ LiteGraph.registerNodeType("events/waitAll", WaitAll); this.addProperty("A", 1); this.addProperty("B", 1); this.addProperty("OP", ">", "enum", { values: MathCondition.values }); - this.addWidget("combo","Cond.",this.properties.OP,{ property: "OP", values: MathCondition.values } ); + this.addWidget("combo","Cond.",this.properties.OP,{ property: "OP", values: MathCondition.values } ); this.size = [80, 60]; } @@ -17983,7 +17794,7 @@ LiteGraph.registerNodeType("events/waitAll", WaitAll); MathCondition["@OP"] = { type: "enum", title: "operation", - values: MathCondition.values + values: MathCondition.values, }; MathCondition.title = "Condition"; @@ -18058,17 +17869,14 @@ LiteGraph.registerNodeType("events/waitAll", WaitAll); var V = this.getInputData(0); var cond = this.getInputData(1); - if(cond) - { - this.setOutputData(0, V); - this.setOutputData(1, null); - } - else - { - this.setOutputData(0, null); - this.setOutputData(1, V); - } - } + if(cond) { + this.setOutputData(0, V); + this.setOutputData(1, null); + } else { + this.setOutputData(0, null); + this.setOutputData(1, V); + } + } LiteGraph.registerNodeType("math/branch", MathBranch); @@ -18099,7 +17907,7 @@ LiteGraph.registerNodeType("events/waitAll", WaitAll); LiteGraph.registerNodeType("math/accumulate", MathAccumulate); - //Math Trigonometry + // Math Trigonometry function MathTrigonometry() { this.addInput("v", "number"); this.addOutput("sin", "number"); @@ -18111,7 +17919,7 @@ LiteGraph.registerNodeType("events/waitAll", WaitAll); MathTrigonometry.title = "Trigonometry"; MathTrigonometry.desc = "Sin Cos Tan"; - //MathTrigonometry.filter = "shader"; + // MathTrigonometry.filter = "shader"; MathTrigonometry.prototype.onExecute = function() { var v = this.getInputData(0); @@ -18167,7 +17975,7 @@ LiteGraph.registerNodeType("events/waitAll", WaitAll); ["tan", "number"], ["asin", "number"], ["acos", "number"], - ["atan", "number"] + ["atan", "number"], ]; }; @@ -18175,18 +17983,18 @@ LiteGraph.registerNodeType("events/waitAll", WaitAll); LiteGraph.registerSearchboxExtra("math/trigonometry", "SIN()", { outputs: [["sin", "number"]], - title: "SIN()" + title: "SIN()", }); LiteGraph.registerSearchboxExtra("math/trigonometry", "COS()", { outputs: [["cos", "number"]], - title: "COS()" + title: "COS()", }); LiteGraph.registerSearchboxExtra("math/trigonometry", "TAN()", { outputs: [["tan", "number"]], - title: "TAN()" + title: "TAN()", }); - //math library for safe math operations without eval + // math library for safe math operations without eval function MathFormula() { this.addInput("x", "number"); this.addInput("y", "number"); @@ -18198,7 +18006,7 @@ LiteGraph.registerNodeType("events/waitAll", WaitAll); this.properties.formula, function(v, canvas, node) { node.properties.formula = v; - } + }, ); this.addWidget("toggle", "allow", LiteGraph.allow_scripts, function(v) { LiteGraph.allow_scripts = v; @@ -18244,7 +18052,7 @@ LiteGraph.registerNodeType("events/waitAll", WaitAll); "x", "y", "TIME", - "return " + this.properties.formula + "return " + this.properties.formula, ); this._func_code = this.properties.formula; } @@ -18406,7 +18214,7 @@ LiteGraph.registerNodeType("events/waitAll", WaitAll); ["x", "number"], ["y", "number"], ["z", "number"], - ["w", "number"] + ["w", "number"], ]); this.addOutput("vec4", "vec4"); this.properties = { x: 0, y: 0, z: 0, w: 0 }; @@ -18447,156 +18255,152 @@ LiteGraph.registerNodeType("events/waitAll", WaitAll); })(this); -//basic nodes -(function(global) { - var LiteGraph = global.LiteGraph; - - function toString(a) { - if(a && a.constructor === Object) - { - try - { - return JSON.stringify(a); - } - catch (err) - { - return String(a); - } - } - return String(a); - } - - LiteGraph.wrapFunctionAsNode("string/toString", toString, [""], "string"); - - function compare(a, b) { - return a == b; - } - - LiteGraph.wrapFunctionAsNode( - "string/compare", - compare, - ["string", "string"], - "boolean" - ); - - function concatenate(a, b) { - if (a === undefined) { - return b; - } - if (b === undefined) { - return a; - } - return a + b; - } - - LiteGraph.wrapFunctionAsNode( - "string/concatenate", - concatenate, - ["string", "string"], - "string" - ); - - function contains(a, b) { - if (a === undefined || b === undefined) { - return false; - } - return a.indexOf(b) != -1; - } - - LiteGraph.wrapFunctionAsNode( - "string/contains", - contains, - ["string", "string"], - "boolean" - ); - - function toUpperCase(a) { - if (a != null && a.constructor === String) { - return a.toUpperCase(); - } - return a; - } - - LiteGraph.wrapFunctionAsNode( - "string/toUpperCase", - toUpperCase, - ["string"], - "string" - ); - - function split(str, separator) { - if(separator == null) - separator = this.properties.separator; - if (str == null ) - return []; - if( str.constructor === String ) - return str.split(separator || " "); - else if( str.constructor === Array ) - { - var r = []; - for(var i = 0; i < str.length; ++i){ - if (typeof str[i] == "string") - r[i] = str[i].split(separator || " "); - } - return r; - } - return null; - } - - LiteGraph.wrapFunctionAsNode( - "string/split", - split, - ["string,array", "string"], - "array", - { separator: "," } - ); - - function toFixed(a) { - if (a != null && a.constructor === Number) { - return a.toFixed(this.properties.precision); - } - return a; - } - - LiteGraph.wrapFunctionAsNode( - "string/toFixed", - toFixed, - ["number"], - "string", - { precision: 0 } - ); - - - function StringToTable() { - this.addInput("", "string"); - this.addOutput("table", "table"); - this.addOutput("rows", "number"); - this.addProperty("value", ""); - this.addProperty("separator", ","); - this._table = null; - } - - StringToTable.title = "toTable"; - StringToTable.desc = "Splits a string to table"; - - StringToTable.prototype.onExecute = function() { - var input = this.getInputData(0); - if(!input) - return; - var separator = this.properties.separator || ","; - if(input != this._str || separator != this._last_separator ) - { - this._last_separator = separator; - this._str = input; - this._table = input.split("\n").map(function(a){ return a.trim().split(separator)}); - } - this.setOutputData(0, this._table ); - this.setOutputData(1, this._table ? this._table.length : 0 ); - }; - - LiteGraph.registerNodeType("string/toTable", StringToTable); - -})(this); +// basic nodes +(function(global) { + var LiteGraph = global.LiteGraph; + + function toString(a) { + if(a && a.constructor === Object) { + try { + return JSON.stringify(a); + } catch (err) { + return String(a); + } + } + return String(a); + } + + LiteGraph.wrapFunctionAsNode("string/toString", toString, [""], "string"); + + function compare(a, b) { + return a == b; + } + + LiteGraph.wrapFunctionAsNode( + "string/compare", + compare, + ["string", "string"], + "boolean", + ); + + function concatenate(a, b) { + if (a === undefined) { + return b; + } + if (b === undefined) { + return a; + } + return a + b; + } + + LiteGraph.wrapFunctionAsNode( + "string/concatenate", + concatenate, + ["string", "string"], + "string", + ); + + function contains(a, b) { + if (a === undefined || b === undefined) { + return false; + } + return a.indexOf(b) != -1; + } + + LiteGraph.wrapFunctionAsNode( + "string/contains", + contains, + ["string", "string"], + "boolean", + ); + + function toUpperCase(a) { + if (a != null && a.constructor === String) { + return a.toUpperCase(); + } + return a; + } + + LiteGraph.wrapFunctionAsNode( + "string/toUpperCase", + toUpperCase, + ["string"], + "string", + ); + + function split(str, separator) { + if(separator == null) + separator = this.properties.separator; + if (str == null ) + return []; + if( str.constructor === String ) + return str.split(separator || " "); + else if( str.constructor === Array ) { + var r = []; + for(var i = 0; i < str.length; ++i) { + if (typeof str[i] == "string") + r[i] = str[i].split(separator || " "); + } + return r; + } + return null; + } + + LiteGraph.wrapFunctionAsNode( + "string/split", + split, + ["string,array", "string"], + "array", + { separator: "," }, + ); + + function toFixed(a) { + if (a != null && a.constructor === Number) { + return a.toFixed(this.properties.precision); + } + return a; + } + + LiteGraph.wrapFunctionAsNode( + "string/toFixed", + toFixed, + ["number"], + "string", + { precision: 0 }, + ); + + + function StringToTable() { + this.addInput("", "string"); + this.addOutput("table", "table"); + this.addOutput("rows", "number"); + this.addProperty("value", ""); + this.addProperty("separator", ","); + this._table = null; + } + + StringToTable.title = "toTable"; + StringToTable.desc = "Splits a string to table"; + + StringToTable.prototype.onExecute = function() { + var input = this.getInputData(0); + if(!input) + return; + var separator = this.properties.separator || ","; + if(input != this._str || separator != this._last_separator ) { + this._last_separator = separator; + this._str = input; + this._table = input.split("\n").map(function(a) { + return a.trim().split(separator) + }); + } + this.setOutputData(0, this._table ); + this.setOutputData(1, this._table ? this._table.length : 0 ); + }; + + LiteGraph.registerNodeType("string/toTable", StringToTable); + +})(this); (function(global) { var LiteGraph = global.LiteGraph; @@ -18646,9 +18450,7 @@ LiteGraph.registerNodeType("events/waitAll", WaitAll); LiteGraph.registerNodeType("logic/selector", Selector); function Sequence() { - this.properties = { - sequence: "A,B,C" - }; + this.properties = {sequence: "A,B,C"}; this.addInput("index", "number"); this.addInput("seq"); this.addOutput("out"); @@ -18682,9 +18484,9 @@ LiteGraph.registerNodeType("events/waitAll", WaitAll); }; LiteGraph.registerNodeType("logic/sequence", Sequence); - - - function logicAnd(){ + + + function logicAnd() { this.properties = { }; this.addInput("a", "boolean"); this.addInput("b", "boolean"); @@ -18694,8 +18496,8 @@ LiteGraph.registerNodeType("events/waitAll", WaitAll); logicAnd.desc = "Return true if all inputs are true"; logicAnd.prototype.onExecute = function() { var ret = true; - for (var inX in this.inputs){ - if (!this.getInputData(inX)){ + for (var inX in this.inputs) { + if (!this.getInputData(inX)) { var ret = false; break; } @@ -18704,13 +18506,13 @@ LiteGraph.registerNodeType("events/waitAll", WaitAll); }; logicAnd.prototype.onGetInputs = function() { return [ - ["and", "boolean"] + ["and", "boolean"], ]; }; LiteGraph.registerNodeType("logic/AND", logicAnd); - - - function logicOr(){ + + + function logicOr() { this.properties = { }; this.addInput("a", "boolean"); this.addInput("b", "boolean"); @@ -18720,8 +18522,8 @@ LiteGraph.registerNodeType("events/waitAll", WaitAll); logicOr.desc = "Return true if at least one input is true"; logicOr.prototype.onExecute = function() { var ret = false; - for (var inX in this.inputs){ - if (this.getInputData(inX)){ + for (var inX in this.inputs) { + if (this.getInputData(inX)) { ret = true; break; } @@ -18730,13 +18532,13 @@ LiteGraph.registerNodeType("events/waitAll", WaitAll); }; logicOr.prototype.onGetInputs = function() { return [ - ["or", "boolean"] + ["or", "boolean"], ]; }; LiteGraph.registerNodeType("logic/OR", logicOr); - - - function logicNot(){ + + + function logicNot() { this.properties = { }; this.addInput("in", "boolean"); this.addOutput("out", "boolean"); @@ -18748,9 +18550,9 @@ LiteGraph.registerNodeType("events/waitAll", WaitAll); this.setOutputData(0, ret); }; LiteGraph.registerNodeType("logic/NOT", logicNot); - - - function logicCompare(){ + + + function logicCompare() { this.properties = { }; this.addInput("a", "boolean"); this.addInput("b", "boolean"); @@ -18761,10 +18563,10 @@ LiteGraph.registerNodeType("events/waitAll", WaitAll); logicCompare.prototype.onExecute = function() { var last = null; var ret = true; - for (var inX in this.inputs){ + for (var inX in this.inputs) { if (last === null) last = this.getInputData(inX); else - if (last != this.getInputData(inX)){ + if (last != this.getInputData(inX)) { ret = false; break; } @@ -18773,13 +18575,13 @@ LiteGraph.registerNodeType("events/waitAll", WaitAll); }; logicCompare.prototype.onGetInputs = function() { return [ - ["bool", "boolean"] + ["bool", "boolean"], ]; }; LiteGraph.registerNodeType("logic/CompareBool", logicCompare); - - - function logicBranch(){ + + + function logicBranch() { this.properties = { }; this.addInput("onTrigger", LiteGraph.ACTION); this.addInput("condition", "boolean"); @@ -18791,7 +18593,7 @@ LiteGraph.registerNodeType("events/waitAll", WaitAll); logicBranch.desc = "Branch execution on condition"; logicBranch.prototype.onExecute = function(param, options) { var condtition = this.getInputData(1); - if (condtition){ + if (condtition) { this.triggerSlot(0); }else{ this.triggerSlot(1); @@ -18800,432 +18602,414 @@ LiteGraph.registerNodeType("events/waitAll", WaitAll); LiteGraph.registerNodeType("logic/IF", logicBranch); })(this); -//event related nodes -(function(global) { - var LiteGraph = global.LiteGraph; - - function LGWebSocket() { - this.size = [60, 20]; - this.addInput("send", LiteGraph.ACTION); - this.addOutput("received", LiteGraph.EVENT); - this.addInput("in", 0); - this.addOutput("out", 0); - this.properties = { - url: "", - room: "lgraph", //allows to filter messages, - only_send_changes: true - }; - this._ws = null; - this._last_sent_data = []; - this._last_received_data = []; - } - - LGWebSocket.title = "WebSocket"; - LGWebSocket.desc = "Send data through a websocket"; - - LGWebSocket.prototype.onPropertyChanged = function(name, value) { - if (name == "url") { - this.connectSocket(); - } - }; - - LGWebSocket.prototype.onExecute = function() { - if (!this._ws && this.properties.url) { - this.connectSocket(); - } - - if (!this._ws || this._ws.readyState != WebSocket.OPEN) { - return; - } - - var room = this.properties.room; - var only_changes = this.properties.only_send_changes; - - for (var i = 1; i < this.inputs.length; ++i) { - var data = this.getInputData(i); - if (data == null) { - continue; - } - var json; - try { - json = JSON.stringify({ - type: 0, - room: room, - channel: i, - data: data - }); - } catch (err) { - continue; - } - if (only_changes && this._last_sent_data[i] == json) { - continue; - } - - this._last_sent_data[i] = json; - this._ws.send(json); - } - - for (var i = 1; i < this.outputs.length; ++i) { - this.setOutputData(i, this._last_received_data[i]); - } - - if (this.boxcolor == "#AFA") { - this.boxcolor = "#6C6"; - } - }; - - LGWebSocket.prototype.connectSocket = function() { - var that = this; - var url = this.properties.url; - if (url.substr(0, 2) != "ws") { - url = "ws://" + url; - } - this._ws = new WebSocket(url); - this._ws.onopen = function() { - console.log("ready"); - that.boxcolor = "#6C6"; - }; - this._ws.onmessage = function(e) { - that.boxcolor = "#AFA"; - var data = JSON.parse(e.data); - if (data.room && data.room != that.properties.room) { - return; - } - if (data.type == 1) { - if ( - data.data.object_class && - LiteGraph[data.data.object_class] - ) { - var obj = null; - try { - obj = new LiteGraph[data.data.object_class](data.data); - that.triggerSlot(0, obj); - } catch (err) { - return; - } - } else { - that.triggerSlot(0, data.data); - } - } else { - that._last_received_data[data.channel || 0] = data.data; - } - }; - this._ws.onerror = function(e) { - console.log("couldnt connect to websocket"); - that.boxcolor = "#E88"; - }; - this._ws.onclose = function(e) { - console.log("connection closed"); - that.boxcolor = "#000"; - }; - }; - - LGWebSocket.prototype.send = function(data) { - if (!this._ws || this._ws.readyState != WebSocket.OPEN) { - return; - } - this._ws.send(JSON.stringify({ type: 1, msg: data })); - }; - - LGWebSocket.prototype.onAction = function(action, param) { - if (!this._ws || this._ws.readyState != WebSocket.OPEN) { - return; - } - this._ws.send({ - type: 1, - room: this.properties.room, - action: action, - data: param - }); - }; - - LGWebSocket.prototype.onGetInputs = function() { - return [["in", 0]]; - }; - - LGWebSocket.prototype.onGetOutputs = function() { - return [["out", 0]]; - }; - - LiteGraph.registerNodeType("network/websocket", LGWebSocket); - - //It is like a websocket but using the SillyServer.js server that bounces packets back to all clients connected: - //For more information: https://github.com/jagenjo/SillyServer.js - - function LGSillyClient() { - //this.size = [60,20]; - this.room_widget = this.addWidget( - "text", - "Room", - "lgraph", - this.setRoom.bind(this) - ); - this.addWidget( - "button", - "Reconnect", - null, - this.connectSocket.bind(this) - ); - - this.addInput("send", LiteGraph.ACTION); - this.addOutput("received", LiteGraph.EVENT); - this.addInput("in", 0); - this.addOutput("out", 0); - this.properties = { - url: "tamats.com:55000", - room: "lgraph", - only_send_changes: true - }; - - this._server = null; - this.connectSocket(); - this._last_sent_data = []; - this._last_received_data = []; - - if(typeof(SillyClient) == "undefined") - console.warn("remember to add SillyClient.js to your project: https://tamats.com/projects/sillyserver/src/sillyclient.js"); - } - - LGSillyClient.title = "SillyClient"; - LGSillyClient.desc = "Connects to SillyServer to broadcast messages"; - - LGSillyClient.prototype.onPropertyChanged = function(name, value) { - if (name == "room") { - this.room_widget.value = value; - } - this.connectSocket(); - }; - - LGSillyClient.prototype.setRoom = function(room_name) { - this.properties.room = room_name; - this.room_widget.value = room_name; - this.connectSocket(); - }; - - //force label names - LGSillyClient.prototype.onDrawForeground = function() { - for (var i = 1; i < this.inputs.length; ++i) { - var slot = this.inputs[i]; - slot.label = "in_" + i; - } - for (var i = 1; i < this.outputs.length; ++i) { - var slot = this.outputs[i]; - slot.label = "out_" + i; - } - }; - - LGSillyClient.prototype.onExecute = function() { - if (!this._server || !this._server.is_connected) { - return; - } - - var only_send_changes = this.properties.only_send_changes; - - for (var i = 1; i < this.inputs.length; ++i) { - var data = this.getInputData(i); - var prev_data = this._last_sent_data[i]; - if (data != null) { - if (only_send_changes) - { - var is_equal = true; - if( data && data.length && prev_data && prev_data.length == data.length && data.constructor !== String) - { - for(var j = 0; j < data.length; ++j) - if( prev_data[j] != data[j] ) - { - is_equal = false; - break; - } - } - else if(this._last_sent_data[i] != data) - is_equal = false; - if(is_equal) - continue; - } - this._server.sendMessage({ type: 0, channel: i, data: data }); - if( data.length && data.constructor !== String ) - { - if( this._last_sent_data[i] ) - { - this._last_sent_data[i].length = data.length; - for(var j = 0; j < data.length; ++j) - this._last_sent_data[i][j] = data[j]; - } - else //create - { - if(data.constructor === Array) - this._last_sent_data[i] = data.concat(); - else - this._last_sent_data[i] = new data.constructor( data ); - } - } - else - this._last_sent_data[i] = data; //should be cloned - } - } - - for (var i = 1; i < this.outputs.length; ++i) { - this.setOutputData(i, this._last_received_data[i]); - } - - if (this.boxcolor == "#AFA") { - this.boxcolor = "#6C6"; - } - }; - - LGSillyClient.prototype.connectSocket = function() { - var that = this; - if (typeof SillyClient == "undefined") { - if (!this._error) { - console.error( - "SillyClient node cannot be used, you must include SillyServer.js" - ); - } - this._error = true; - return; - } - - this._server = new SillyClient(); - this._server.on_ready = function() { - console.log("ready"); - that.boxcolor = "#6C6"; - }; - this._server.on_message = function(id, msg) { - var data = null; - try { - data = JSON.parse(msg); - } catch (err) { - return; - } - - if (data.type == 1) { - //EVENT slot - if ( - data.data.object_class && - LiteGraph[data.data.object_class] - ) { - var obj = null; - try { - obj = new LiteGraph[data.data.object_class](data.data); - that.triggerSlot(0, obj); - } catch (err) { - return; - } - } else { - that.triggerSlot(0, data.data); - } - } //for FLOW slots - else { - that._last_received_data[data.channel || 0] = data.data; - } - that.boxcolor = "#AFA"; - }; - this._server.on_error = function(e) { - console.log("couldnt connect to websocket"); - that.boxcolor = "#E88"; - }; - this._server.on_close = function(e) { - console.log("connection closed"); - that.boxcolor = "#000"; - }; - - if (this.properties.url && this.properties.room) { - try { - this._server.connect(this.properties.url, this.properties.room); - } catch (err) { - console.error("SillyServer error: " + err); - this._server = null; - return; - } - this._final_url = this.properties.url + "/" + this.properties.room; - } - }; - - LGSillyClient.prototype.send = function(data) { - if (!this._server || !this._server.is_connected) { - return; - } - this._server.sendMessage({ type: 1, data: data }); - }; - - LGSillyClient.prototype.onAction = function(action, param) { - if (!this._server || !this._server.is_connected) { - return; - } - this._server.sendMessage({ type: 1, action: action, data: param }); - }; - - LGSillyClient.prototype.onGetInputs = function() { - return [["in", 0]]; - }; - - LGSillyClient.prototype.onGetOutputs = function() { - return [["out", 0]]; - }; - - LiteGraph.registerNodeType("network/sillyclient", LGSillyClient); - -//HTTP Request -function HTTPRequestNode() { - var that = this; - this.addInput("request", LiteGraph.ACTION); - this.addInput("url", "string"); - this.addProperty("url", ""); - this.addOutput("ready", LiteGraph.EVENT); - this.addOutput("data", "string"); - this.addWidget("button", "Fetch", null, this.fetch.bind(this)); - this._data = null; - this._fetching = null; -} - -HTTPRequestNode.title = "HTTP Request"; -HTTPRequestNode.desc = "Fetch data through HTTP"; - -HTTPRequestNode.prototype.fetch = function() -{ - var url = this.properties.url; - if(!url) - return; - - this.boxcolor = "#FF0"; - var that = this; - this._fetching = fetch(url) - .then(resp=>{ - if(!resp.ok) - { - this.boxcolor = "#F00"; - that.trigger("error"); - } - else - { - this.boxcolor = "#0F0"; - return resp.text(); - } - }) - .then(data=>{ - that._data = data; - that._fetching = null; - that.trigger("ready"); - }); -} - -HTTPRequestNode.prototype.onAction = function(evt) -{ - if(evt == "request") - this.fetch(); -} - -HTTPRequestNode.prototype.onExecute = function() { - this.setOutputData(1, this._data); -}; - -HTTPRequestNode.prototype.onGetOutputs = function() { - return [["error",LiteGraph.EVENT]]; -} - -LiteGraph.registerNodeType("network/httprequest", HTTPRequestNode); - - - -})(this); +// event related nodes +(function(global) { + var LiteGraph = global.LiteGraph; + + function LGWebSocket() { + this.size = [60, 20]; + this.addInput("send", LiteGraph.ACTION); + this.addOutput("received", LiteGraph.EVENT); + this.addInput("in", 0); + this.addOutput("out", 0); + this.properties = { + url: "", + room: "lgraph", // allows to filter messages, + only_send_changes: true, + }; + this._ws = null; + this._last_sent_data = []; + this._last_received_data = []; + } + + LGWebSocket.title = "WebSocket"; + LGWebSocket.desc = "Send data through a websocket"; + + LGWebSocket.prototype.onPropertyChanged = function(name, value) { + if (name == "url") { + this.connectSocket(); + } + }; + + LGWebSocket.prototype.onExecute = function() { + if (!this._ws && this.properties.url) { + this.connectSocket(); + } + + if (!this._ws || this._ws.readyState != WebSocket.OPEN) { + return; + } + + var room = this.properties.room; + var only_changes = this.properties.only_send_changes; + + for (var i = 1; i < this.inputs.length; ++i) { + var data = this.getInputData(i); + if (data == null) { + continue; + } + var json; + try { + json = JSON.stringify({ + type: 0, + room: room, + channel: i, + data: data, + }); + } catch (err) { + continue; + } + if (only_changes && this._last_sent_data[i] == json) { + continue; + } + + this._last_sent_data[i] = json; + this._ws.send(json); + } + + for (var i = 1; i < this.outputs.length; ++i) { + this.setOutputData(i, this._last_received_data[i]); + } + + if (this.boxcolor == "#AFA") { + this.boxcolor = "#6C6"; + } + }; + + LGWebSocket.prototype.connectSocket = function() { + var that = this; + var url = this.properties.url; + if (url.substr(0, 2) != "ws") { + url = "ws://" + url; + } + this._ws = new WebSocket(url); + this._ws.onopen = function() { + console.log("ready"); + that.boxcolor = "#6C6"; + }; + this._ws.onmessage = function(e) { + that.boxcolor = "#AFA"; + var data = JSON.parse(e.data); + if (data.room && data.room != that.properties.room) { + return; + } + if (data.type == 1) { + if ( + data.data.object_class && + LiteGraph[data.data.object_class] + ) { + var obj = null; + try { + obj = new LiteGraph[data.data.object_class](data.data); + that.triggerSlot(0, obj); + } catch (err) { + return; + } + } else { + that.triggerSlot(0, data.data); + } + } else { + that._last_received_data[data.channel || 0] = data.data; + } + }; + this._ws.onerror = function(e) { + console.log("couldnt connect to websocket"); + that.boxcolor = "#E88"; + }; + this._ws.onclose = function(e) { + console.log("connection closed"); + that.boxcolor = "#000"; + }; + }; + + LGWebSocket.prototype.send = function(data) { + if (!this._ws || this._ws.readyState != WebSocket.OPEN) { + return; + } + this._ws.send(JSON.stringify({ type: 1, msg: data })); + }; + + LGWebSocket.prototype.onAction = function(action, param) { + if (!this._ws || this._ws.readyState != WebSocket.OPEN) { + return; + } + this._ws.send({ + type: 1, + room: this.properties.room, + action: action, + data: param, + }); + }; + + LGWebSocket.prototype.onGetInputs = function() { + return [["in", 0]]; + }; + + LGWebSocket.prototype.onGetOutputs = function() { + return [["out", 0]]; + }; + + LiteGraph.registerNodeType("network/websocket", LGWebSocket); + + // It is like a websocket but using the SillyServer.js server that bounces packets back to all clients connected: + // For more information: https://github.com/jagenjo/SillyServer.js + + function LGSillyClient() { + // this.size = [60,20]; + this.room_widget = this.addWidget( + "text", + "Room", + "lgraph", + this.setRoom.bind(this), + ); + this.addWidget( + "button", + "Reconnect", + null, + this.connectSocket.bind(this), + ); + + this.addInput("send", LiteGraph.ACTION); + this.addOutput("received", LiteGraph.EVENT); + this.addInput("in", 0); + this.addOutput("out", 0); + this.properties = { + url: "tamats.com:55000", + room: "lgraph", + only_send_changes: true, + }; + + this._server = null; + this.connectSocket(); + this._last_sent_data = []; + this._last_received_data = []; + + if(typeof(SillyClient) == "undefined") + console.warn("remember to add SillyClient.js to your project: https://tamats.com/projects/sillyserver/src/sillyclient.js"); + } + + LGSillyClient.title = "SillyClient"; + LGSillyClient.desc = "Connects to SillyServer to broadcast messages"; + + LGSillyClient.prototype.onPropertyChanged = function(name, value) { + if (name == "room") { + this.room_widget.value = value; + } + this.connectSocket(); + }; + + LGSillyClient.prototype.setRoom = function(room_name) { + this.properties.room = room_name; + this.room_widget.value = room_name; + this.connectSocket(); + }; + + // force label names + LGSillyClient.prototype.onDrawForeground = function() { + for (var i = 1; i < this.inputs.length; ++i) { + var slot = this.inputs[i]; + slot.label = "in_" + i; + } + for (var i = 1; i < this.outputs.length; ++i) { + var slot = this.outputs[i]; + slot.label = "out_" + i; + } + }; + + LGSillyClient.prototype.onExecute = function() { + if (!this._server || !this._server.is_connected) { + return; + } + + var only_send_changes = this.properties.only_send_changes; + + for (var i = 1; i < this.inputs.length; ++i) { + var data = this.getInputData(i); + var prev_data = this._last_sent_data[i]; + if (data != null) { + if (only_send_changes) { + var is_equal = true; + if( data && data.length && prev_data && prev_data.length == data.length && data.constructor !== String) { + for(var j = 0; j < data.length; ++j) + if( prev_data[j] != data[j] ) { + is_equal = false; + break; + } + } else if(this._last_sent_data[i] != data) + is_equal = false; + if(is_equal) + continue; + } + this._server.sendMessage({ type: 0, channel: i, data: data }); + if( data.length && data.constructor !== String ) { + if( this._last_sent_data[i] ) { + this._last_sent_data[i].length = data.length; + for(var j = 0; j < data.length; ++j) + this._last_sent_data[i][j] = data[j]; + } else { // create + if(data.constructor === Array) + this._last_sent_data[i] = data.concat(); + else + this._last_sent_data[i] = new data.constructor( data ); + } + } else + this._last_sent_data[i] = data; // should be cloned + } + } + + for (var i = 1; i < this.outputs.length; ++i) { + this.setOutputData(i, this._last_received_data[i]); + } + + if (this.boxcolor == "#AFA") { + this.boxcolor = "#6C6"; + } + }; + + LGSillyClient.prototype.connectSocket = function() { + var that = this; + if (typeof SillyClient == "undefined") { + if (!this._error) { + console.error("SillyClient node cannot be used, you must include SillyServer.js"); + } + this._error = true; + return; + } + + this._server = new SillyClient(); + this._server.on_ready = function() { + console.log("ready"); + that.boxcolor = "#6C6"; + }; + this._server.on_message = function(id, msg) { + var data = null; + try { + data = JSON.parse(msg); + } catch (err) { + return; + } + + if (data.type == 1) { + // EVENT slot + if ( + data.data.object_class && + LiteGraph[data.data.object_class] + ) { + var obj = null; + try { + obj = new LiteGraph[data.data.object_class](data.data); + that.triggerSlot(0, obj); + } catch (err) { + return; + } + } else { + that.triggerSlot(0, data.data); + } + } else { + that._last_received_data[data.channel || 0] = data.data; + } + that.boxcolor = "#AFA"; + }; + this._server.on_error = function(e) { + console.log("couldnt connect to websocket"); + that.boxcolor = "#E88"; + }; + this._server.on_close = function(e) { + console.log("connection closed"); + that.boxcolor = "#000"; + }; + + if (this.properties.url && this.properties.room) { + try { + this._server.connect(this.properties.url, this.properties.room); + } catch (err) { + console.error("SillyServer error: " + err); + this._server = null; + return; + } + this._final_url = this.properties.url + "/" + this.properties.room; + } + }; + + LGSillyClient.prototype.send = function(data) { + if (!this._server || !this._server.is_connected) { + return; + } + this._server.sendMessage({ type: 1, data: data }); + }; + + LGSillyClient.prototype.onAction = function(action, param) { + if (!this._server || !this._server.is_connected) { + return; + } + this._server.sendMessage({ type: 1, action: action, data: param }); + }; + + LGSillyClient.prototype.onGetInputs = function() { + return [["in", 0]]; + }; + + LGSillyClient.prototype.onGetOutputs = function() { + return [["out", 0]]; + }; + + LiteGraph.registerNodeType("network/sillyclient", LGSillyClient); + + // HTTP Request + function HTTPRequestNode() { + this.addInput("request", LiteGraph.ACTION); + this.addInput("url", "string"); + this.addProperty("url", ""); + this.addOutput("ready", LiteGraph.EVENT); + this.addOutput("data", "string"); + this.addWidget("button", "Fetch", null, this.fetch.bind(this)); + this._data = null; + this._fetching = null; + } + + HTTPRequestNode.title = "HTTP Request"; + HTTPRequestNode.desc = "Fetch data through HTTP"; + + HTTPRequestNode.prototype.fetch = function() { + var url = this.getInputData(1) || this.properties.url; + if(!url) + return; + + this.boxcolor = "#FF0"; + var that = this; + this._fetching = fetch(url) + .then((resp) => { + if(!resp.ok) { + this.boxcolor = "#F00"; + that.trigger("error"); + } else { + this.boxcolor = "#0F0"; + return resp.text(); + } + }) + .then((data) => { + that._data = data; + that._fetching = null; + that.trigger("ready"); + }); + } + + HTTPRequestNode.prototype.onAction = function(evt) { + if(evt == "request") + this.fetch(); + } + + HTTPRequestNode.prototype.onExecute = function() { + this.setOutputData(1, this._data); + }; + + HTTPRequestNode.prototype.onGetOutputs = function() { + return [["error",LiteGraph.EVENT]]; + } + + LiteGraph.registerNodeType("network/httprequest", HTTPRequestNode); + + + +})(this); diff --git a/build/litegraph.mini.pack.js b/build/litegraph.mini.pack.js new file mode 100644 index 000000000..9c57ddb27 --- /dev/null +++ b/build/litegraph.mini.pack.js @@ -0,0 +1,16 @@ +/*packed*/!function(global){var LiteGraph=global.LiteGraph={VERSION:.4,CANVAS_GRID_SIZE:10,NODE_TITLE_HEIGHT:30,NODE_TITLE_TEXT_Y:20,NODE_SLOT_HEIGHT:20,NODE_WIDGET_HEIGHT:20,NODE_WIDTH:140,NODE_MIN_WIDTH:50,NODE_COLLAPSED_RADIUS:10,NODE_COLLAPSED_WIDTH:80,NODE_TITLE_COLOR:"#999",NODE_SELECTED_TITLE_COLOR:"#FFF",NODE_TEXT_SIZE:14,NODE_TEXT_COLOR:"#AAA",NODE_SUBTEXT_SIZE:12,NODE_DEFAULT_COLOR:"#333",NODE_DEFAULT_BGCOLOR:"#353535",NODE_DEFAULT_BOXCOLOR:"#666",NODE_DEFAULT_SHAPE:"box",NODE_BOX_OUTLINE_COLOR:"#FFF",DEFAULT_SHADOW_COLOR:"rgba(0,0,0,0.5)",DEFAULT_GROUP_FONT:24,WIDGET_BGCOLOR:"#222",WIDGET_OUTLINE_COLOR:"#666",WIDGET_TEXT_COLOR:"#DDD",WIDGET_SECONDARY_TEXT_COLOR:"#999",LINK_COLOR:"#9A9",EVENT_LINK_COLOR:"#A86",CONNECTING_LINK_COLOR:"#AFA",MAX_NUMBER_OF_NODES:1e3,DEFAULT_POSITION:[100,100],VALID_SHAPES:["default","box","round","card"],BOX_SHAPE:1,ROUND_SHAPE:2,CIRCLE_SHAPE:3,CARD_SHAPE:4,ARROW_SHAPE:5,GRID_SHAPE:6,INPUT:1,OUTPUT:2,EVENT:-1,ACTION:-1,NODE_MODES:["Always","On Event","Never","On Trigger"],NODE_MODES_COLORS:["#666","#422","#333","#224","#626"],ALWAYS:0,ON_EVENT:1,NEVER:2,ON_TRIGGER:3,UP:1,DOWN:2,LEFT:3,RIGHT:4,CENTER:5,LINK_RENDER_MODES:["Straight","Linear","Spline"],STRAIGHT_LINK:0,LINEAR_LINK:1,SPLINE_LINK:2,NORMAL_TITLE:0,NO_TITLE:1,TRANSPARENT_TITLE:2,AUTOHIDE_TITLE:3,VERTICAL_LAYOUT:"vertical",proxy:null,node_images_path:"",debug:!1,catch_exceptions:!0,throw_errors:!0,allow_scripts:!1,use_deferred_actions:!0,registered_node_types:{},node_types_by_file_extension:{},Nodes:{},Globals:{},searchbox_extras:{},auto_sort_node_types:!1,node_box_coloured_when_on:!1,node_box_coloured_by_mode:!1,dialog_close_on_mouse_leave:!0,dialog_close_on_mouse_leave_delay:500,shift_click_do_break_link_from:!1,click_do_break_link_to:!1,search_hide_on_mouse_leave:!0,search_filter_enabled:!1,search_show_all_on_open:!0,auto_load_slot_types:!1,registered_slot_in_types:{},registered_slot_out_types:{},slot_types_in:[],slot_types_out:[],slot_types_default_in:[],slot_types_default_out:[],alt_drag_do_clone_nodes:!1,do_add_triggers_slots:!1,allow_multi_output_for_events:!0,middle_click_slot_add_default_node:!1,release_link_on_empty_shows_menu:!1,pointerevents_method:"mouse",ctrl_shift_v_paste_connect_unselected_outputs:!1,use_uuids:!1,registerNodeType:function(type,base_class){if(!base_class.prototype)throw"Cannot register a simple object, it must be a class with a prototype";base_class.type=type,LiteGraph.debug&&console.log("Node registered: "+type);var i,classname=base_class.name,pos=type.lastIndexOf("/");for(i in base_class.category=type.substring(0,pos),base_class.title||(base_class.title=classname),LGraphNode.prototype)base_class.prototype[i]||(base_class.prototype[i]=LGraphNode.prototype[i]);pos=this.registered_node_types[type];if(pos&&console.log("replacing node type: "+type),!Object.prototype.hasOwnProperty.call(base_class.prototype,"shape")&&(Object.defineProperty(base_class.prototype,"shape",{set:function(v){switch(v){case"default":delete this._shape;break;case"box":this._shape=LiteGraph.BOX_SHAPE;break;case"round":this._shape=LiteGraph.ROUND_SHAPE;break;case"circle":this._shape=LiteGraph.CIRCLE_SHAPE;break;case"card":this._shape=LiteGraph.CARD_SHAPE;break;default:this._shape=v}},get:function(){return this._shape},enumerable:!0,configurable:!0}),base_class.supported_extensions))for(let i in base_class.supported_extensions){var ext=base_class.supported_extensions[i];ext&&ext.constructor===String&&(this.node_types_by_file_extension[ext.toLowerCase()]=base_class)}(this.registered_node_types[type]=base_class).constructor.name&&(this.Nodes[classname]=base_class),LiteGraph.onNodeTypeRegistered&&LiteGraph.onNodeTypeRegistered(type,base_class),pos&&LiteGraph.onNodeTypeReplaced&&LiteGraph.onNodeTypeReplaced(type,base_class,pos),base_class.prototype.onPropertyChange&&console.warn("LiteGraph node class "+type+" has onPropertyChange method, it must be called onPropertyChanged with d at the end"),this.auto_load_slot_types&&new base_class(base_class.title||"tmpnode")},unregisterNodeType:function(type){var base_class=type.constructor===String?this.registered_node_types[type]:type;if(!base_class)throw"node type not found: "+type;delete this.registered_node_types[base_class.type],base_class.constructor.name&&delete this.Nodes[base_class.constructor.name]},registerNodeAndSlotType:function(type,slot_type,out){out=out||!1;var class_type=(type.constructor===String&&"anonymous"!==this.registered_node_types[type]?this.registered_node_types[type]:type).constructor.type;let allTypes=[];allTypes="string"==typeof slot_type?slot_type.split(","):slot_type==this.EVENT||slot_type==this.ACTION?["_event_"]:["*"];for(let i=0;i(a^16*Math.random()>>a/4).toString(16))},isValidConnection:function(type_a,type_b){if(""!=type_b&&"*"!==type_b||(type_b=0),!(type_a=""!=type_a&&"*"!==type_a?type_a:0)||!type_b||type_a==type_b||type_a==LiteGraph.EVENT&&type_b==LiteGraph.ACTION)return!0;if(type_a=String(type_a),type_b=String(type_b),type_a=type_a.toLowerCase(),type_b=type_b.toLowerCase(),-1==type_a.indexOf(",")&&-1==type_b.indexOf(","))return type_a==type_b;for(var supported_types_a=type_a.split(","),supported_types_b=type_b.split(","),i=0;imax_size&&(max_size=node.size[max_size_index]),layout==LiteGraph.VERTICAL_LAYOUT?0:1);y+=node.size[max_size_index]+margin+LiteGraph.NODE_TITLE_HEIGHT}x+=max_size+margin}}this.setDirtyCanvas(!0,!0)},LGraph.prototype.getTime=function(){return this.globaltime},LGraph.prototype.getFixedTime=function(){return this.fixedtime},LGraph.prototype.getElapsedTime=function(){return this.elapsed_time},LGraph.prototype.sendEventToAllNodes=function(eventname,params,mode){mode=mode||LiteGraph.ALWAYS;var nodes=this._nodes_in_order||this._nodes;if(nodes)for(var j=0,l=nodes.length;j=LiteGraph.MAX_NUMBER_OF_NODES)throw"LiteGraph: max number of nodes in a graph reached";return LiteGraph.use_uuids?null!=node.id&&-1!=node.id||(node.id=LiteGraph.uuidv4()):null==node.id||-1==node.id?node.id=++this.last_node_id:this.last_node_id=this.outputs.length)){var output_info=this.outputs[slot];if(output_info&&(output_info._data=data,this.outputs[slot].links))for(var i=0;i=this.outputs.length)){var output_info=this.outputs[slot];if(output_info&&(output_info.type=type,this.outputs[slot].links))for(var i=0;i=this.inputs.length||null==this.inputs[slot].link))return slot=this.inputs[slot].link,(slot=this.graph.links[slot])?(force_update&&(force_update=this.graph.getNodeById(slot.origin_id))&&(force_update.updateOutputData?force_update.updateOutputData(slot.origin_slot):force_update.onExecute&&force_update.onExecute()),slot.data):null},LGraphNode.prototype.getInputDataType=function(slot){var node;return this.inputs&&!(slot>=this.inputs.length||null==this.inputs[slot].link)&&(slot=this.inputs[slot].link,slot=this.graph.links[slot])?(node=this.graph.getNodeById(slot.origin_id))?(node=node.outputs[slot.origin_slot])?node.type:null:slot.type:null},LGraphNode.prototype.getInputDataByName=function(slot_name,force_update){slot_name=this.findInputSlot(slot_name);return-1==slot_name?null:this.getInputData(slot_name,force_update)},LGraphNode.prototype.isInputConnected=function(slot){return!!this.inputs&&slot=this.inputs.length)&&(slot=this.inputs[slot])&&null!==slot.link&&(slot=this.graph.links[slot.link])?this.graph.getNodeById(slot.origin_id):null},LGraphNode.prototype.getInputOrProperty=function(name){if(!this.inputs||!this.inputs.length)return this.properties?this.properties[name]:null;for(var i=0,l=this.inputs.length;i=this.outputs.length?null:this.outputs[slot]._data},LGraphNode.prototype.getOutputInfo=function(slot){return this.outputs&&slot=this.outputs.length)return null;var output=this.outputs[slot];if(!output.links||0==output.links.length)return null;for(var r=[],i=0;ix&&this.pos[1]-margin_top-marginy)return!0;return!1},LGraphNode.prototype.getSlotInPosition=function(x,y){var link_pos=new Float32Array(2);if(this.inputs)for(var i=0,l=this.inputs.length;i=this.outputs.length)return LiteGraph.debug&&console.log("Connect: Error, slot number not found"),null;if(!(target_node=target_node&&target_node.constructor===Number?this.graph.getNodeById(target_node):target_node))throw"target node is null";if(target_node==this)return null;if(target_slot.constructor===String){if(-1==(target_slot=target_node.findInputSlot(target_slot)))return LiteGraph.debug&&console.log("Connect: Error, no slot of name "+target_slot),null}else if(target_slot===LiteGraph.EVENT){if(!LiteGraph.do_add_triggers_slots)return null;target_node.changeMode(LiteGraph.ON_TRIGGER),target_slot=target_node.findInputSlot("onTrigger")}else if(!target_node.inputs||target_slot>=target_node.inputs.length)return LiteGraph.debug&&console.log("Connect: Error, slot number not found"),null;var link_info,changed=!1,input=target_node.inputs[target_slot],output=this.outputs[slot];return this.outputs[slot]?!1!==(target_slot=target_node.onBeforeConnectInput?target_node.onBeforeConnectInput(target_slot):target_slot)&&null!==target_slot&&LiteGraph.isValidConnection(output.type,input.type)?target_node.onConnectInput&&!1===target_node.onConnectInput(target_slot,output.type,output,this,slot)||this.onConnectOutput&&!1===this.onConnectOutput(slot,input.type,input,target_node,target_slot)?null:(target_node.inputs[target_slot]&&null!=target_node.inputs[target_slot].link&&(this.graph.beforeChange(),target_node.disconnectInput(target_slot,{doProcessChange:!1}),changed=!0),null!==output.links&&output.links.length&&output.type===LiteGraph.EVENT&&!LiteGraph.allow_multi_output_for_events&&(this.graph.beforeChange(),this.disconnectOutput(slot,!1,{doProcessChange:!1}),changed=!0),link_info=new LLink(LiteGraph.use_uuids?LiteGraph.uuidv4():++this.graph.last_link_id,input.type||output.type,this.id,slot,target_node.id,target_slot),this.graph.links[link_info.id]=link_info,null==output.links&&(output.links=[]),output.links.push(link_info.id),target_node.inputs[target_slot].link=link_info.id,this.graph&&this.graph._version++,this.onConnectionsChange&&this.onConnectionsChange(LiteGraph.OUTPUT,slot,!0,link_info,output),target_node.onConnectionsChange&&target_node.onConnectionsChange(LiteGraph.INPUT,target_slot,!0,link_info,input),this.graph&&this.graph.onNodeConnectionChange&&(this.graph.onNodeConnectionChange(LiteGraph.INPUT,target_node,target_slot,this,slot),this.graph.onNodeConnectionChange(LiteGraph.OUTPUT,this,slot,target_node,target_slot)),this.setDirtyCanvas(!1,!0),this.graph.afterChange(),this.graph.connectionChange(this,link_info),link_info):(this.setDirtyCanvas(!1,!0),changed&&this.graph.connectionChange(this,null),null):null},LGraphNode.prototype.disconnectOutput=function(slot,target_node){if(slot.constructor===String){if(-1==(slot=this.findOutputSlot(slot)))return LiteGraph.debug&&console.log("Connect: Error, no slot of name "+slot),!1}else if(!this.outputs||slot>=this.outputs.length)return LiteGraph.debug&&console.log("Connect: Error, slot number not found"),!1;var output=this.outputs[slot];if(!output||!output.links||0==output.links.length)return!1;if(target_node){if(!(target_node=target_node.constructor===Number?this.graph.getNodeById(target_node):target_node))throw"Target Node not found";for(var i=0,l=output.links.length;i=this.inputs.length)return LiteGraph.debug&&console.log("Connect: Error, slot number not found"),!1;var input=this.inputs[slot];if(!input)return!1;var link_id=this.inputs[slot].link;if(null!=link_id){this.inputs[slot].link=null;var link_info=this.graph.links[link_id];if(link_info){var target_node=this.graph.getNodeById(link_info.origin_id);if(!target_node)return!1;var output=target_node.outputs[link_info.origin_slot];if(!output||!output.links||0==output.links.length)return!1;for(var i=0,l=output.links.length;iLGraphNode.MAX_CONSOLE&&this.console.shift(),this.graph.onNodeTrace&&this.graph.onNodeTrace(this,msg)},LGraphNode.prototype.setDirtyCanvas=function(dirty_foreground,dirty_background){this.graph&&this.graph.sendActionToCanvas("setDirty",[dirty_foreground,dirty_background])},LGraphNode.prototype.loadImage=function(url){var img=new Image,that=(img.src=LiteGraph.node_images_path+url,img.ready=!1,this);return img.onload=function(){this.ready=!0,that.setDirtyCanvas(!0)},img},LGraphNode.prototype.captureInput=function(v){if(this.graph&&this.graph.list_of_graphcanvas)for(var list=this.graph.list_of_graphcanvas,i=0;i=this.viewport[0]&&x=this.viewport[1]&&rectthis.max_scale&&(value=this.max_scale),value!=this.scale&&this.element&&(rect=this.element.getBoundingClientRect())&&(zooming_center=zooming_center||[.5*rect.width,.5*rect.height],rect=this.convertCanvasToOffset(zooming_center),this.scale=value,Math.abs(this.scale-1)<.01&&(this.scale=1),zooming_center=[(value=this.convertCanvasToOffset(zooming_center))[0]-rect[0],value[1]-rect[1]],this.offset[0]+=zooming_center[0],this.offset[1]+=zooming_center[1],this.onredraw)&&this.onredraw(this)},DragAndScale.prototype.changeDeltaScale=function(value,zooming_center){this.changeScale(this.scale*value,zooming_center)},DragAndScale.prototype.reset=function(){this.scale=1,this.offset[0]=0,this.offset[1]=0},global.LGraphCanvas=LiteGraph.LGraphCanvas=LGraphCanvas,LGraphCanvas.DEFAULT_BACKGROUND_IMAGE="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAIAAAD/gAIDAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAQBJREFUeNrs1rEKwjAUhlETUkj3vP9rdmr1Ysammk2w5wdxuLgcMHyptfawuZX4pJSWZTnfnu/lnIe/jNNxHHGNn//HNbbv+4dr6V+11uF527arU7+u63qfa/bnmh8sWLBgwYJlqRf8MEptXPBXJXa37BSl3ixYsGDBMliwFLyCV/DeLIMFCxYsWLBMwSt4Be/NggXLYMGCBUvBK3iNruC9WbBgwYJlsGApeAWv4L1ZBgsWLFiwYJmCV/AK3psFC5bBggULloJX8BpdwXuzYMGCBctgwVLwCl7Be7MMFixYsGDBsu8FH1FaSmExVfAxBa/gvVmwYMGCZbBg/W4vAQYA5tRF9QYlv/QAAAAASUVORK5CYII=",LGraphCanvas.link_type_colors={"-1":LiteGraph.EVENT_LINK_COLOR,number:"#AAA",node:"#DCA"},LGraphCanvas.gradients={},LGraphCanvas.prototype.clear=function(){this.frame=0,this.last_draw_time=0,this.render_time=0,this.fps=0,this.dragging_rectangle=null,this.selected_nodes={},this.selected_group=null,this.visible_nodes=[],this.node_dragged=null,this.node_over=null,this.node_capturing_input=null,this.connecting_node=null,this.highlighted_links={},this.dragging_canvas=!1,this.dirty_canvas=!0,this.dirty_bgcanvas=!0,this.dirty_area=null,this.node_in_panel=null,this.node_widget=null,this.last_mouse=[0,0],this.last_mouseclick=0,this.pointer_is_down=!1,this.pointer_is_double=!1,this.visible_area.set([0,0,0,0]),this.onClear&&this.onClear()},LGraphCanvas.prototype.setGraph=function(graph,skip_clear){this.graph!=graph&&(skip_clear||this.clear(),!graph&&this.graph?this.graph.detachCanvas(this):(graph.attachCanvas(this),this._graph_stack&&(this._graph_stack=null),this.setDirty(!0,!0)))},LGraphCanvas.prototype.getTopGraph=function(){return this._graph_stack.length?this._graph_stack[0]:this.graph},LGraphCanvas.prototype.openSubgraph=function(graph){if(!graph)throw"graph cannot be null";if(this.graph==graph)throw"graph cannot be the same";this.clear(),this.graph&&(this._graph_stack||(this._graph_stack=[]),this._graph_stack.push(this.graph)),graph.attachCanvas(this),this.checkPanels(),this.setDirty(!0,!0)},LGraphCanvas.prototype.closeSubgraph=function(){var subgraph_node,graph;this._graph_stack&&0!=this._graph_stack.length&&(subgraph_node=this.graph._subgraph_node,graph=this._graph_stack.pop(),this.selected_nodes={},this.highlighted_links={},graph.attachCanvas(this),this.setDirty(!0,!0),subgraph_node&&(this.centerOnNode(subgraph_node),this.selectNodes([subgraph_node])),this.ds.offset=[0,0],this.ds.scale=1)},LGraphCanvas.prototype.getCurrentGraph=function(){return this.graph},LGraphCanvas.prototype.setCanvas=function(canvas,skip_events){if(canvas&&canvas.constructor===String&&!(canvas=document.getElementById(canvas)))throw"Error creating LiteGraph canvas: Canvas not found";if(canvas!==this.canvas&&(canvas||!this.canvas||skip_events||this.unbindEvents(),this.canvas=canvas,this.ds.element=canvas)){if(canvas.className+=" lgraphcanvas",canvas.data=this,canvas.tabindex="1",this.bgcanvas=null,this.bgcanvas||(this.bgcanvas=document.createElement("canvas"),this.bgcanvas.width=this.canvas.width,this.bgcanvas.height=this.canvas.height),null==canvas.getContext){if("canvas"!=canvas.localName)throw"Element supplied for LGraphCanvas must be a element, you passed a "+canvas.localName;throw"This browser doesn't support Canvas"}null==(this.ctx=canvas.getContext("2d"))&&(canvas.webgl_enabled||console.warn("This canvas seems to be WebGL, enabling WebGL renderer"),this.enableWebGL()),skip_events||this.bindEvents()}},LGraphCanvas.prototype._doNothing=function(e){return e.preventDefault(),!1},LGraphCanvas.prototype._doReturnTrue=function(e){return e.preventDefault(),!0},LGraphCanvas.prototype.bindEvents=function(){var canvas,document;this._events_binded?console.warn("LGraphCanvas: events already binded"):(canvas=this.canvas,document=this.getCanvasWindow().document,this._mousedown_callback=this.processMouseDown.bind(this),this._mousewheel_callback=this.processMouseWheel.bind(this),this._mousemove_callback=this.processMouseMove.bind(this),this._mouseup_callback=this.processMouseUp.bind(this),LiteGraph.pointerListenerAdd(canvas,"down",this._mousedown_callback,!0),canvas.addEventListener("mousewheel",this._mousewheel_callback,!1),LiteGraph.pointerListenerAdd(canvas,"up",this._mouseup_callback,!0),LiteGraph.pointerListenerAdd(canvas,"move",this._mousemove_callback),canvas.addEventListener("contextmenu",this._doNothing),canvas.addEventListener("DOMMouseScroll",this._mousewheel_callback,!1),this._key_callback=this.processKey.bind(this),canvas.setAttribute("tabindex",1),canvas.addEventListener("keydown",this._key_callback,!0),document.addEventListener("keyup",this._key_callback,!0),this._ondrop_callback=this.processDrop.bind(this),canvas.addEventListener("dragover",this._doNothing,!1),canvas.addEventListener("dragend",this._doNothing,!1),canvas.addEventListener("drop",this._ondrop_callback,!1),canvas.addEventListener("dragenter",this._doReturnTrue,!1),this._events_binded=!0)},LGraphCanvas.prototype.unbindEvents=function(){var document;this._events_binded?(document=this.getCanvasWindow().document,LiteGraph.pointerListenerRemove(this.canvas,"move",this._mousedown_callback),LiteGraph.pointerListenerRemove(this.canvas,"up",this._mousedown_callback),LiteGraph.pointerListenerRemove(this.canvas,"down",this._mousedown_callback),this.canvas.removeEventListener("mousewheel",this._mousewheel_callback),this.canvas.removeEventListener("DOMMouseScroll",this._mousewheel_callback),this.canvas.removeEventListener("keydown",this._key_callback),document.removeEventListener("keyup",this._key_callback),this.canvas.removeEventListener("contextmenu",this._doNothing),this.canvas.removeEventListener("drop",this._ondrop_callback),this.canvas.removeEventListener("dragenter",this._doReturnTrue),this._mousedown_callback=null,this._mousewheel_callback=null,this._key_callback=null,this._ondrop_callback=null,this._events_binded=!1):console.warn("LGraphCanvas: no events binded")},LGraphCanvas.getFileExtension=function(url){var question=url.indexOf("?"),question=(url=-1!=question?url.substr(0,question):url).lastIndexOf(".");return-1==question?"":url.substr(question+1).toLowerCase()},LGraphCanvas.prototype.enableWebGL=function(){if("undefined"==typeof GL)throw"litegl.js must be included to use a WebGL canvas";if("undefined"==typeof enableWebGLCanvas)throw"webglCanvas.js must be included to use this feature";this.gl=this.ctx=enableWebGLCanvas(this.canvas),this.ctx.webgl=!0,this.bgcanvas=this.canvas,this.bgctx=this.gl,this.canvas.webgl_enabled=!0},LGraphCanvas.prototype.setDirty=function(fgcanvas,bgcanvas){fgcanvas&&(this.dirty_canvas=!0),bgcanvas&&(this.dirty_bgcanvas=!0)},LGraphCanvas.prototype.getCanvasWindow=function(){var doc;return this.canvas?(doc=this.canvas.ownerDocument).defaultView||doc.parentWindow:window},LGraphCanvas.prototype.startRendering=function(){function renderFrame(){this.pause_rendering||this.draw();var window=this.getCanvasWindow();this.is_rendering&&window.requestAnimationFrame(renderFrame.bind(this))}this.is_rendering||(this.is_rendering=!0,renderFrame.call(this))},LGraphCanvas.prototype.stopRendering=function(){this.is_rendering=!1},LGraphCanvas.prototype.blockClick=function(){this.block_click=!0,this.last_mouseclick=0},LGraphCanvas.prototype.processMouseDown=function(e){if(this.set_canvas_dirty_on_mouse_event&&(this.dirty_canvas=!0),this.graph){this.adjustMouseEvent(e);var ref_window=this.getCanvasWindow(),that=(ref_window.document,LGraphCanvas.active_canvas=this),x=e.clientX,y=e.clientY,x=(this.ds.viewport=this.viewport,!this.viewport||this.viewport&&x>=this.viewport[0]&&x=this.viewport[1]&&ycenter[0]+4||e.canvasYcenter[1]+4)){this.showLinkMenu(link,e),this.over_link_center=null;break}}this.selected_group=this.graph.getGroupOnPos(e.canvasX,e.canvasY),this.selected_group_resizing=!1,this.selected_group&&!this.read_only&&(e.ctrlKey&&(this.dragging_rectangle=null),distance([e.canvasX,e.canvasY],[this.selected_group.pos[0]+this.selected_group.size[0],this.selected_group.pos[1]+this.selected_group.size[1]])*this.ds.scale<10?this.selected_group_resizing=!0:this.selected_group.recomputeInsideNodes()),is_double_click&&!this.read_only&&this.allow_searchbox&&(this.showSearchBox(e),e.preventDefault(),e.stopPropagation()),x=!0}}else{if(this.live_mode||node.flags.pinned||this.bringToFront(node),this.allow_interaction&&!this.connecting_node&&!node.flags.collapsed&&!this.live_mode)if(!skip_action&&!1!==node.resizable&&isInsideRectangle(e.canvasX,e.canvasY,node.pos[0]+node.size[0]-5,node.pos[1]+node.size[1]-5,10,10))this.graph.beforeChange(),this.resizing_node=node,this.canvas.style.cursor="se-resize",skip_action=!0;else{if(node.outputs)for(var i=0,l=node.outputs.length;inode.size[0]-LiteGraph.NODE_TITLE_HEIGHT&&y[1]<0&&(that=this,setTimeout(function(){that.openSubgraph(node.subgraph)},10)),this.live_mode&&(block_drag_node=x=!0)),block_drag_node?node.is_selected||this.processNodeSelected(node,e):(this.allow_dragnodes&&(this.graph.beforeChange(),this.node_dragged=node),this.processNodeSelected(node,e)),this.dirty_canvas=!0)}!skip_action&&x&&this.allow_dragcanvas&&(this.dragging_canvas=!0)}return this.last_mouse[0]=e.clientX,this.last_mouse[1]=e.clientY,this.last_mouseclick=LiteGraph.getTime(),this.last_mouse_dragging=!0,this.graph.change(),ref_window.document.activeElement&&("input"==ref_window.document.activeElement.nodeName.toLowerCase()||"textarea"==ref_window.document.activeElement.nodeName.toLowerCase())||e.preventDefault(),e.stopPropagation(),this.onMouseDown&&this.onMouseDown(e),!1}}}},LGraphCanvas.prototype.processMouseMove=function(e){if(this.autoresize&&this.resize(),this.set_canvas_dirty_on_mouse_event&&(this.dirty_canvas=!0),this.graph){(LGraphCanvas.active_canvas=this).adjustMouseEvent(e);var mouse=[e.clientX,e.clientY],delta=(this.mouse[0]=mouse[0],this.mouse[1]=mouse[1],[mouse[0]-this.last_mouse[0],mouse[1]-this.last_mouse[1]]);if(this.last_mouse=mouse,this.graph_mouse[0]=e.canvasX,this.graph_mouse[1]=e.canvasY,!this.block_click){e.dragging=this.last_mouse_dragging,this.node_widget&&(this.processNodeWidgets(this.node_widget[0],this.graph_mouse,e,this.node_widget[1]),this.dirty_canvas=!0);var node=this.graph.getNodeOnPos(e.canvasX,e.canvasY,this.visible_nodes);if(this.dragging_rectangle)this.dragging_rectangle[2]=e.canvasX-this.dragging_rectangle[0],this.dragging_rectangle[3]=e.canvasY-this.dragging_rectangle[1],this.dirty_canvas=!0;else if(this.selected_group&&!this.read_only)this.selected_group_resizing?this.selected_group.size=[e.canvasX-this.selected_group.pos[0],e.canvasY-this.selected_group.pos[1]]:(mouse=delta[0]/this.ds.scale,deltay=delta[1]/this.ds.scale,this.selected_group.move(mouse,deltay,e.ctrlKey),this.selected_group._nodes.length&&(this.dirty_canvas=!0)),this.dirty_bgcanvas=!0;else if(this.dragging_canvas)this.ds.offset[0]+=delta[0]/this.ds.scale,this.ds.offset[1]+=delta[1]/this.ds.scale,this.dirty_canvas=!0,this.dirty_bgcanvas=!0;else if((this.allow_interaction||node&&node.flags.allow_interaction)&&!this.read_only){this.connecting_node&&(this.dirty_canvas=!0);for(var pos,slot,slot_type,deltay,i=0,l=this.graph._nodes.length;icenter[0]+4||e.canvasYcenter[1]+4)){over_link=link;break}}over_link!=this.over_link_center&&(this.over_link_center=over_link,this.dirty_canvas=!0),this.canvas&&(this.canvas.style.cursor="")}if(this.node_capturing_input&&this.node_capturing_input!=node&&this.node_capturing_input.onMouseMove&&this.node_capturing_input.onMouseMove(e,[e.canvasX-this.node_capturing_input.pos[0],e.canvasY-this.node_capturing_input.pos[1]],this),this.node_dragged&&!this.live_mode){for(var i in this.selected_nodes){var n=this.selected_nodes[i];n.pos[0]+=delta[0]/this.ds.scale,n.pos[1]+=delta[1]/this.ds.scale,n.is_selected||this.processNodeSelected(n,e)}this.dirty_canvas=!0,this.dirty_bgcanvas=!0}this.resizing_node&&!this.live_mode&&(mouse=[e.canvasX-this.resizing_node.pos[0],e.canvasY-this.resizing_node.pos[1]],deltay=this.resizing_node.computeSize(),mouse[0]=Math.max(deltay[0],mouse[0]),mouse[1]=Math.max(deltay[1],mouse[1]),this.resizing_node.setSize(mouse),this.canvas.style.cursor="se-resize",this.dirty_canvas=!0,this.dirty_bgcanvas=!0)}}return e.preventDefault(),!1}},LGraphCanvas.prototype.processMouseUp=function(e){var is_primary=void 0===e.isPrimary||e.isPrimary;if(!is_primary)return!1;if(this.set_canvas_dirty_on_mouse_event&&(this.dirty_canvas=!0),this.graph){var document=this.getCanvasWindow().document,document=((LGraphCanvas.active_canvas=this).options.skip_events||(LiteGraph.pointerListenerRemove(document,"move",this._mousemove_callback,!0),LiteGraph.pointerListenerAdd(this.canvas,"move",this._mousemove_callback,!0),LiteGraph.pointerListenerRemove(document,"up",this._mouseup_callback,!0)),this.adjustMouseEvent(e),LiteGraph.getTime());if(e.click_time=document-this.last_mouseclick,this.last_mouse_dragging=!1,this.last_click_position=null,this.block_click&&(this.block_click=!1),1==e.which){this.node_widget&&this.processNodeWidgets(this.node_widget[0],this.graph_mouse,e),this.node_widget=null,this.selected_group&&(document=this.selected_group.pos[0]-Math.round(this.selected_group.pos[0]),diffy=this.selected_group.pos[1]-Math.round(this.selected_group.pos[1]),this.selected_group.move(document,diffy,e.ctrlKey),this.selected_group.pos[0]=Math.round(this.selected_group.pos[0]),this.selected_group.pos[1]=Math.round(this.selected_group.pos[1]),this.selected_group._nodes.length&&(this.dirty_canvas=!0),this.selected_group=null),this.selected_group_resizing=!1;var slot,document=this.graph.getNodeOnPos(e.canvasX,e.canvasY,this.visible_nodes);if(this.dragging_rectangle){if(this.graph){var nodes=this.graph._nodes,node_bounding=new Float32Array(4),diffy=Math.abs(this.dragging_rectangle[2]),h=Math.abs(this.dragging_rectangle[3]),startx=this.dragging_rectangle[2]<0?this.dragging_rectangle[0]-diffy:this.dragging_rectangle[0],starty=this.dragging_rectangle[3]<0?this.dragging_rectangle[1]-h:this.dragging_rectangle[1];if(this.dragging_rectangle[0]=startx,this.dragging_rectangle[1]=starty,this.dragging_rectangle[2]=diffy,this.dragging_rectangle[3]=h,!document||10=this.viewport[0]&&x=this.viewport[1]&&yclipboard_info.nodes[i].pos[0]&&(posMin[0]=clipboard_info.nodes[i].pos[0],posMinIndexes[0]=i),posMin[1]>clipboard_info.nodes[i].pos[1]&&(posMin[1]=clipboard_info.nodes[i].pos[1],posMinIndexes[1]=i)):(posMin=[clipboard_info.nodes[i].pos[0],clipboard_info.nodes[i].pos[1]],posMinIndexes=[i,i]);for(var nodes=[],i=0;i=this.viewport[0]&&x=this.viewport[1]&&y> ";ctx.fillText(title+viewport.getTitle(),.5*canvas.width,40),ctx.restore()}var that,viewport=!1;this.onRenderBackground&&(viewport=this.onRenderBackground(canvas,ctx)),this.viewport||(ctx.restore(),ctx.setTransform(1,0,0,1,0,0)),this.visible_links.length=0,this.graph&&(ctx.save(),this.ds.toCanvasContext(ctx),this.ds.scale<1.5&&!viewport&&this.clear_background_color&&(ctx.fillStyle=this.clear_background_color,ctx.fillRect(this.visible_area[0],this.visible_area[1],this.visible_area[2],this.visible_area[3])),this.background_image&&.5bounding[2]&&(bounding[2]=x),ybounding[3]&&(bounding[3]=y)}function isInsideBounding(p,bb){return!(p[0]bb[1][0]||p[1]>bb[1][1])}function overlapBounding(a,b){var A_end_x=a[0]+a[2],A_end_y=a[1]+a[3],B_end_x=b[0]+b[2],B_end_y=b[1]+b[3];return!(a[0]>B_end_x||a[1]>B_end_y||A_end_xrect.width-root_rect.width-10&&(eventClass=rect.width-root_rect.width-10),rect.height)&&top>rect.height-root_rect.height-10&&(top=rect.height-root_rect.height-10),root.style.left=eventClass+"px",root.style.top=top+"px",options.scale&&(root.style.transform="scale("+options.scale+")")}function CurveEditor(points){this.points=points,this.selected=-1,this.nearest=-1,this.size=null,this.must_update=!0,this.margin=5}function clamp(v,a,b){return va[1]?0:Math.PI,ctx.save(),ctx.translate(skip_border[0],skip_border[1]),ctx.rotate(angleA),ctx.beginPath(),ctx.moveTo(-5,-3),ctx.lineTo(0,7),ctx.lineTo(5,-3),ctx.fill(),ctx.restore(),ctx.save(),ctx.translate(posC[0],posC[1]),ctx.rotate(angleB),ctx.beginPath(),ctx.moveTo(-5,-3),ctx.lineTo(0,7),ctx.lineTo(5,-3),ctx.fill(),ctx.restore()),ctx.beginPath(),ctx.arc(pos[0],pos[1],5,0,2*Math.PI),ctx.fill()),flow){ctx.fillStyle=color;for(i=0;i<5;++i){var f=(.001*LiteGraph.getTime()+.2*i)%1,pos=this.computeConnectionPoint(a,b,f,start_dir,end_dir);ctx.beginPath(),ctx.arc(pos[0],pos[1],5,0,2*Math.PI),ctx.fill()}}},LGraphCanvas.prototype.computeConnectionPoint=function(a,b,t,start_dir,end_dir){start_dir=start_dir||LiteGraph.RIGHT,end_dir=end_dir||LiteGraph.LEFT;var dist=distance(a,b),p0=a,p1=[a[0],a[1]],p2=[b[0],b[1]],a=b;switch(start_dir){case LiteGraph.LEFT:p1[0]+=-.25*dist;break;case LiteGraph.RIGHT:p1[0]+=.25*dist;break;case LiteGraph.UP:p1[1]+=-.25*dist;break;case LiteGraph.DOWN:p1[1]+=.25*dist}switch(end_dir){case LiteGraph.LEFT:p2[0]+=-.25*dist;break;case LiteGraph.RIGHT:p2[0]+=.25*dist;break;case LiteGraph.UP:p2[1]+=-.25*dist;break;case LiteGraph.DOWN:p2[1]+=.25*dist}b=(1-t)*(1-t)*(1-t),start_dir=(1-t)*(1-t)*3*t,end_dir=3*(1-t)*(t*t),t*=t*t;return[b*p0[0]+start_dir*p1[0]+end_dir*p2[0]+t*a[0],b*p0[1]+start_dir*p1[1]+end_dir*p2[1]+t*a[1]]},LGraphCanvas.prototype.drawExecutionOrder=function(ctx){ctx.shadowColor="transparent",ctx.globalAlpha=.25,ctx.textAlign="center",ctx.strokeStyle="white",ctx.globalAlpha=.75;for(var visible_nodes=this.visible_nodes,i=0;iw.last_y+widget_height||void 0===w.last_y)){var old_value=w.value;switch(w.type){case"button":event.type===LiteGraph.pointerevents_method+"down"&&(w.callback&&setTimeout(function(){w.callback(w,that,node,pos,event)},20),w.clicked=!0,this.dirty_canvas=!0);break;case"slider":var old_value=w.value,nvalue=clamp((x-15)/(widget_width-30),0,1);w.options.read_only||(w.value=w.options.min+(w.options.max-w.options.min)*nvalue,old_value!=w.value&&setTimeout(function(){inner_value_change(w,w.value)},20),this.dirty_canvas=!0);break;case"number":case"combo":var old_value=w.value,values,values_list,delta,index,text_values,menu,delta;function inner_clicked(v,option,event){return values!=values_list&&(v=text_values.indexOf(v)),this.value=v,inner_value_change(this,v),!(that.dirty_canvas=!0)}event.type==LiteGraph.pointerevents_method+"move"&&"number"==w.type?(deltaX&&(w.value+=.1*deltaX*(w.options.step||1)),null!=w.options.min&&w.valuew.options.max&&(w.value=w.options.max)):event.type==LiteGraph.pointerevents_method+"down"?(values=w.options.values,values&&values.constructor===Function&&(values=w.options.values(w,node)),values_list=null,"number"!=w.type&&(values_list=values.constructor===Array?values:Object.keys(values)),delta=x<40?-1:widget_width-40w.options.max&&(w.value=w.options.max)):delta?(index=-1,this.last_mouseclick=0,index=values.constructor===Object?values_list.indexOf(String(w.value))+delta:values_list.indexOf(w.value)+delta,index>=values_list.length&&(index=values_list.length-1),index<0&&(index=0),values.constructor===Array?w.value=values[index]:w.value=index):(text_values=values!=values_list?Object.values(values):values,menu=new LiteGraph.ContextMenu(text_values,{scale:Math.max(1,this.ds.scale),event:event,className:"dark",callback:inner_clicked.bind(w)},ref_window))):event.type==LiteGraph.pointerevents_method+"up"&&"number"==w.type&&(delta=x<40?-1:widget_width-40right.pos[0]+right.size[0])&&(right=node),(null===bottom||y+height>bottom.pos[1]+bottom.size[1])&&(bottom=node),(null===left||x"+(info.label||i)+""+value+"",value:i})}if(entries.length)return new LiteGraph.ContextMenu(entries,{event:e,callback:function(v,options,e,prev){var rect;node&&(rect=this.getBoundingClientRect(),canvas.showEditPropertyValue(node,v.value,{position:[rect.left,rect.top]}))},parentMenu:prev_menu,allow_html:!0,node:node},ref_window),!1}},LGraphCanvas.decodeHTML=function(str){var e=document.createElement("div");return e.innerText=str,e.innerHTML},LGraphCanvas.onMenuResizeNode=function(value,options,e,menu,node){if(node){function fApplyMultiNode(node){node.size=node.computeSize(),node.onResize&&node.onResize(node.size)}var graphcanvas=LGraphCanvas.active_canvas;if(!graphcanvas.selected_nodes||Object.keys(graphcanvas.selected_nodes).length<=1)fApplyMultiNode(node);else for(var i in graphcanvas.selected_nodes)fApplyMultiNode(graphcanvas.selected_nodes[i]);node.setDirtyCanvas(!0,!0)}},LGraphCanvas.prototype.showLinkMenu=function(link,e){var that=this,node_left=that.graph.getNodeById(link.origin_id),node_right=that.graph.getNodeById(link.target_id),fromType=!1,destType=(node_left&&node_left.outputs&&node_left.outputs[link.origin_slot]&&(fromType=node_left.outputs[link.origin_slot].type),!1),menu=(node_right&&node_right.outputs&&node_right.outputs[link.target_slot]&&(destType=node_right.inputs[link.target_slot].type),new LiteGraph.ContextMenu(["Add Node",null,"Delete",null],{event:e,title:null!=link.data?link.data.constructor.name:null,callback:function(v,options,e){switch(v){case"Add Node":LGraphCanvas.onMenuAdd(null,null,e,menu,function(node){node.inputs&&node.inputs.length&&node.outputs&&node.outputs.length&&node_left.connectByType(link.origin_slot,node,fromType)&&(node.connectByType(link.target_slot,node_right,destType),node.pos[0]-=.5*node.size[0])});break;case"Delete":that.graph.removeLink(link.id)}}}));return!1},LGraphCanvas.prototype.createDefaultNodeForSlot=function(optPass){var optPass=optPass||{},opts=Object.assign({nodeFrom:null,slotFrom:null,nodeTo:null,slotTo:null,position:[],nodeType:null,posAdd:[0,0],posSizeFix:[0,0]},optPass),isFrom=opts.nodeFrom&&null!==opts.slotFrom,optPass=!isFrom&&opts.nodeTo&&null!==opts.slotTo;if(isFrom||optPass)if(opts.nodeType){var nodeX=isFrom?opts.nodeFrom:opts.nodeTo,slotX=isFrom?opts.slotFrom:opts.slotTo,iSlotConn=!1;switch(typeof slotX){case"string":iSlotConn=isFrom?nodeX.findOutputSlot(slotX,!1):nodeX.findInputSlot(slotX,!1),slotX=(isFrom?nodeX.outputs:nodeX.inputs)[slotX];break;case"object":iSlotConn=isFrom?nodeX.findOutputSlot(slotX.name):nodeX.findInputSlot(slotX.name);break;case"number":iSlotConn=slotX,slotX=(isFrom?nodeX.outputs:nodeX.inputs)[slotX];break;default:return console.warn("Cant get slot information "+slotX),!1}!1!==slotX&&!1!==iSlotConn||console.warn("createDefaultNodeForSlot bad slotX "+slotX+" "+iSlotConn);var fromSlotType=slotX.type==LiteGraph.EVENT?"_event_":slotX.type,slotTypesDefault=isFrom?LiteGraph.slot_types_default_out:LiteGraph.slot_types_default_in;if(slotTypesDefault&&slotTypesDefault[fromSlotType]){if(slotX.link,nodeNewType=!1,Array.isArray(slotTypesDefault[fromSlotType])||"object"==typeof slotTypesDefault[fromSlotType]){for(var typeX in slotTypesDefault[fromSlotType])if(opts.nodeType==slotTypesDefault[fromSlotType][typeX]||"AUTO"==opts.nodeType){nodeNewType=slotTypesDefault[fromSlotType][typeX];break}}else opts.nodeType!=slotTypesDefault[fromSlotType]&&"AUTO"!=opts.nodeType||(nodeNewType=slotTypesDefault[fromSlotType]);if(nodeNewType){var nodeNewOpts=!1,newNode=("object"==typeof nodeNewType&&nodeNewType.node&&(nodeNewType=(nodeNewOpts=nodeNewType).node),LiteGraph.createNode(nodeNewType));if(newNode){if(nodeNewOpts){if(nodeNewOpts.properties)for(var i in nodeNewOpts.properties)newNode.addProperty(i,nodeNewOpts.properties[i]);if(nodeNewOpts.inputs)for(var i in newNode.inputs=[],nodeNewOpts.inputs)newNode.addOutput(nodeNewOpts.inputs[i][0],nodeNewOpts.inputs[i][1]);if(nodeNewOpts.outputs)for(var i in newNode.outputs=[],nodeNewOpts.outputs)newNode.addOutput(nodeNewOpts.outputs[i][0],nodeNewOpts.outputs[i][1]);nodeNewOpts.title&&(newNode.title=nodeNewOpts.title),nodeNewOpts.json&&newNode.configure(nodeNewOpts.json)}return this.graph.add(newNode),newNode.pos=[opts.position[0]+opts.posAdd[0]+(opts.posSizeFix[0]?opts.posSizeFix[0]*newNode.size[0]:0),opts.position[1]+opts.posAdd[1]+(opts.posSizeFix[1]?opts.posSizeFix[1]*newNode.size[1]:0)],isFrom?opts.nodeFrom.connectByType(iSlotConn,newNode,fromSlotType):opts.nodeTo.connectByTypeOutput(iSlotConn,newNode,fromSlotType),!0}console.log("failed creating "+nodeNewType)}}}else console.warn("No type to createDefaultNodeForSlot");else console.warn("No data passed to createDefaultNodeForSlot "+opts.nodeFrom+" "+opts.slotFrom+" "+opts.nodeTo+" "+opts.slotTo);return!1},LGraphCanvas.prototype.showConnectionMenu=function(optPass){var optPass=optPass||{},opts=Object.assign({nodeFrom:null,slotFrom:null,nodeTo:null,slotTo:null,e:null},optPass),that=this,isFrom=opts.nodeFrom&&opts.slotFrom,optPass=!isFrom&&opts.nodeTo&&opts.slotTo;if(isFrom||optPass){var nodeX=isFrom?opts.nodeFrom:opts.nodeTo,slotX=isFrom?opts.slotFrom:opts.slotTo,iSlotConn=!1;switch(typeof slotX){case"string":iSlotConn=isFrom?nodeX.findOutputSlot(slotX,!1):nodeX.findInputSlot(slotX,!1),slotX=(isFrom?nodeX.outputs:nodeX.inputs)[slotX];break;case"object":iSlotConn=isFrom?nodeX.findOutputSlot(slotX.name):nodeX.findInputSlot(slotX.name);break;case"number":iSlotConn=slotX,slotX=(isFrom?nodeX.outputs:nodeX.inputs)[slotX];break;default:return console.warn("Cant get slot information "+slotX),!1}var options=["Add Node",null],fromSlotType=(that.allow_searchbox&&(options.push("Search"),options.push(null)),slotX.type==LiteGraph.EVENT?"_event_":slotX.type),slotTypesDefault=isFrom?LiteGraph.slot_types_default_out:LiteGraph.slot_types_default_in;if(slotTypesDefault&&slotTypesDefault[fromSlotType])if(Array.isArray(slotTypesDefault[fromSlotType])||"object"==typeof slotTypesDefault[fromSlotType])for(var typeX in slotTypesDefault[fromSlotType])options.push(slotTypesDefault[fromSlotType][typeX]);else options.push(slotTypesDefault[fromSlotType]);var menu=new LiteGraph.ContextMenu(options,{event:opts.e,title:(slotX&&""!=slotX.name?slotX.name+(fromSlotType?" | ":""):"")+(slotX&&fromSlotType?fromSlotType:""),callback:function(v,options,e){switch(v){case"Add Node":LGraphCanvas.onMenuAdd(null,null,e,menu,function(node){isFrom?opts.nodeFrom.connectByType(iSlotConn,node,fromSlotType):opts.nodeTo.connectByTypeOutput(iSlotConn,node,fromSlotType)});break;case"Search":isFrom?that.showSearchBox(e,{node_from:opts.nodeFrom,slot_from:slotX,type_filter_in:fromSlotType}):that.showSearchBox(e,{node_to:opts.nodeTo,slot_from:slotX,type_filter_out:fromSlotType});break;default:that.createDefaultNodeForSlot(Object.assign(opts,{position:[opts.e.canvasX,opts.e.canvasY],nodeType:v}))}}})}else console.warn("No data passed to showConnectionMenu");return!1},LGraphCanvas.onShowPropertyEditor=function(item,options,e,menu,node){var property=item.property||"title",value=node[property],dialog=document.createElement("div");dialog.is_modified=!1,dialog.className="graphdialog",dialog.innerHTML="",dialog.close=function(){dialog.parentNode&&dialog.parentNode.removeChild(dialog)};dialog.querySelector(".name").innerText=property;var input=dialog.querySelector(".value");input&&(input.value=value,input.addEventListener("blur",function(e){this.focus()}),input.addEventListener("keydown",function(e){if(dialog.is_modified=!0,27==e.keyCode)dialog.close();else if(13==e.keyCode)inner();else if(13!=e.keyCode&&"textarea"!=e.target.localName)return;e.preventDefault(),e.stopPropagation()}));var value=LGraphCanvas.active_canvas.canvas,rect=value.getBoundingClientRect(),offsetx=-20,offsety=-20;rect&&(offsetx-=rect.left,offsety-=rect.top),event?(dialog.style.left=event.clientX+offsetx+"px",dialog.style.top=event.clientY+offsety+"px"):(dialog.style.left=.5*value.width+offsetx+"px",dialog.style.top=.5*value.height+offsety+"px");dialog.querySelector("button").addEventListener("click",inner),value.parentNode.appendChild(dialog),input&&input.focus();var dialogCloseTimer=null;function inner(){var value;input&&(value=input.value,"Number"==item.type?value=Number(value):"Boolean"==item.type&&(value=Boolean(value)),node[property]=value,dialog.parentNode&&dialog.parentNode.removeChild(dialog),node.setDirtyCanvas(!0,!0))}dialog.addEventListener("mouseleave",function(e){LiteGraph.dialog_close_on_mouse_leave&&!dialog.is_modified&&LiteGraph.dialog_close_on_mouse_leave&&(dialogCloseTimer=setTimeout(dialog.close,LiteGraph.dialog_close_on_mouse_leave_delay))}),dialog.addEventListener("mouseenter",function(e){LiteGraph.dialog_close_on_mouse_leave&&dialogCloseTimer&&clearTimeout(dialogCloseTimer)})},LGraphCanvas.prototype.prompt=function(title,value,callback,event,multiline){var that=this,dialog=(title=title||"",document.createElement("div"));dialog.is_modified=!1,dialog.className="graphdialog rounded",dialog.innerHTML=multiline?" ":" ",dialog.close=function(){that.prompt_box=null,dialog.parentNode&&dialog.parentNode.removeChild(dialog)};var multiline=LGraphCanvas.active_canvas.canvas,dialogCloseTimer=(multiline.parentNode.appendChild(dialog),1LGraphCanvas.search_limit)break}}var filtered=null;if(Array.prototype.filter)filtered=Object.keys(LiteGraph.registered_node_types).filter(inner_test_filter);else for(var i in filtered=[],LiteGraph.registered_node_types)inner_test_filter(i)&&filtered.push(i);for(i=0;iLGraphCanvas.search_limit));i++);if(options.show_general_after_typefiltered&&(sIn.value||sOut.value)){for(var i in filtered_extra=[],LiteGraph.registered_node_types)inner_test_filter(i,{inTypeOverride:!(!sIn||!sIn.value)&&"*",outTypeOverride:!(!sOut||!sOut.value)&&"*"})&&filtered_extra.push(i);for(i=0;iLGraphCanvas.search_limit));i++);}if((sIn.value||sOut.value)&&0==helper.childNodes.length&&options.show_general_if_none_on_typefilter){for(var i in filtered_extra=[],LiteGraph.registered_node_types)inner_test_filter(i,{skipFilter:!0})&&filtered_extra.push(i);for(i=0;iLGraphCanvas.search_limit));i++);}function inner_test_filter(type,optsIn){var optsIn=optsIn||{},optsIn=Object.assign({skipFilter:!1,inTypeOverride:!1,outTypeOverride:!1},optsIn),ctor=LiteGraph.registered_node_types[type];if(filter&&ctor.filter!=filter)return!1;if((!options.show_all_if_empty||str)&&-1===type.toLowerCase().indexOf(str))return!1;if(options.do_type_filter&&!optsIn.skipFilter){ctor=type,type=sIn.value;if(!1!==optsIn.inTypeOverride&&(type=optsIn.inTypeOverride),sIn&&type&&LiteGraph.registered_slot_in_types[type]&&LiteGraph.registered_slot_in_types[type].nodes){var doesInc=LiteGraph.registered_slot_in_types[type].nodes.includes(ctor);if(!1===doesInc)return!1}type=sOut.value;if(!1!==optsIn.outTypeOverride&&(type=optsIn.outTypeOverride),sOut&&type&&LiteGraph.registered_slot_out_types[type]&&LiteGraph.registered_slot_out_types[type].nodes){doesInc=LiteGraph.registered_slot_out_types[type].nodes.includes(ctor);if(!1===doesInc)return!1}}return!0}}function addResult(type,className){var help=document.createElement("div");first=first||type,help.innerText=type,help.dataset.type=escape(type),help.className="litegraph lite-search-item",className&&(help.className+=" "+className),help.addEventListener("click",function(e){select(unescape(this.dataset.type))}),helper.appendChild(help)}}return dialog.style.left=left+"px",dialog.style.top=top+"px",event.layerY>def_options.height-200&&(helper.style.maxHeight=def_options.height-event.layerY-20+"px"),input.focus(),options.show_all_on_open&&refreshHelper(),dialog},LGraphCanvas.prototype.showEditPropertyValue=function(node,property,options){if(node&&void 0!==node.properties[property]){options=options||{};var info=node.getPropertyInfo(property),type=info.type,input_html="";if("string"==type||"number"==type||"array"==type||"object"==type)input_html="";else if("enum"!=type&&"combo"!=type||!info.values){if("boolean"!=type&&"toggle"!=type)return void console.warn("unknown type: "+type);input_html=""}else{for(var i in input_html=""}var dialog=this.createDialog(""+(info.label||property)+""+input_html+"",options),input=!1;return"enum"!=type&&"combo"!=type||!info.values?"boolean"==type||"toggle"==type?(input=dialog.querySelector("input"))&&input.addEventListener("click",function(e){dialog.modified(),setValue(!!input.checked)}):(input=dialog.querySelector("input"))&&(input.addEventListener("blur",function(e){this.focus()}),v=void 0!==node.properties[property]?node.properties[property]:"","string"!==type&&(v=JSON.stringify(v)),input.value=v,input.addEventListener("keydown",function(e){if(27==e.keyCode)dialog.close();else if(13==e.keyCode)inner();else if(13!=e.keyCode)return void dialog.modified();e.preventDefault(),e.stopPropagation()})):(input=dialog.querySelector("select")).addEventListener("change",function(e){dialog.modified(),setValue(e.target.value)}),input&&input.focus(),dialog.querySelector("button").addEventListener("click",inner),dialog}function inner(){setValue(input.value)}function setValue(value){info&&info.values&&info.values.constructor===Object&&null!=info.values[value]&&(value=info.values[value]),"number"==typeof node.properties[property]&&(value=Number(value)),"array"!=type&&"object"!=type||(value=JSON.parse(value)),node.properties[property]=value,node.graph&&node.graph._version++,node.onPropertyChanged&&node.onPropertyChanged(property,value),options.onclose&&options.onclose(),dialog.close(),node.setDirtyCanvas(!0,!0)}},LGraphCanvas.prototype.createDialog=function(html,options){options=Object.assign({checkForInput:!1,closeOnLeave:!0,closeOnLeave_checkModified:!0},options||{});var dialog=document.createElement("div"),html=(dialog.className="graphdialog",dialog.innerHTML=html,dialog.is_modified=!1,this.canvas.getBoundingClientRect()),offsetx=-20,offsety=-20,dialogCloseTimer=(html&&(offsetx-=html.left,offsety-=html.top),options.position?(offsetx+=options.position[0],offsety+=options.position[1]):options.event?(offsetx+=options.event.clientX,offsety+=options.event.clientY):(offsetx+=.5*this.canvas.width,offsety+=.5*this.canvas.height),dialog.style.left=offsetx+"px",dialog.style.top=offsety+"px",this.canvas.parentNode.appendChild(dialog),options.checkForInput&&(html=[],html=dialog.querySelectorAll("input"))&&html.forEach(function(iX){iX.addEventListener("keydown",function(e){if(dialog.modified(),27==e.keyCode)dialog.close();else if(13!=e.keyCode)return;e.preventDefault(),e.stopPropagation()}),iX.focus()}),dialog.modified=function(){dialog.is_modified=!0},dialog.close=function(){dialog.parentNode&&dialog.parentNode.removeChild(dialog)},null),prevent_timeout=!1,offsetx=(dialog.addEventListener("mouseleave",function(e){prevent_timeout||(options.closeOnLeave||LiteGraph.dialog_close_on_mouse_leave)&&!dialog.is_modified&&LiteGraph.dialog_close_on_mouse_leave&&(dialogCloseTimer=setTimeout(dialog.close,LiteGraph.dialog_close_on_mouse_leave_delay))}),dialog.addEventListener("mouseenter",function(e){(options.closeOnLeave||LiteGraph.dialog_close_on_mouse_leave)&&dialogCloseTimer&&clearTimeout(dialogCloseTimer)}),dialog.querySelectorAll("select"));return offsetx&&offsetx.forEach(function(selIn){selIn.addEventListener("click",function(e){prevent_timeout++}),selIn.addEventListener("blur",function(e){prevent_timeout=0}),selIn.addEventListener("change",function(e){prevent_timeout=-1})}),dialog},LGraphCanvas.prototype.createPanel=function(title,options){var ref_window=(options=options||{}).window||window,root=document.createElement("div");return root.className="litegraph dialog",root.innerHTML="
",root.header=root.querySelector(".dialog-header"),options.width&&(root.style.width=options.width+(options.width.constructor===Number?"px":"")),options.height&&(root.style.height=options.height+(options.height.constructor===Number?"px":"")),options.closable&&((options=document.createElement("span")).innerHTML="✕",options.classList.add("close"),options.addEventListener("click",function(){root.close()}),root.header.appendChild(options)),root.title_element=root.querySelector(".dialog-title"),root.title_element.innerText=title,root.content=root.querySelector(".dialog-content"),root.alt_content=root.querySelector(".dialog-alt-content"),root.footer=root.querySelector(".dialog-footer"),root.close=function(){root.onClose&&"function"==typeof root.onClose&&root.onClose(),root.parentNode&&root.parentNode.removeChild(root),this.parentNode&&this.parentNode.removeChild(this)},root.toggleAltContent=function(force){var vTo;force=void 0!==force?(vTo=force?"block":"none",force?"none":"block"):(vTo="block"!=root.alt_content.style.display?"block":"none","block"!=root.alt_content.style.display?"none":"block"),root.alt_content.style.display=vTo,root.content.style.display=force},root.toggleFooterVisibility=function(force){force=void 0!==force?force?"block":"none":"block"!=root.footer.style.display?"block":"none",root.footer.style.display=force},root.clear=function(){this.content.innerHTML=""},root.addHTML=function(code,classname,on_footer){var elem=document.createElement("div");return classname&&(elem.className=classname),elem.innerHTML=code,(on_footer?root.footer:root.content).appendChild(elem),elem},root.addButton=function(name,callback,options){var elem=document.createElement("button");return elem.innerText=name,elem.options=options,elem.classList.add("btn"),elem.addEventListener("click",callback),root.footer.appendChild(elem),elem},root.addSeparator=function(){var elem=document.createElement("div");elem.className="separator",root.content.appendChild(elem)},root.addWidget=function(type,name,value,options,callback){options=options||{};var str_value=String(value),elem=("number"==(type=type.toLowerCase())&&(str_value=value.toFixed(3)),document.createElement("div")),value_element=(elem.className="property",elem.innerHTML="",elem.querySelector(".property_name").innerText=options.label||name,elem.querySelector(".property_value"));function innerChange(name,value){options.callback&&options.callback(name,value,options),callback&&callback(name,value,options)}return value_element.innerText=str_value,elem.dataset.property=name,elem.dataset.type=options.type||type,elem.options=options,elem.value=value,"code"==type?elem.addEventListener("click",function(e){root.inner_showCodePad(this.dataset.property)}):"boolean"==type?(elem.classList.add("boolean"),value&&elem.classList.add("bool-on"),elem.addEventListener("click",function(){var propname=this.dataset.property;this.value=!this.value,this.classList.toggle("bool-on"),this.querySelector(".property_value").innerText=this.value?"true":"false",innerChange(propname,this.value)})):"string"==type||"number"==type?(value_element.setAttribute("contenteditable",!0),value_element.addEventListener("keydown",function(e){"Enter"!=e.code||"string"==type&&e.shiftKey||(e.preventDefault(),this.blur())}),value_element.addEventListener("blur",function(){var v=this.innerText;innerChange(this.parentNode.dataset.property,v="number"==this.parentNode.dataset.type?Number(v):v)})):"enum"!=type&&"combo"!=type||(str_value=LGraphCanvas.getPropertyPrintableValue(value,options.values),value_element.innerText=str_value,value_element.addEventListener("click",function(event){var values=options.values||[],propname=this.parentNode.dataset.property,elem_that=this;new LiteGraph.ContextMenu(values,{event:event,className:"dark",callback:function(v,option,event){return elem_that.innerText=v,innerChange(propname,v),!1}},ref_window)})),root.content.appendChild(elem),elem},root.onOpen&&"function"==typeof root.onOpen&&root.onOpen(),root},LGraphCanvas.getPropertyPrintableValue=function(value,values){if(!values)return String(value);if(values.constructor===Array)return String(value);if(values.constructor===Object){var k,desc_value="";for(k in values)if(values[k]==value){desc_value=k;break}return String(value)+" ("+desc_value+")"}},LGraphCanvas.prototype.closePanels=function(){var panel=document.querySelector("#node-panel");panel&&panel.close(),(panel=document.querySelector("#option-panel"))&&panel.close()},LGraphCanvas.prototype.showShowGraphOptionsPanel=function(refOpts,obEv,refMenu,refMenu2){if(this.constructor&&"HTMLDivElement"==this.constructor.name){if(!(obEv&&obEv.event&&obEv.event.target&&obEv.event.target.lgraphcanvas))return void console.warn("Canvas not found");var graphcanvas=obEv.event.target.lgraphcanvas}else graphcanvas=this;graphcanvas.closePanels();obEv=graphcanvas.getCanvasWindow();function fUpdate(name,value,options){options&&options.key&&(name=options.key),options.values&&(value=Object.values(options.values).indexOf(value)),graphcanvas[name]=value}panel=graphcanvas.createPanel("Options",{closable:!0,window:obEv,onOpen:function(){graphcanvas.OPTIONPANEL_IS_OPEN=!0},onClose:function(){graphcanvas.OPTIONPANEL_IS_OPEN=!1,graphcanvas.options_panel=null}}),(graphcanvas.options_panel=panel).id="option-panel",panel.classList.add("settings"),panel.content.innerHTML="";var pI,aProps=LiteGraph.availableCanvasOptions;for(pI in aProps.sort(),aProps){var pX=aProps[pI];panel.addWidget("boolean",pX,graphcanvas[pX],{key:pX,on:"True",off:"False"},fUpdate)}graphcanvas.links_render_mode,panel.addWidget("combo","Render mode",LiteGraph.LINK_RENDER_MODES[graphcanvas.links_render_mode],{key:"links_render_mode",values:LiteGraph.LINK_RENDER_MODES},fUpdate),panel.addSeparator(),panel.footer.innerHTML="",graphcanvas.canvas.parentNode.appendChild(panel)},LGraphCanvas.prototype.showShowNodePanel=function(node){this.SELECTED_NODE=node,this.closePanels();var ref_window=this.getCanvasWindow(),graphcanvas=this,panel=this.createPanel(node.title||"",{closable:!0,window:ref_window,onOpen:function(){graphcanvas.NODEPANEL_IS_OPEN=!0},onClose:function(){graphcanvas.NODEPANEL_IS_OPEN=!1,graphcanvas.node_panel=null}});function inner_refresh(){panel.content.innerHTML="",panel.addHTML(""+node.type+""+(node.constructor.desc||"")+""),panel.addHTML("

Properties

");function fUpdate(name,value){switch(graphcanvas.graph.beforeChange(node),name){case"Title":node.title=value;break;case"Mode":var kV=Object.values(LiteGraph.NODE_MODES).indexOf(value);0<=kV&&LiteGraph.NODE_MODES[kV]?node.changeMode(kV):console.warn("unexpected mode: "+value);break;case"Color":LGraphCanvas.node_colors[value]?(node.color=LGraphCanvas.node_colors[value].color,node.bgcolor=LGraphCanvas.node_colors[value].bgcolor):console.warn("unexpected color: "+value);break;default:node.setProperty(name,value)}graphcanvas.graph.afterChange(),graphcanvas.dirty_canvas=!0}panel.addWidget("string","Title",node.title,{},fUpdate),panel.addWidget("combo","Mode",LiteGraph.NODE_MODES[node.mode],{values:LiteGraph.NODE_MODES},fUpdate);var pName,nodeCol="";for(pName in void 0!==node.color&&(nodeCol=Object.keys(LGraphCanvas.node_colors).filter(function(nK){return LGraphCanvas.node_colors[nK].color==node.color})),panel.addWidget("combo","Color",nodeCol,{values:Object.keys(LGraphCanvas.node_colors)},fUpdate),node.properties){var value=node.properties[pName],info=node.getPropertyInfo(pName);info.type;node.onAddPropertyToPanel&&node.onAddPropertyToPanel(pName,panel)||panel.addWidget(info.widget||info.type,pName,value,info,fUpdate)}panel.addSeparator(),node.onShowCustomPanelInfo&&node.onShowCustomPanelInfo(panel),panel.footer.innerHTML="",panel.addButton("Delete",function(){node.block_delete||(node.graph.remove(node),panel.close())}).classList.add("delete")}(graphcanvas.node_panel=panel).id="node-panel",panel.node=node,panel.classList.add("settings"),panel.inner_showCodePad=function(propname){panel.classList.remove("settings"),panel.classList.add("centered"),panel.alt_content.innerHTML="";function fDoneWith(){panel.toggleAltContent(!1),panel.toggleFooterVisibility(!0),textarea.parentNode.removeChild(textarea),panel.classList.add("settings"),panel.classList.remove("centered"),inner_refresh()}var textarea=panel.alt_content.querySelector("textarea"),assign=(textarea.value=node.properties[propname],textarea.addEventListener("keydown",function(e){"Enter"==e.code&&e.ctrlKey&&(node.setProperty(propname,textarea.value),fDoneWith())}),panel.toggleAltContent(!0),panel.toggleFooterVisibility(!1),textarea.style.height="calc(100% - 40px)",panel.addButton("Assign",function(){node.setProperty(propname,textarea.value),fDoneWith()})),assign=(panel.alt_content.appendChild(assign),panel.addButton("Close",fDoneWith));assign.style.float="right",panel.alt_content.appendChild(assign)},inner_refresh(),this.canvas.parentNode.appendChild(panel)},LGraphCanvas.prototype.showSubgraphPropertiesDialog=function(node){console.log("showing subgraph properties dialog");var old_panel=this.canvas.parentNode.querySelector(".subgraph_dialog"),panel=(old_panel&&old_panel.close(),this.createPanel("Subgraph Inputs",{closable:!0,width:500}));function inner_refresh(){if(panel.clear(),node.inputs)for(var i=0;i","subgraph_property")).dataset.name=input.name,elem.dataset.slot=i,elem.querySelector(".name").innerText=input.name,elem.querySelector(".type").innerText=input.type,elem.querySelector("button").addEventListener("click",function(e){node.removeInput(Number(this.parentNode.dataset.slot)),inner_refresh()}))}}panel.node=node,panel.classList.add("subgraph_dialog");return panel.addHTML(" + NameType","subgraph_property extra",!0).querySelector("button").addEventListener("click",function(e){var elem=this.parentNode,name=elem.querySelector(".name").value,type=elem.querySelector(".type").value;name&&-1==node.findInputSlot(name)&&(node.addInput(name,type),elem.querySelector(".name").value="",elem.querySelector(".type").value="",inner_refresh())}),inner_refresh(),this.canvas.parentNode.appendChild(panel),panel},LGraphCanvas.prototype.showSubgraphPropertiesDialogRight=function(node){var old_panel=this.canvas.parentNode.querySelector(".subgraph_dialog"),panel=(old_panel&&old_panel.close(),this.createPanel("Subgraph Outputs",{closable:!0,width:500}));function inner_refresh(){if(panel.clear(),node.outputs)for(var i=0;i","subgraph_property")).dataset.name=input.name,elem.dataset.slot=i,elem.querySelector(".name").innerText=input.name,elem.querySelector(".type").innerText=input.type,elem.querySelector("button").addEventListener("click",function(e){node.removeOutput(Number(this.parentNode.dataset.slot)),inner_refresh()}))}}panel.node=node,panel.classList.add("subgraph_dialog");old_panel=panel.addHTML(" + NameType","subgraph_property extra",!0);function addOutput(){var elem=this.parentNode,name=elem.querySelector(".name").value,type=elem.querySelector(".type").value;name&&-1==node.findOutputSlot(name)&&(node.addOutput(name,type),elem.querySelector(".name").value="",elem.querySelector(".type").value="",inner_refresh())}return old_panel.querySelector(".name").addEventListener("keydown",function(e){13==e.keyCode&&addOutput.apply(this)}),old_panel.querySelector("button").addEventListener("click",function(e){addOutput.apply(this)}),inner_refresh(),this.canvas.parentNode.appendChild(panel),panel},LGraphCanvas.prototype.checkPanels=function(){if(this.canvas)for(var panels=this.canvas.parentNode.querySelectorAll(".litegraph.dialog"),i=0;iNo color"}),LGraphCanvas.node_colors){var color=LGraphCanvas.node_colors[i],value={value:i,content:""+i+""};values.push(value)}return new LiteGraph.ContextMenu(values,{event:e,callback:function(v){if(node){function fApplyColor(node){color?node.constructor===LiteGraph.LGraphGroup?node.color=color.groupcolor:(node.color=color.color,node.bgcolor=color.bgcolor):(delete node.color,delete node.bgcolor)}var color=v.value?LGraphCanvas.node_colors[v.value]:null,graphcanvas=LGraphCanvas.active_canvas;if(!graphcanvas.selected_nodes||Object.keys(graphcanvas.selected_nodes).length<=1)fApplyColor(node);else for(var i in graphcanvas.selected_nodes)fApplyColor(graphcanvas.selected_nodes[i]);node.setDirtyCanvas(!0,!0)}},parentMenu:menu,node:node}),!1},LGraphCanvas.onMenuNodeShapes=function(value,options,e,menu,node){if(node)return new LiteGraph.ContextMenu(LiteGraph.VALID_SHAPES,{event:e,callback:function(v){if(node){node.graph.beforeChange();function fApplyMultiNode(node){node.shape=v}var graphcanvas=LGraphCanvas.active_canvas;if(!graphcanvas.selected_nodes||Object.keys(graphcanvas.selected_nodes).length<=1)fApplyMultiNode(node);else for(var i in graphcanvas.selected_nodes)fApplyMultiNode(graphcanvas.selected_nodes[i]);node.graph.afterChange(),node.setDirtyCanvas(!0)}},parentMenu:menu,node:node}),!1;throw"no node passed"},LGraphCanvas.onMenuNodeRemove=function(value,options,e,menu,node){if(!node)throw"no node passed";function fApplyMultiNode(node){!1!==node.removable&&graph.remove(node)}var graph=node.graph,graphcanvas=(graph.beforeChange(),LGraphCanvas.active_canvas);if(!graphcanvas.selected_nodes||Object.keys(graphcanvas.selected_nodes).length<=1)fApplyMultiNode(node);else for(var i in graphcanvas.selected_nodes)fApplyMultiNode(graphcanvas.selected_nodes[i]);graph.afterChange(),node.setDirtyCanvas(!0,!0)},LGraphCanvas.onMenuNodeToSubgraph=function(value,options,e,menu,node){var nodes_list,subgraph_node,graph=node.graph,graphcanvas=LGraphCanvas.active_canvas;graphcanvas&&((nodes_list=Object.values(graphcanvas.selected_nodes||{})).length||(nodes_list=[node]),(subgraph_node=LiteGraph.createNode("graph/subgraph")).pos=node.pos.concat(),graph.add(subgraph_node),subgraph_node.buildFromNodes(nodes_list),graphcanvas.deselectAllNodes(),node.setDirtyCanvas(!0,!0))},LGraphCanvas.onMenuNodeClone=function(value,options,e,menu,node){node.graph.beforeChange();function fApplyMultiNode(node){var newnode;!1!==node.clonable&&(newnode=node.clone())&&(newnode.pos=[node.pos[0]+5,node.pos[1]+5],node.graph.add(newnode),newSelected[newnode.id]=newnode)}var newSelected={},graphcanvas=LGraphCanvas.active_canvas;if(!graphcanvas.selected_nodes||Object.keys(graphcanvas.selected_nodes).length<=1)fApplyMultiNode(node);else for(var i in graphcanvas.selected_nodes)fApplyMultiNode(graphcanvas.selected_nodes[i]);Object.keys(newSelected).length&&graphcanvas.selectNodes(newSelected),node.graph.afterChange(),node.setDirtyCanvas(!0,!0)},LGraphCanvas.node_colors={red:{color:"#322",bgcolor:"#533",groupcolor:"#A88"},brown:{color:"#332922",bgcolor:"#593930",groupcolor:"#b06634"},green:{color:"#232",bgcolor:"#353",groupcolor:"#8A8"},blue:{color:"#223",bgcolor:"#335",groupcolor:"#88A"},pale_blue:{color:"#2a363b",bgcolor:"#3f5159",groupcolor:"#3f789e"},cyan:{color:"#233",bgcolor:"#355",groupcolor:"#8AA"},purple:{color:"#323",bgcolor:"#535",groupcolor:"#a1309b"},yellow:{color:"#432",bgcolor:"#653",groupcolor:"#b58b2a"},black:{color:"#222",bgcolor:"#000",groupcolor:"#444"}},LGraphCanvas.prototype.getCanvasMenuOptions=function(){var extra,options=null;return this.getMenuOptions?options=this.getMenuOptions():(options=[{content:"Add Node",has_submenu:!0,callback:LGraphCanvas.onMenuAdd},{content:"Add Group",callback:LGraphCanvas.onGroupAdd}],1Name",options),(input=dialog.querySelector("input"))&&slot_info&&(input.value=slot_info.label||""),inner=function(){node.graph.beforeChange(),input.value&&(slot_info&&(slot_info.label=input.value),that.setDirty(!0)),dialog.close(),node.graph.afterChange()},dialog.querySelector("button").addEventListener("click",inner),input.addEventListener("keydown",function(e){if(dialog.is_modified=!0,27==e.keyCode)dialog.close();else if(13==e.keyCode)inner();else if(13!=e.keyCode&&"textarea"!=e.target.localName)return;e.preventDefault(),e.stopPropagation()}),input.focus()))},extra:node},slot=(node&&(options.title=node.type),null);node&&(slot=node.getSlotInPosition(event.canvasX,event.canvasY),LGraphCanvas.active_node=node),slot?(menu_info=[],node.getSlotMenuOptions?menu_info=node.getSlotMenuOptions(slot):(slot&&slot.output&&slot.output.links&&slot.output.links.length&&menu_info.push({content:"Disconnect Links",slot:slot}),(_slot=slot.input||slot.output).removable&&menu_info.push(_slot.locked?"Cannot remove":{content:"Remove Slot",slot:slot}),_slot.nameLocked||menu_info.push({content:"Rename Slot",slot:slot})),options.title=(slot.input||slot.output).type||"*",slot.input&&slot.input.type==LiteGraph.ACTION&&(options.title="Action"),slot.output&&slot.output.type==LiteGraph.EVENT&&(options.title="Event")):node?menu_info=this.getNodeMenuOptions(node):(menu_info=this.getCanvasMenuOptions(),(_slot=this.graph.getGroupOnPos(event.canvasX,event.canvasY))&&menu_info.push(null,{content:"Edit Group",has_submenu:!0,submenu:{title:"Group",extra:_slot,options:this.getGroupMenuOptions(_slot)}})),menu_info&&new LiteGraph.ContextMenu(menu_info,options,ref_window)},LiteGraph.compareObjects=compareObjects,LiteGraph.distance=distance,LiteGraph.colorToString=colorToString,LiteGraph.isInsideRectangle=isInsideRectangle,LiteGraph.growBounding=growBounding,LiteGraph.isInsideBounding=isInsideBounding,LiteGraph.overlapBounding=overlapBounding,LiteGraph.hex2num=hex2num,LiteGraph.num2hex=num2hex,ContextMenu.prototype.addItem=function(name,value,options){var that=this,element=(options=options||{},document.createElement("div")),disabled=!(element.className="litemenu-entry submenu");function inner_onclick(e){var value=this.value,close_parent=!0;if((that.current_submenu&&that.current_submenu.close(e),options.callback&&!0===options.callback.call(this,value,options,e,that,options.node)&&(close_parent=!1),value)&&(value.callback&&!options.ignore_item_callbacks&&!0!==value.disabled&&!0===value.callback.call(this,value,options,e,that,options.extra)&&(close_parent=!1),value.submenu)){if(!value.submenu.options)throw"ContextMenu submenu needs options";new that.constructor(value.submenu.options,{callback:value.submenu.callback,event:e,parentMenu:that,ignore_item_callbacks:value.submenu.ignore_item_callbacks,title:value.submenu.title,extra:value.submenu.extra,autoopen:options.autoopen});close_parent=!1}close_parent&&!that.lock&&that.close()}return null===value?element.classList.add("separator"):(element.innerHTML=value&&value.title?value.title:name,(element.value=value)&&(value.disabled&&(disabled=!0,element.classList.add("disabled")),value.submenu||value.has_submenu)&&element.classList.add("has_submenu"),"function"==typeof value?(element.dataset.value=name,element.onclick_callback=value):element.dataset.value=value,value.className&&(element.className+=" "+value.className)),this.root.appendChild(element),disabled||element.addEventListener("click",inner_onclick),!disabled&&options.autoopen&&LiteGraph.pointerListenerAdd(element,"enter",function(e){var value=this.value;value&&value.has_submenu&&inner_onclick.call(this,e)}),element},ContextMenu.prototype.close=function(e,ignore_parent_menu){this.root.parentNode&&this.root.parentNode.removeChild(this.root),this.parentMenu&&!ignore_parent_menu&&(this.parentMenu.lock=!1,this.parentMenu.current_submenu=null,void 0===e?this.parentMenu.close():e&&!ContextMenu.isCursorOverElement(e,this.parentMenu.root)&&ContextMenu.trigger(this.parentMenu.root,LiteGraph.pointerevents_method+"leave",e)),this.current_submenu&&this.current_submenu.close(e,!0),this.root.closing_timer&&clearTimeout(this.root.closing_timer)},ContextMenu.trigger=function(element,event_name,params,origin){var evt=document.createEvent("CustomEvent");return evt.initCustomEvent(event_name,!0,!0,params),evt.srcElement=origin,element.dispatchEvent?element.dispatchEvent(evt):element.__events&&element.__events.dispatchEvent(evt),evt},ContextMenu.prototype.getTopMenu=function(){return this.options.parentMenu?this.options.parentMenu.getTopMenu():this},ContextMenu.prototype.getFirstEvent=function(){return this.options.parentMenu?this.options.parentMenu.getFirstEvent():this.options.event},ContextMenu.isCursorOverElement=function(event,element){var left=event.clientX,event=event.clientY,element=element.getBoundingClientRect();return!!element&&event>element.top&&eventelement.left&&leftthis.size[0]+10||localpos[1]<-10||localpos[1]>this.size[1]+10)?(points.splice(s,1),this.selected=-1):(curvepos[0]=graphcanvas?0==s?0:1:clamp(x,0,1),curvepos[1]=1-clamp(y,0,1),points.sort(function(a,b){return a[0]-b[0]}),this.selected=points.indexOf(curvepos),this.must_update=!0)))},CurveEditor.prototype.onMouseUp=function(localpos,graphcanvas){return!(this.selected=-1)},CurveEditor.prototype.getCloserPoint=function(pos,max_dist){var points=this.points;if(!points)return-1;max_dist=max_dist||30;for(var w=this.size[0]-2*this.margin,h=this.size[1]-2*this.margin,num=points.length,p2=[0,0],min_dist=1e6,closest=-1,i=0;iy&&(localpos[0]idMap.linkIDs[l]))}for(const node of graph.nodes){var merge;"graph/subgraph"===node.type&&(merge=reassignGraphUUIDs(node.subgraph),idMap.nodeIDs.assign(merge.nodeIDs),idMap.linkIDs.assign(merge.linkIDs))}},Subgraph.prototype.clone=function(){var subgraph,node=LiteGraph.createNode(this.type),data=this.serialize();return LiteGraph.use_uuids&&(subgraph=LiteGraph.cloneObject(data.subgraph),this.reassignSubgraphUUIDs(subgraph),data.subgraph=subgraph),delete data.id,delete data.inputs,delete data.outputs,node.configure(data),node},Subgraph.prototype.buildFromNodes=function(nodes){for(var ids={},min_x=0,i=0;ithreshold?gamepad.xbox.axes.lx:0,this._left_axis[1]=Math.abs(gamepad.xbox.axes.ly)>threshold?gamepad.xbox.axes.ly:0,this._right_axis[0]=Math.abs(gamepad.xbox.axes.rx)>threshold?gamepad.xbox.axes.rx:0,this._right_axis[1]=Math.abs(gamepad.xbox.axes.ry)>threshold?gamepad.xbox.axes.ry:0,this._triggers[0]=Math.abs(gamepad.xbox.axes.ltrigger)>threshold?gamepad.xbox.axes.ltrigger:0,this._triggers[1]=Math.abs(gamepad.xbox.axes.rtrigger)>threshold?gamepad.xbox.axes.rtrigger:0),this.outputs)for(var i=0;i","enum",{values:MathCondition.values}),this.addWidget("combo","Cond.",this.properties.OP,{property:"OP",values:MathCondition.values}),this.size=[80,60]}function MathBranch(){this.addInput("in",0),this.addInput("cond","boolean"),this.addOutput("true",0),this.addOutput("false",0),this.size=[80,60]}function MathAccumulate(){this.addInput("inc","number"),this.addOutput("total","number"),this.addProperty("increment",1),this.addProperty("value",0)}function MathTrigonometry(){this.addInput("v","number"),this.addOutput("sin","number"),this.addProperty("amplitude",1),this.addProperty("offset",0),this.bgImageUrl="nodes/imgs/icon-sin.png"}function MathFormula(){this.addInput("x","number"),this.addInput("y","number"),this.addOutput("","number"),this.properties={x:1,y:1,formula:"x+y"},this.code_widget=this.addWidget("text","F(x,y)",this.properties.formula,function(v,canvas,node){node.properties.formula=v}),this.addWidget("toggle","allow",LiteGraph.allow_scripts,function(v){LiteGraph.allow_scripts=v}),this._func=null}function Math3DVec2ToXY(){this.addInput("vec2","vec2"),this.addOutput("x","number"),this.addOutput("y","number")}function Math3DXYToVec2(){this.addInputs([["x","number"],["y","number"]]),this.addOutput("vec2","vec2"),this.properties={x:0,y:0},this._data=new Float32Array(2)}function Math3DVec3ToXYZ(){this.addInput("vec3","vec3"),this.addOutput("x","number"),this.addOutput("y","number"),this.addOutput("z","number")}function Math3DXYZToVec3(){this.addInputs([["x","number"],["y","number"],["z","number"]]),this.addOutput("vec3","vec3"),this.properties={x:0,y:0,z:0},this._data=new Float32Array(3)}function Math3DVec4ToXYZW(){this.addInput("vec4","vec4"),this.addOutput("x","number"),this.addOutput("y","number"),this.addOutput("z","number"),this.addOutput("w","number")}function Math3DXYZWToVec4(){this.addInputs([["x","number"],["y","number"],["z","number"],["w","number"]]),this.addOutput("vec4","vec4"),this.properties={x:0,y:0,z:0,w:0},this._data=new Float32Array(4)}Converter.title="Converter",Converter.desc="type A to type B",Converter.prototype.onExecute=function(){var v=this.getInputData(0);if(null!=v&&this.outputs)for(var i=0;inum_samples&&(this._current=0),0),i=0;iB":value=B=B":value=B<=A}this.setOutputData(i,value)}}},MathCompare.prototype.onGetOutputs=function(){return[["A==B","boolean"],["A!=B","boolean"],["A>B","boolean"],["A=B","boolean"],["A<=B","boolean"]]},LiteGraph.registerNodeType("math/compare",MathCompare),LiteGraph.registerSearchboxExtra("math/compare","==",{outputs:[["A==B","boolean"]],title:"A==B"}),LiteGraph.registerSearchboxExtra("math/compare","!=",{outputs:[["A!=B","boolean"]],title:"A!=B"}),LiteGraph.registerSearchboxExtra("math/compare",">",{outputs:[["A>B","boolean"]],title:"A>B"}),LiteGraph.registerSearchboxExtra("math/compare","<",{outputs:[["A=",{outputs:[["A>=B","boolean"]],title:"A>=B"}),LiteGraph.registerSearchboxExtra("math/compare","<=",{outputs:[["A<=B","boolean"]],title:"A<=B"}),MathCondition["@OP"]={type:"enum",title:"operation",values:MathCondition.values=[">","<","==","!=","<=",">=","||","&&"]},MathCondition.title="Condition",MathCondition.desc="evaluates condition between A and B",MathCondition.prototype.getTitle=function(){return"A "+this.properties.OP+" B"},MathCondition.prototype.onExecute=function(){var A=this.getInputData(0),B=(void 0===A?A=this.properties.A:this.properties.A=A,this.getInputData(1)),result=(void 0===B?B=this.properties.B:this.properties.B=B,!0);switch(this.properties.OP){case">":result=B=":result=B<=A;break;case"||":result=A||B;break;case"&&":result=A&&B}this.setOutputData(0,result),this.setOutputData(1,!result)},LiteGraph.registerNodeType("math/condition",MathCondition),MathBranch.title="Branch",MathBranch.desc="If condition is true, outputs IN in true, otherwise in false",MathBranch.prototype.onExecute=function(){var V=this.getInputData(0);this.getInputData(1)?(this.setOutputData(0,V),this.setOutputData(1,null)):(this.setOutputData(0,null),this.setOutputData(1,V))},LiteGraph.registerNodeType("math/branch",MathBranch),MathAccumulate.title="Accumulate",MathAccumulate.desc="Increments a value every time",MathAccumulate.prototype.onExecute=function(){null===this.properties.value&&(this.properties.value=0);var inc=this.getInputData(0);this.properties.value+=null!==inc?inc:this.properties.increment,this.setOutputData(0,this.properties.value)},LiteGraph.registerNodeType("math/accumulate",MathAccumulate),MathTrigonometry.title="Trigonometry",MathTrigonometry.desc="Sin Cos Tan",MathTrigonometry.prototype.onExecute=function(){var v=this.getInputData(0),amplitude=(null==v&&(v=0),this.properties.amplitude),slot=this.findInputSlot("amplitude"),offset=(-1!=slot&&(amplitude=this.getInputData(slot)),this.properties.offset);-1!=(slot=this.findInputSlot("offset"))&&(offset=this.getInputData(slot));for(var value,i=0,l=this.outputs.length;iXY",Math3DVec2ToXY.desc="vector 2 to components",Math3DVec2ToXY.prototype.onExecute=function(){var v=this.getInputData(0);null!=v&&(this.setOutputData(0,v[0]),this.setOutputData(1,v[1]))},LiteGraph.registerNodeType("math3d/vec2-to-xy",Math3DVec2ToXY),Math3DXYToVec2.title="XY->Vec2",Math3DXYToVec2.desc="components to vector2",Math3DXYToVec2.prototype.onExecute=function(){var x=this.getInputData(0),y=(null==x&&(x=this.properties.x),this.getInputData(1)),data=(null==y&&(y=this.properties.y),this._data);data[0]=x,data[1]=y,this.setOutputData(0,data)},LiteGraph.registerNodeType("math3d/xy-to-vec2",Math3DXYToVec2),Math3DVec3ToXYZ.title="Vec3->XYZ",Math3DVec3ToXYZ.desc="vector 3 to components",Math3DVec3ToXYZ.prototype.onExecute=function(){var v=this.getInputData(0);null!=v&&(this.setOutputData(0,v[0]),this.setOutputData(1,v[1]),this.setOutputData(2,v[2]))},LiteGraph.registerNodeType("math3d/vec3-to-xyz",Math3DVec3ToXYZ),Math3DXYZToVec3.title="XYZ->Vec3",Math3DXYZToVec3.desc="components to vector3",Math3DXYZToVec3.prototype.onExecute=function(){var x=this.getInputData(0),y=(null==x&&(x=this.properties.x),this.getInputData(1)),z=(null==y&&(y=this.properties.y),this.getInputData(2)),data=(null==z&&(z=this.properties.z),this._data);data[0]=x,data[1]=y,data[2]=z,this.setOutputData(0,data)},LiteGraph.registerNodeType("math3d/xyz-to-vec3",Math3DXYZToVec3),Math3DVec4ToXYZW.title="Vec4->XYZW",Math3DVec4ToXYZW.desc="vector 4 to components",Math3DVec4ToXYZW.prototype.onExecute=function(){var v=this.getInputData(0);null!=v&&(this.setOutputData(0,v[0]),this.setOutputData(1,v[1]),this.setOutputData(2,v[2]),this.setOutputData(3,v[3]))},LiteGraph.registerNodeType("math3d/vec4-to-xyzw",Math3DVec4ToXYZW),Math3DXYZWToVec4.title="XYZW->Vec4",Math3DXYZWToVec4.desc="components to vector4",Math3DXYZWToVec4.prototype.onExecute=function(){var x=this.getInputData(0),y=(null==x&&(x=this.properties.x),this.getInputData(1)),z=(null==y&&(y=this.properties.y),this.getInputData(2)),w=(null==z&&(z=this.properties.z),this.getInputData(3)),data=(null==w&&(w=this.properties.w),this._data);data[0]=x,data[1]=y,data[2]=z,data[3]=w,this.setOutputData(0,data)},LiteGraph.registerNodeType("math3d/xyzw-to-vec4",Math3DXYZWToVec4)}(this); + +!function(global){global=global.LiteGraph;function StringToTable(){this.addInput("","string"),this.addOutput("table","table"),this.addOutput("rows","number"),this.addProperty("value",""),this.addProperty("separator",","),this._table=null}global.wrapFunctionAsNode("string/toString",function(a){if(a&&a.constructor===Object)try{return JSON.stringify(a)}catch(err){}return String(a)},[""],"string"),global.wrapFunctionAsNode("string/compare",function(a,b){return a==b},["string","string"],"boolean"),global.wrapFunctionAsNode("string/concatenate",function(a,b){return void 0===a?b:void 0===b?a:a+b},["string","string"],"string"),global.wrapFunctionAsNode("string/contains",function(a,b){return void 0!==a&&void 0!==b&&-1!=a.indexOf(b)},["string","string"],"boolean"),global.wrapFunctionAsNode("string/toUpperCase",function(a){return null!=a&&a.constructor===String?a.toUpperCase():a},["string"],"string"),global.wrapFunctionAsNode("string/split",function(str,separator){if(null==separator&&(separator=this.properties.separator),null==str)return[];if(str.constructor===String)return str.split(separator||" ");if(str.constructor!==Array)return null;for(var r=[],i=0;i{if(resp.ok)return this.boxcolor="#0F0",resp.text();this.boxcolor="#F00",that.trigger("error")}).then(data=>{that._data=data,that._fetching=null,that.trigger("ready")}))},HTTPRequestNode.prototype.onAction=function(evt){"request"==evt&&this.fetch()},HTTPRequestNode.prototype.onExecute=function(){this.setOutputData(1,this._data)},HTTPRequestNode.prototype.onGetOutputs=function(){return[["error",LiteGraph.EVENT]]},LiteGraph.registerNodeType("network/httprequest",HTTPRequestNode)}(this); + diff --git a/build/litegraph_mini.min.js b/build/litegraph_mini.min.js deleted file mode 100644 index 67b223fa8..000000000 --- a/build/litegraph_mini.min.js +++ /dev/null @@ -1,495 +0,0 @@ -var $jscomp=$jscomp||{};$jscomp.scope={};$jscomp.ASSUME_ES5=!1;$jscomp.ASSUME_NO_NATIVE_MAP=!1;$jscomp.ASSUME_NO_NATIVE_SET=!1;$jscomp.defineProperty=$jscomp.ASSUME_ES5||"function"==typeof Object.defineProperties?Object.defineProperty:function(w,m,r){w!=Array.prototype&&w!=Object.prototype&&(w[m]=r.value)};$jscomp.getGlobal=function(w){return"undefined"!=typeof window&&window===w?w:"undefined"!=typeof global&&null!=global?global:w};$jscomp.global=$jscomp.getGlobal(this);$jscomp.SYMBOL_PREFIX="jscomp_symbol_"; -$jscomp.initSymbol=function(){$jscomp.initSymbol=function(){};$jscomp.global.Symbol||($jscomp.global.Symbol=$jscomp.Symbol)};$jscomp.Symbol=function(){var w=0;return function(m){return $jscomp.SYMBOL_PREFIX+(m||"")+w++}}(); -$jscomp.initSymbolIterator=function(){$jscomp.initSymbol();var w=$jscomp.global.Symbol.iterator;w||(w=$jscomp.global.Symbol.iterator=$jscomp.global.Symbol("iterator"));"function"!=typeof Array.prototype[w]&&$jscomp.defineProperty(Array.prototype,w,{configurable:!0,writable:!0,value:function(){return $jscomp.arrayIterator(this)}});$jscomp.initSymbolIterator=function(){}};$jscomp.arrayIterator=function(w){var m=0;return $jscomp.iteratorPrototype(function(){return mr&&(r=Math.max(0,y+r));if(null==h||h>y)h=y;h=Number(h);0>h&&(h=Math.max(0,y+h));for(r=Number(r||0);r=z}},"es6","es3");$jscomp.findInternal=function(w,m,r){w instanceof String&&(w=String(w));for(var h=w.length,y=0;ya&&eb?!0:!1}function D(a,b){var c=a[0]+a[2],e=a[1]+a[3],d=b[1]+b[3];return a[0]>b[0]+b[2]||a[1]>d||ck.width-n.width-10&&(d=k.width-n.width-10),k.height&&a>k.height-n.height-10&&(a=k.height-n.height-10));g.style.left=d+"px";g.style.top=a+"px";b.scale&&(g.style.transform="scale("+b.scale+")")}function H(a){this.points=a;this.nearest=this.selected=-1;this.size=null;this.must_update=!0;this.margin=5}function I(a,b,c){return b>a?b:c>a/4).toString(16)})},isValidConnection:function(a, -b){if(""==a||"*"===a)a=0;if(""==b||"*"===b)b=0;if(!a||!b||a==b||a==f.EVENT&&b==f.ACTION)return!0;a=String(a);b=String(b);a=a.toLowerCase();b=b.toLowerCase();if(-1==a.indexOf(",")&&-1==b.indexOf(","))return a==b;a=a.split(",");b=b.split(",");for(var c=0;ck&&(k=p.size[q]);n+=p.size[b==f.VERTICAL_LAYOUT?0:1]+a+f.NODE_TITLE_HEIGHT}c+=k+a}this.setDirtyCanvas(!0,!0)};m.prototype.getTime=function(){return this.globaltime};m.prototype.getFixedTime=function(){return this.fixedtime};m.prototype.getElapsedTime=function(){return this.elapsed_time};m.prototype.sendEventToAllNodes=function(a, -b,c){c=c||f.ALWAYS;var e=this._nodes_in_order?this._nodes_in_order:this._nodes;if(e)for(var d=0,g=e.length;d=f.MAX_NUMBER_OF_NODES)throw"LiteGraph: max number of nodes in a graph reached";if(f.use_uuids){if(null==a.id||-1==a.id)a.id=f.uuidv4()}else null==a.id||-1==a.id? -a.id=++this.last_node_id:this.last_node_ida.length||(this._pos[0]=a[0],this._pos[1]=a[1])},get:function(){return this._pos},enumerable:!0});this.id=f.use_uuids?f.uuidv4():-1;this.type=null;this.inputs=[];this.outputs=[];this.connections=[];this.properties={};this.properties_info=[];this.flags={}};h.prototype.configure=function(a){this.graph&& -this.graph._version++;for(var b in a)if("properties"==b)for(var c in a.properties){if(this.properties[c]=a.properties[c],this.onPropertyChanged)this.onPropertyChanged(c,a.properties[c])}else null!=a[b]&&("object"==typeof a[b]?this[b]&&this[b].configure?this[b].configure(a[b]):this[b]=f.cloneObject(a[b],this[b]):this[b]=a[b]);a.title||(this.title=this.constructor.title);if(this.inputs)for(c=0;c=this.outputs.length)){var c=this.outputs[a];if(c&&(c._data=b,this.outputs[a].links))for(c=0;c=this.outputs.length)){var c=this.outputs[a];if(c&&(c.type=b,this.outputs[a].links))for(c=0;c=this.inputs.length||null==this.inputs[a].link)){a=this.graph.links[this.inputs[a].link];if(!a)return null;if(!b)return a.data;b=this.graph.getNodeById(a.origin_id);if(!b)return a.data;if(b.updateOutputData)b.updateOutputData(a.origin_slot);else if(b.onExecute)b.onExecute();return a.data}};h.prototype.getInputDataType=function(a){if(!this.inputs||a>=this.inputs.length||null==this.inputs[a].link)return null;a=this.graph.links[this.inputs[a].link];if(!a)return null; -var b=this.graph.getNodeById(a.origin_id);return b?(a=b.outputs[a.origin_slot])?a.type:null:a.type};h.prototype.getInputDataByName=function(a,b){a=this.findInputSlot(a);return-1==a?null:this.getInputData(a,b)};h.prototype.isInputConnected=function(a){return this.inputs?a=this.inputs.length)return null;a=this.inputs[a];return a&&null!==a.link?(a=this.graph.links[a.link])?this.graph.getNodeById(a.origin_id):null:null};h.prototype.getInputOrProperty=function(a){if(!this.inputs||!this.inputs.length)return this.properties?this.properties[a]:null;for(var b=0,c=this.inputs.length;b=this.outputs.length?null:this.outputs[a]._data};h.prototype.getOutputInfo=function(a){return this.outputs?a=this.outputs.length)return null;a=this.outputs[a];if(!a.links||0==a.links.length)return null;for(var b=[],c=0;ca&&this.pos[1]-d-cb)return!0;return!1};h.prototype.getSlotInPosition=function(a,b){var c=new Float32Array(2);if(this.inputs)for(var e=0,d=this.inputs.length;e=this.outputs.length)return f.debug&&console.log("Connect: Error, slot number not found"),null;b&&b.constructor=== -Number&&(b=this.graph.getNodeById(b));if(!b)throw"target node is null";if(b==this)return null;if(c.constructor===String){if(c=b.findInputSlot(c),-1==c)return f.debug&&console.log("Connect: Error, no slot of name "+c),null}else if(c===f.EVENT)if(f.do_add_triggers_slots)b.changeMode(f.ON_TRIGGER),c=b.findInputSlot("onTrigger");else return null;else if(!b.inputs||c>=b.inputs.length)return f.debug&&console.log("Connect: Error, slot number not found"),null;var e=b.inputs[c],d=this.outputs[a];if(!this.outputs[a])return null; -b.onBeforeConnectInput&&(c=b.onBeforeConnectInput(c));if(!1===c||null===c||!f.isValidConnection(d.type,e.type))return this.setDirtyCanvas(!1,!0),null;if(b.onConnectInput&&!1===b.onConnectInput(c,d.type,d,this,a)||this.onConnectOutput&&!1===this.onConnectOutput(a,e.type,e,b,c))return null;b.inputs[c]&&null!=b.inputs[c].link&&(this.graph.beforeChange(),b.disconnectInput(c,{doProcessChange:!1}));if(null!==d.links&&d.links.length)switch(d.type){case f.EVENT:f.allow_multi_output_for_events||(this.graph.beforeChange(), -this.disconnectOutput(a,!1,{doProcessChange:!1}))}var g=f.use_uuids?f.uuidv4():++this.graph.last_link_id;g=new r(g,e.type||d.type,this.id,a,b.id,c);this.graph.links[g.id]=g;null==d.links&&(d.links=[]);d.links.push(g.id);b.inputs[c].link=g.id;this.graph&&this.graph._version++;if(this.onConnectionsChange)this.onConnectionsChange(f.OUTPUT,a,!0,g,d);if(b.onConnectionsChange)b.onConnectionsChange(f.INPUT,c,!0,g,e);this.graph&&this.graph.onNodeConnectionChange&&(this.graph.onNodeConnectionChange(f.INPUT, -b,c,this,a),this.graph.onNodeConnectionChange(f.OUTPUT,this,a,b,c));this.setDirtyCanvas(!1,!0);this.graph.afterChange();this.graph.connectionChange(this,g);return g};h.prototype.disconnectOutput=function(a,b){if(a.constructor===String){if(a=this.findOutputSlot(a),-1==a)return f.debug&&console.log("Connect: Error, no slot of name "+a),!1}else if(!this.outputs||a>=this.outputs.length)return f.debug&&console.log("Connect: Error, slot number not found"),!1;var c=this.outputs[a];if(!c||!c.links||0==c.links.length)return!1; -if(b){b.constructor===Number&&(b=this.graph.getNodeById(b));if(!b)throw"Target Node not found";for(var e=0,d=c.links.length;e=this.inputs.length)return f.debug&&console.log("Connect: Error, slot number not found"),!1;var b=this.inputs[a];if(!b)return!1;var c=this.inputs[a].link;if(null!=c){this.inputs[a].link=null;var e=this.graph.links[c];if(e){var d=this.graph.getNodeById(e.origin_id);if(!d)return!1;var g=d.outputs[e.origin_slot];if(!g||!g.links||0==g.links.length)return!1;for(var k=0,n=g.links.length;kb&&this.inputs[b].pos)return c[0]=this.pos[0]+this.inputs[b].pos[0],c[1]=this.pos[1]+ -this.inputs[b].pos[1],c;if(!a&&e>b&&this.outputs[b].pos)return c[0]=this.pos[0]+this.outputs[b].pos[0],c[1]=this.pos[1]+this.outputs[b].pos[1],c;if(this.horizontal)return c[0]=this.pos[0]+this.size[0]/e*(b+.5),c[1]=a?this.pos[1]-f.NODE_TITLE_HEIGHT:this.pos[1]+this.size[1],c;c[0]=a?this.pos[0]+d:this.pos[0]+this.size[0]+1-d;c[1]=this.pos[1]+(b+.7)*f.NODE_SLOT_HEIGHT+(this.constructor.slot_start_y||0);return c};h.prototype.alignToGrid=function(){this.pos[0]=f.CANVAS_GRID_SIZE*Math.round(this.pos[0]/ -f.CANVAS_GRID_SIZE);this.pos[1]=f.CANVAS_GRID_SIZE*Math.round(this.pos[1]/f.CANVAS_GRID_SIZE)};h.prototype.trace=function(a){this.console||(this.console=[]);this.console.push(a);this.console.length>h.MAX_CONSOLE&&this.console.shift();if(this.graph.onNodeTrace)this.graph.onNodeTrace(this,a)};h.prototype.setDirtyCanvas=function(a,b){this.graph&&this.graph.sendActionToCanvas("setDirty",[a,b])};h.prototype.loadImage=function(a){var b=new Image;b.src=f.node_images_path+a;b.ready=!1;var c=this;b.onload= -function(){this.ready=!0;c.setDirtyCanvas(!0)};return b};h.prototype.captureInput=function(a){if(this.graph&&this.graph.list_of_graphcanvas)for(var b=this.graph.list_of_graphcanvas,c=0;ca.length||(this._pos[0]=a[0],this._pos[1]=a[1])},get:function(){return this._pos},enumerable:!0});Object.defineProperty(this,"size",{set:function(a){!a||2>a.length||(this._size[0]=Math.max(140,a[0]),this._size[1]=Math.max(80,a[1]))},get:function(){return this._size},enumerable:!0})};y.prototype.configure=function(a){this.title=a.title;this._bounding.set(a.bounding);this.color=a.color;this.font_size=a.font_size};y.prototype.serialize=function(){var a= -this._bounding;return{title:this.title,bounding:[Math.round(a[0]),Math.round(a[1]),Math.round(a[2]),Math.round(a[3])],color:this.color,font_size:this.font_size}};y.prototype.move=function(a,b,c){this._pos[0]+=a;this._pos[1]+=b;if(!c)for(c=0;c=this.viewport[0]&&e=this.viewport[1]&&cthis.max_scale&&(a=this.max_scale);if(a!=this.scale&&this.element){var c=this.element.getBoundingClientRect();if(c&&(b= -b||[.5*c.width,.5*c.height],c=this.convertCanvasToOffset(b),this.scale=a,.01>Math.abs(this.scale-1)&&(this.scale=1),a=this.convertCanvasToOffset(b),a=[a[0]-c[0],a[1]-c[1]],this.offset[0]+=a[0],this.offset[1]+=a[1],this.onredraw))this.onredraw(this)}};z.prototype.changeDeltaScale=function(a,b){this.changeScale(this.scale*a,b)};z.prototype.reset=function(){this.scale=1;this.offset[0]=0;this.offset[1]=0};w.LGraphCanvas=f.LGraphCanvas=l;l.DEFAULT_BACKGROUND_IMAGE="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAIAAAD/gAIDAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAQBJREFUeNrs1rEKwjAUhlETUkj3vP9rdmr1Ysammk2w5wdxuLgcMHyptfawuZX4pJSWZTnfnu/lnIe/jNNxHHGNn//HNbbv+4dr6V+11uF527arU7+u63qfa/bnmh8sWLBgwYJlqRf8MEptXPBXJXa37BSl3ixYsGDBMliwFLyCV/DeLIMFCxYsWLBMwSt4Be/NggXLYMGCBUvBK3iNruC9WbBgwYJlsGApeAWv4L1ZBgsWLFiwYJmCV/AK3psFC5bBggULloJX8BpdwXuzYMGCBctgwVLwCl7Be7MMFixYsGDBsu8FH1FaSmExVfAxBa/gvVmwYMGCZbBg/W4vAQYA5tRF9QYlv/QAAAAASUVORK5CYII="; -l.link_type_colors={"-1":f.EVENT_LINK_COLOR,number:"#AAA",node:"#DCA"};l.gradients={};l.prototype.clear=function(){this.fps=this.render_time=this.last_draw_time=this.frame=0;this.dragging_rectangle=null;this.selected_nodes={};this.selected_group=null;this.visible_nodes=[];this.connecting_node=this.node_capturing_input=this.node_over=this.node_dragged=null;this.highlighted_links={};this.dragging_canvas=!1;this.dirty_bgcanvas=this.dirty_canvas=!0;this.node_widget=this.node_in_panel=this.dirty_area= -null;this.last_mouse=[0,0];this.last_mouseclick=0;this.pointer_is_double=this.pointer_is_down=!1;this.visible_area.set([0,0,0,0]);if(this.onClear)this.onClear()};l.prototype.setGraph=function(a,b){this.graph!=a&&(b||this.clear(),!a&&this.graph?this.graph.detachCanvas(this):(a.attachCanvas(this),this._graph_stack&&(this._graph_stack=null),this.setDirty(!0,!0)))};l.prototype.getTopGraph=function(){return this._graph_stack.length?this._graph_stack[0]:this.graph};l.prototype.openSubgraph=function(a){if(!a)throw"graph cannot be null"; -if(this.graph==a)throw"graph cannot be the same";this.clear();this.graph&&(this._graph_stack||(this._graph_stack=[]),this._graph_stack.push(this.graph));a.attachCanvas(this);this.checkPanels();this.setDirty(!0,!0)};l.prototype.closeSubgraph=function(){if(this._graph_stack&&0!=this._graph_stack.length){var a=this.graph._subgraph_node,b=this._graph_stack.pop();this.selected_nodes={};this.highlighted_links={};b.attachCanvas(this);this.setDirty(!0,!0);a&&(this.centerOnNode(a),this.selectNodes([a]));this.ds.offset= -[0,0];this.ds.scale=1}};l.prototype.getCurrentGraph=function(){return this.graph};l.prototype.setCanvas=function(a,b){if(a&&a.constructor===String&&(a=document.getElementById(a),!a))throw"Error creating LiteGraph canvas: Canvas not found";if(a!==this.canvas&&(!a&&this.canvas&&(b||this.unbindEvents()),this.canvas=a,this.ds.element=a)){a.className+=" lgraphcanvas";a.data=this;a.tabindex="1";this.bgcanvas=null;this.bgcanvas||(this.bgcanvas=document.createElement("canvas"),this.bgcanvas.width=this.canvas.width, -this.bgcanvas.height=this.canvas.height);if(null==a.getContext){if("canvas"!=a.localName)throw"Element supplied for LGraphCanvas must be a element, you passed a "+a.localName;throw"This browser doesn't support Canvas";}null==(this.ctx=a.getContext("2d"))&&(a.webgl_enabled||console.warn("This canvas seems to be WebGL, enabling WebGL renderer"),this.enableWebGL());b||this.bindEvents()}};l.prototype._doNothing=function(a){a.preventDefault();return!1};l.prototype._doReturnTrue=function(a){a.preventDefault(); -return!0};l.prototype.bindEvents=function(){if(this._events_binded)console.warn("LGraphCanvas: events already binded");else{var a=this.canvas,b=this.getCanvasWindow().document;this._mousedown_callback=this.processMouseDown.bind(this);this._mousewheel_callback=this.processMouseWheel.bind(this);this._mousemove_callback=this.processMouseMove.bind(this);this._mouseup_callback=this.processMouseUp.bind(this);f.pointerListenerAdd(a,"down",this._mousedown_callback,!0);a.addEventListener("mousewheel",this._mousewheel_callback, -!1);f.pointerListenerAdd(a,"up",this._mouseup_callback,!0);f.pointerListenerAdd(a,"move",this._mousemove_callback);a.addEventListener("contextmenu",this._doNothing);a.addEventListener("DOMMouseScroll",this._mousewheel_callback,!1);this._key_callback=this.processKey.bind(this);a.addEventListener("keydown",this._key_callback,!0);b.addEventListener("keyup",this._key_callback,!0);this._ondrop_callback=this.processDrop.bind(this);a.addEventListener("dragover",this._doNothing,!1);a.addEventListener("dragend", -this._doNothing,!1);a.addEventListener("drop",this._ondrop_callback,!1);a.addEventListener("dragenter",this._doReturnTrue,!1);this._events_binded=!0}};l.prototype.unbindEvents=function(){if(this._events_binded){var a=this.getCanvasWindow().document;f.pointerListenerRemove(this.canvas,"move",this._mousedown_callback);f.pointerListenerRemove(this.canvas,"up",this._mousedown_callback);f.pointerListenerRemove(this.canvas,"down",this._mousedown_callback);this.canvas.removeEventListener("mousewheel",this._mousewheel_callback); -this.canvas.removeEventListener("DOMMouseScroll",this._mousewheel_callback);this.canvas.removeEventListener("keydown",this._key_callback);a.removeEventListener("keyup",this._key_callback);this.canvas.removeEventListener("contextmenu",this._doNothing);this.canvas.removeEventListener("drop",this._ondrop_callback);this.canvas.removeEventListener("dragenter",this._doReturnTrue);this._ondrop_callback=this._key_callback=this._mousewheel_callback=this._mousedown_callback=null;this._events_binded=!1}else console.warn("LGraphCanvas: no events binded")}; -l.getFileExtension=function(a){var b=a.indexOf("?");-1!=b&&(a=a.substr(0,b));b=a.lastIndexOf(".");return-1==b?"":a.substr(b+1).toLowerCase()};l.prototype.enableWebGL=function(){if("undefined"===typeof GL)throw"litegl.js must be included to use a WebGL canvas";if("undefined"===typeof enableWebGLCanvas)throw"webglCanvas.js must be included to use this feature";this.gl=this.ctx=enableWebGLCanvas(this.canvas);this.ctx.webgl=!0;this.bgcanvas=this.canvas;this.bgctx=this.gl;this.canvas.webgl_enabled=!0}; -l.prototype.setDirty=function(a,b){a&&(this.dirty_canvas=!0);b&&(this.dirty_bgcanvas=!0)};l.prototype.getCanvasWindow=function(){if(!this.canvas)return window;var a=this.canvas.ownerDocument;return a.defaultView||a.parentWindow};l.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))};l.prototype.stopRendering=function(){this.is_rendering= -!1};l.prototype.blockClick=function(){this.block_click=!0;this.last_mouseclick=0};l.prototype.processMouseDown=function(a){this.set_canvas_dirty_on_mouse_event&&(this.dirty_canvas=!0);if(this.graph){this.adjustMouseEvent(a);var b=this.getCanvasWindow();l.active_canvas=this;var c=this,e=a.clientX,d=a.clientY;this.ds.viewport=this.viewport;e=!this.viewport||this.viewport&&e>=this.viewport[0]&&e=this.viewport[1]&&dd-this.last_mouseclick&&k;this.mouse[0]=a.clientX;this.mouse[1]=a.clientY;this.graph_mouse[0]=a.canvasX;this.graph_mouse[1]=a.canvasY;this.last_click_position= -[this.mouse[0],this.mouse[1]];this.pointer_is_double=this.pointer_is_down&&k?!0:!1;this.pointer_is_down=!0;this.canvas.focus();f.closeAllContextMenus(b);if(!this.onMouse||1!=this.onMouse(a)){if(1!=a.which||this.pointer_is_double)if(2==a.which)if(f.middle_click_slot_add_default_node){if(g&&this.allow_interaction&&!e&&!this.read_only&&!this.connecting_node&&!g.flags.collapsed&&!this.live_mode){d=e=k=!1;if(g.outputs)for(q=0,n=g.outputs.length;qg.size[0]-f.NODE_TITLE_HEIGHT&& -0>n[1]&&(c=this,setTimeout(function(){c.openSubgraph(g.subgraph)},10)),this.live_mode&&(q=k=!0));q?g.is_selected||this.processNodeSelected(g,a):(this.allow_dragnodes&&(this.graph.beforeChange(),this.node_dragged=g),this.processNodeSelected(g,a));this.dirty_canvas=!0}}else if(!e){if(!this.read_only)for(q=0;qn[0]+4||a.canvasYn[1]+4)){this.showLinkMenu(k,a);this.over_link_center=null; -break}this.selected_group=this.graph.getGroupOnPos(a.canvasX,a.canvasY);this.selected_group_resizing=!1;this.selected_group&&!this.read_only&&(a.ctrlKey&&(this.dragging_rectangle=null),10>C([a.canvasX,a.canvasY],[this.selected_group.pos[0]+this.selected_group.size[0],this.selected_group.pos[1]+this.selected_group.size[1]])*this.ds.scale?this.selected_group_resizing=!0:this.selected_group.recomputeInsideNodes());d&&!this.read_only&&this.allow_searchbox&&(this.showSearchBox(a),a.preventDefault(),a.stopPropagation()); -k=!0}!e&&k&&this.allow_dragcanvas&&(this.dragging_canvas=!0)}this.last_mouse[0]=a.clientX;this.last_mouse[1]=a.clientY;this.last_mouseclick=f.getTime();this.last_mouse_dragging=!0;this.graph.change();(!b.document.activeElement||"input"!=b.document.activeElement.nodeName.toLowerCase()&&"textarea"!=b.document.activeElement.nodeName.toLowerCase())&&a.preventDefault();a.stopPropagation();if(this.onMouseDown)this.onMouseDown(a);return!1}}}};l.prototype.processMouseMove=function(a){this.autoresize&&this.resize(); -this.set_canvas_dirty_on_mouse_event&&(this.dirty_canvas=!0);if(this.graph){l.active_canvas=this;this.adjustMouseEvent(a);var b=[a.clientX,a.clientY];this.mouse[0]=b[0];this.mouse[1]=b[1];var c=[b[0]-this.last_mouse[0],b[1]-this.last_mouse[1]];this.last_mouse=b;this.graph_mouse[0]=a.canvasX;this.graph_mouse[1]=a.canvasY;if(this.block_click)return a.preventDefault(),!1;a.dragging=this.last_mouse_dragging;this.node_widget&&(this.processNodeWidgets(this.node_widget[0],this.graph_mouse,a,this.node_widget[1]), -this.dirty_canvas=!0);var e=this.graph.getNodeOnPos(a.canvasX,a.canvasY,this.visible_nodes);if(this.dragging_rectangle)this.dragging_rectangle[2]=a.canvasX-this.dragging_rectangle[0],this.dragging_rectangle[3]=a.canvasY-this.dragging_rectangle[1],this.dirty_canvas=!0;else if(this.selected_group&&!this.read_only)this.selected_group_resizing?this.selected_group.size=[a.canvasX-this.selected_group.pos[0],a.canvasY-this.selected_group.pos[1]]:(this.selected_group.move(c[0]/this.ds.scale,c[1]/this.ds.scale, -a.ctrlKey),this.selected_group._nodes.length&&(this.dirty_canvas=!0)),this.dirty_bgcanvas=!0;else if(this.dragging_canvas)this.ds.offset[0]+=c[0]/this.ds.scale,this.ds.offset[1]+=c[1]/this.ds.scale,this.dirty_bgcanvas=this.dirty_canvas=!0;else if((this.allow_interaction||e&&e.flags.allow_interaction)&&!this.read_only){this.connecting_node&&(this.dirty_canvas=!0);b=0;for(var d=this.graph._nodes.length;bk[0]+4||a.canvasYk[1]+4)){d=g;break}d!=this.over_link_center&&(this.over_link_center=d,this.dirty_canvas=!0);this.canvas&&(this.canvas.style.cursor="")}if(this.node_capturing_input&& -this.node_capturing_input!=e&&this.node_capturing_input.onMouseMove)this.node_capturing_input.onMouseMove(a,[a.canvasX-this.node_capturing_input.pos[0],a.canvasY-this.node_capturing_input.pos[1]],this);if(this.node_dragged&&!this.live_mode){for(b in this.selected_nodes)e=this.selected_nodes[b],e.pos[0]+=c[0]/this.ds.scale,e.pos[1]+=c[1]/this.ds.scale,e.is_selected||this.processNodeSelected(e,a);this.dirty_bgcanvas=this.dirty_canvas=!0}this.resizing_node&&!this.live_mode&&(c=[a.canvasX-this.resizing_node.pos[0], -a.canvasY-this.resizing_node.pos[1]],b=this.resizing_node.computeSize(),c[0]=Math.max(b[0],c[0]),c[1]=Math.max(b[1],c[1]),this.resizing_node.setSize(c),this.canvas.style.cursor="se-resize",this.dirty_bgcanvas=this.dirty_canvas=!0)}a.preventDefault();return!1}};l.prototype.processMouseUp=function(a){var b=void 0===a.isPrimary||a.isPrimary;if(!b)return!1;this.set_canvas_dirty_on_mouse_event&&(this.dirty_canvas=!0);if(this.graph){var c=this.getCanvasWindow().document;l.active_canvas=this;this.options.skip_events|| -(f.pointerListenerRemove(c,"move",this._mousemove_callback,!0),f.pointerListenerAdd(this.canvas,"move",this._mousemove_callback,!0),f.pointerListenerRemove(c,"up",this._mouseup_callback,!0));this.adjustMouseEvent(a);c=f.getTime();a.click_time=c-this.last_mouseclick;this.last_mouse_dragging=!1;this.last_click_position=null;this.block_click&&(this.block_click=!1);if(1==a.which){this.node_widget&&this.processNodeWidgets(this.node_widget[0],this.graph_mouse,a);this.node_widget=null;this.selected_group&& -(this.selected_group.move(this.selected_group.pos[0]-Math.round(this.selected_group.pos[0]),this.selected_group.pos[1]-Math.round(this.selected_group.pos[1]),a.ctrlKey),this.selected_group.pos[0]=Math.round(this.selected_group.pos[0]),this.selected_group.pos[1]=Math.round(this.selected_group.pos[1]),this.selected_group._nodes.length&&(this.dirty_canvas=!0),this.selected_group=null);this.selected_group_resizing=!1;var e=this.graph.getNodeOnPos(a.canvasX,a.canvasY,this.visible_nodes);if(this.dragging_rectangle){if(this.graph){c= -this.graph._nodes;var d=new Float32Array(4),g=Math.abs(this.dragging_rectangle[2]),k=Math.abs(this.dragging_rectangle[3]),n=0>this.dragging_rectangle[3]?this.dragging_rectangle[1]-k:this.dragging_rectangle[1];this.dragging_rectangle[0]=0>this.dragging_rectangle[2]?this.dragging_rectangle[0]-g:this.dragging_rectangle[0];this.dragging_rectangle[1]=n;this.dragging_rectangle[2]=g;this.dragging_rectangle[3]=k;if(!e||10a.click_time&&B(a.canvasX,a.canvasY,e.pos[0],e.pos[1]-f.NODE_TITLE_HEIGHT,f.NODE_TITLE_HEIGHT,f.NODE_TITLE_HEIGHT)&&e.collapse();this.dirty_bgcanvas=this.dirty_canvas=!0;this.node_dragged.pos[0]=Math.round(this.node_dragged.pos[0]);this.node_dragged.pos[1]=Math.round(this.node_dragged.pos[1]);(this.graph.config.align_to_grid||this.align_to_grid)&&this.node_dragged.alignToGrid();if(this.onNodeMoved)this.onNodeMoved(this.node_dragged);this.graph.afterChange(this.node_dragged); -this.node_dragged=null}else{e=this.graph.getNodeOnPos(a.canvasX,a.canvasY,this.visible_nodes);!e&&300>a.click_time&&this.deselectAllNodes();this.dirty_canvas=!0;this.dragging_canvas=!1;if(this.node_over&&this.node_over.onMouseUp)this.node_over.onMouseUp(a,[a.canvasX-this.node_over.pos[0],a.canvasY-this.node_over.pos[1]],this);if(this.node_capturing_input&&this.node_capturing_input.onMouseUp)this.node_capturing_input.onMouseUp(a,[a.canvasX-this.node_capturing_input.pos[0],a.canvasY-this.node_capturing_input.pos[1]])}}else 2== -a.which?(this.dirty_canvas=!0,this.dragging_canvas=!1):3==a.which&&(this.dirty_canvas=!0,this.dragging_canvas=!1);b&&(this.pointer_is_double=this.pointer_is_down=!1);this.graph.change();a.stopPropagation();a.preventDefault();return!1}};l.prototype.processMouseWheel=function(a){if(this.graph&&this.allow_dragcanvas){var b=null!=a.wheelDeltaY?a.wheelDeltaY:-60*a.detail;this.adjustMouseEvent(a);var c=a.clientX,e=a.clientY;if(!this.viewport||this.viewport&&c>=this.viewport[0]&&c=this.viewport[1]&&eb&&(c*=1/1.1),this.ds.changeScale(c,[a.clientX,a.clientY]),this.graph.change(),a.preventDefault(),!1}};l.prototype.isOverNodeBox=function(a,b,c){var e=f.NODE_TITLE_HEIGHT;return B(b,c,a.pos[0]+2,a.pos[1]+2-e,e-4,e-4)?!0:!1};l.prototype.isOverNodeInput=function(a,b,c,e){if(a.inputs)for(var d=0,g=a.inputs.length;db.nodes[d].pos[0]&&(c[0]=b.nodes[d].pos[0],e[0]=d),c[1]>b.nodes[d].pos[1]&&(c[1]=b.nodes[d].pos[1],e[1]=d)):(c=[b.nodes[d].pos[0],b.nodes[d].pos[1]],e=[d,d]);e=[];for(d=0;d=this.viewport[0]&&b=this.viewport[1]&&cc-this.graph._last_trigger_time)&&this.drawBackCanvas();(this.dirty_canvas||a)&&this.drawFrontCanvas();this.fps=this.render_time?1/this.render_time:0;this.frame+=1}};l.prototype.drawFrontCanvas= -function(){this.dirty_canvas=!1;this.ctx||(this.ctx=this.bgcanvas.getContext("2d"));var a=this.ctx;if(a){var b=this.canvas;a.start2D&&!this.viewport&&(a.start2D(),a.restore(),a.setTransform(1,0,0,1,0,0));var c=this.viewport||this.dirty_area;c&&(a.save(),a.beginPath(),a.rect(c[0],c[1],c[2],c[3]),a.clip());this.clear_background&&(c?a.clearRect(c[0],c[1],c[2],c[3]):a.clearRect(0,0,b.width,b.height));this.bgcanvas==this.canvas?this.drawBackCanvas():a.drawImage(this.bgcanvas,0,0);if(this.onRender)this.onRender(b, -a);this.show_info&&this.renderInfo(a,c?c[0]:0,c?c[1]:0);if(this.graph){a.save();this.ds.toCanvasContext(a);b=this.computeVisibleNodes(null,this.visible_nodes);for(var e=0;e> ";b.fillText(e+c.getTitle(),.5*a.width,40);b.restore()}c=!1;this.onRenderBackground&&(c=this.onRenderBackground(a,b));this.viewport||(b.restore(),b.setTransform(1,0,0,1,0,0));this.visible_links.length=0;if(this.graph){b.save();this.ds.toCanvasContext(b);1.5>this.ds.scale&&!c&&this.clear_background_color&& -(b.fillStyle=this.clear_background_color,b.fillRect(this.visible_area[0],this.visible_area[1],this.visible_area[2],this.visible_area[3]));if(this.background_image&&.5this.ds.scale;if(this.live_mode){if(!a.flags.collapsed&&(b.shadowColor="transparent",a.onDrawForeground))a.onDrawForeground(b,this,this.canvas)}else{var g=this.editor_alpha;b.globalAlpha=g;this.render_shadows&&!d?(b.shadowColor=f.DEFAULT_SHADOW_COLOR,b.shadowOffsetX=2*this.ds.scale, -b.shadowOffsetY=2*this.ds.scale,b.shadowBlur=3*this.ds.scale):b.shadowColor="transparent";if(!a.flags.collapsed||!a.onDrawCollapsed||1!=a.onDrawCollapsed(b,this)){var k=a._shape||f.BOX_SHAPE;K.set(a.size);var n=a.horizontal;if(a.flags.collapsed){b.font=this.inner_text_font;var t=a.getTitle?a.getTitle():a.title;null!=t&&(a._collapsed_width=Math.min(a.size[0],b.measureText(t).width+2*f.NODE_TITLE_HEIGHT),K[0]=a._collapsed_width,K[1]=0)}a.clip_area&&(b.save(),b.beginPath(),k==f.BOX_SHAPE?b.rect(0,0, -K[0],K[1]):k==f.ROUND_SHAPE?b.roundRect(0,0,K[0],K[1],[10]):k==f.CIRCLE_SHAPE&&b.arc(.5*K[0],.5*K[1],.5*K[0],0,2*Math.PI),b.clip());a.has_errors&&(e="red");this.drawNodeShape(a,b,K,c,e,a.is_selected,a.mouseOver);b.shadowColor="transparent";if(a.onDrawForeground)a.onDrawForeground(b,this,this.canvas);b.textAlign=n?"center":"left";b.font=this.inner_text_font;e=!d;var p=this.connecting_output;k=this.connecting_input;b.lineWidth=1;t=0;var q=new Float32Array(2);if(!a.flags.collapsed){if(a.inputs)for(c= -0;cthis.ds.scale,t=a._shape||a.constructor.shape||f.ROUND_SHAPE,p=a.constructor.title_mode,q=!0;p==f.TRANSPARENT_TITLE||p==f.NO_TITLE?q=!1:p==f.AUTOHIDE_TITLE&&k&&(q=!0);A[0]=0;A[1]=q?-d:0;A[2]=c[0]+1;A[3]=q?c[1]+d:c[1];k=b.globalAlpha;b.beginPath();t==f.BOX_SHAPE||n?b.fillRect(A[0],A[1],A[2],A[3]):t==f.ROUND_SHAPE||t==f.CARD_SHAPE?b.roundRect(A[0],A[1],A[2],A[3],t==f.CARD_SHAPE?[this.round_radius, -this.round_radius,0,0]:[this.round_radius]):t==f.CIRCLE_SHAPE&&b.arc(.5*c[0],.5*c[1],.5*c[0],0,2*Math.PI);b.fill();!a.flags.collapsed&&q&&(b.shadowColor="transparent",b.fillStyle="rgba(0,0,0,0.2)",b.fillRect(0,-1,A[2],2));b.shadowColor="transparent";if(a.onDrawBackground)a.onDrawBackground(b,this,this.canvas,this.graph_mouse);if(q||p==f.TRANSPARENT_TITLE){if(a.onDrawTitleBar)a.onDrawTitleBar(b,d,c,this.ds.scale,e);else if(p!=f.TRANSPARENT_TITLE&&(a.constructor.title_color||this.render_title_colored)){q= -a.constructor.title_color||e;a.flags.collapsed&&(b.shadowColor=f.DEFAULT_SHADOW_COLOR);if(this.use_gradients){var x=l.gradients[q];x||(x=l.gradients[q]=b.createLinearGradient(0,0,400,0),x.addColorStop(0,q),x.addColorStop(1,"#000"));b.fillStyle=x}else b.fillStyle=q;b.beginPath();t==f.BOX_SHAPE||n?b.rect(0,-d,c[0]+1,d):(t==f.ROUND_SHAPE||t==f.CARD_SHAPE)&&b.roundRect(0,-d,c[0]+1,d,a.flags.collapsed?[this.round_radius]:[this.round_radius,this.round_radius,0,0]);b.fill();b.shadowColor="transparent"}q= -!1;f.node_box_coloured_by_mode&&f.NODE_MODES_COLORS[a.mode]&&(q=f.NODE_MODES_COLORS[a.mode]);f.node_box_coloured_when_on&&(q=a.action_triggered?"#FFF":a.execute_triggered?"#AAA":q);if(a.onDrawTitleBox)a.onDrawTitleBox(b,d,c,this.ds.scale);else t==f.ROUND_SHAPE||t==f.CIRCLE_SHAPE||t==f.CARD_SHAPE?(n&&(b.fillStyle="black",b.beginPath(),b.arc(.5*d,-.5*d,6,0,2*Math.PI),b.fill()),b.fillStyle=a.boxcolor||q||f.NODE_DEFAULT_BOXCOLOR,n?b.fillRect(.5*d-5,-.5*d-5,10,10):(b.beginPath(),b.arc(.5*d,-.5*d,5,0,2* -Math.PI),b.fill())):(n&&(b.fillStyle="black",b.fillRect(.5*(d-10)-1,-.5*(d+10)-1,12,12)),b.fillStyle=a.boxcolor||q||f.NODE_DEFAULT_BOXCOLOR,b.fillRect(.5*(d-10),-.5*(d+10),10,10));b.globalAlpha=k;if(a.onDrawTitleText)a.onDrawTitleText(b,d,c,this.ds.scale,this.title_text_font,g);!n&&(b.font=this.title_text_font,k=String(a.getTitle()))&&(b.fillStyle=g?f.NODE_SELECTED_TITLE_COLOR:a.constructor.title_text_color||this.node_title_color,a.flags.collapsed?(b.textAlign="left",b.measureText(k),b.fillText(k.substr(0, -20),d,f.NODE_TITLE_TEXT_Y-d),b.textAlign="left"):(b.textAlign="left",b.fillText(k,d,f.NODE_TITLE_TEXT_Y-d)));a.flags.collapsed||!a.subgraph||a.skip_subgraph_button||(k=f.NODE_TITLE_HEIGHT,q=a.size[0]-k,x=f.isInsideRectangle(this.graph_mouse[0]-a.pos[0],this.graph_mouse[1]-a.pos[1],q+2,-k+2,k-4,k-4),b.fillStyle=x?"#888":"#555",t==f.BOX_SHAPE||n?b.fillRect(q+2,-k+2,k-4,k-4):(b.beginPath(),b.roundRect(q+2,-k+2,k-4,k-4,[4]),b.fill()),b.fillStyle="#333",b.beginPath(),b.moveTo(q+.2*k,.6*-k),b.lineTo(q+ -.8*k,.6*-k),b.lineTo(q+.5*k,.3*-k),b.fill());if(a.onDrawTitle)a.onDrawTitle(b)}if(g){if(a.onBounding)a.onBounding(A);p==f.TRANSPARENT_TITLE&&(A[1]-=d,A[3]+=d);b.lineWidth=1;b.globalAlpha=.8;b.beginPath();t==f.BOX_SHAPE?b.rect(-6+A[0],-6+A[1],12+A[2],12+A[3]):t==f.ROUND_SHAPE||t==f.CARD_SHAPE&&a.flags.collapsed?b.roundRect(-6+A[0],-6+A[1],12+A[2],12+A[3],[2*this.round_radius]):t==f.CARD_SHAPE?b.roundRect(-6+A[0],-6+A[1],12+A[2],12+A[3],[2*this.round_radius,2,2*this.round_radius,2]):t==f.CIRCLE_SHAPE&& -b.arc(.5*c[0],.5*c[1],.5*c[0]+6,0,2*Math.PI);b.strokeStyle=f.NODE_BOX_OUTLINE_COLOR;b.stroke();b.strokeStyle=e;b.globalAlpha=1}0E[2]&&(E[0]+=E[2],E[2]=Math.abs(E[2]));0>E[3]&& -(E[1]+=E[3],E[3]=Math.abs(E[3]));if(D(E,L)){var u=t.outputs[p];p=g.inputs[k];if(u&&p&&(t=u.dir||(t.horizontal?f.DOWN:f.RIGHT),p=p.dir||(g.horizontal?f.UP:f.LEFT),this.renderLink(a,q,x,n,!1,0,null,t,p),n&&n._last_time&&1E3>b-n._last_time)){u=2-.002*(b-n._last_time);var l=a.globalAlpha;a.globalAlpha=l*u;this.renderLink(a,q,x,n,!0,u,"white",t,p);a.globalAlpha=l}}}}}}a.globalAlpha=1};l.prototype.renderLink=function(a,b,c,e,d,g,k,n,t,p){e&&this.visible_links.push(e);!k&&e&&(k=e.color||l.link_type_colors[e.type]); -k||(k=this.default_link_color);null!=e&&this.highlighted_links[e.id]&&(k="#FFF");n=n||f.RIGHT;t=t||f.LEFT;var q=C(b,c);this.render_connections_border&&.6b[1]?0:Math.PI,a.save(),a.translate(x[0],x[1]),a.rotate(q),a.beginPath(),a.moveTo(-5,-3),a.lineTo(0,7),a.lineTo(5, --3),a.fill(),a.restore(),a.save(),a.translate(e[0],e[1]),a.rotate(p),a.beginPath(),a.moveTo(-5,-3),a.lineTo(0,7),a.lineTo(5,-3),a.fill(),a.restore()),a.beginPath(),a.arc(d[0],d[1],5,0,2*Math.PI),a.fill());if(g)for(a.fillStyle=k,x=0;5>x;++x)g=(.001*f.getTime()+.2*x)%1,d=this.computeConnectionPoint(b,c,g,n,t),a.beginPath(),a.arc(d[0],d[1],5,0,2*Math.PI),a.fill()};l.prototype.computeConnectionPoint=function(a,b,c,e,d){e=e||f.RIGHT;d=d||f.LEFT;var g=C(a,b),k=[a[0],a[1]],n=[b[0],b[1]];switch(e){case f.LEFT:k[0]+= --.25*g;break;case f.RIGHT:k[0]+=.25*g;break;case f.UP:k[1]+=-.25*g;break;case f.DOWN:k[1]+=.25*g}switch(d){case f.LEFT:n[0]+=-.25*g;break;case f.RIGHT:n[0]+=.25*g;break;case f.UP:n[1]+=-.25*g;break;case f.DOWN:n[1]+=.25*g}e=(1-c)*(1-c)*(1-c);d=3*(1-c)*(1-c)*c;g=3*(1-c)*c*c;c*=c*c;return[e*a[0]+d*k[0]+g*n[0]+c*b[0],e*a[1]+d*k[1]+g*n[1]+c*b[1]]};l.prototype.drawExecutionOrder=function(a){a.shadowColor="transparent";a.globalAlpha=.25;a.textAlign="center";a.strokeStyle="white";a.globalAlpha=.75;for(var b= -this.visible_nodes,c=0;cz&&(z=0);1r&&(r=0),1g||g>v-12||ku.last_y+h||void 0===u.last_y)){e=u.value;switch(u.type){case "button":c.type===f.pointerevents_method+"down"&&(u.callback&&setTimeout(function(){u.callback(u,p,a,b,c)},20),this.dirty_canvas=u.clicked=!0);break;case "slider":e=u.value;q=I((g-15)/(v-30),0,1);if(u.options.read_only)break;u.value=u.options.min+(u.options.max-u.options.min)*q;e!=u.value&&setTimeout(function(){d(u,u.value)},20);this.dirty_canvas= -!0;break;case "number":case "combo":e=u.value;if(c.type==f.pointerevents_method+"move"&&"number"==u.type)l&&(u.value+=.1*l*(u.options.step||1)),null!=u.options.min&&u.valueu.options.max&&(u.value=u.options.max);else if(c.type==f.pointerevents_method+"down"){var m=u.options.values;m&&m.constructor===Function&&(m=u.options.values(u,a));var r=null;"number"!=u.type&&(r=m.constructor===Array?m:Object.keys(m));g=40>g?-1:g>v-40?1:0;if("number"== -u.type)u.value+=.1*g*(u.options.step||1),null!=u.options.min&&u.valueu.options.max&&(u.value=u.options.max);else if(g)q=-1,this.last_mouseclick=0,q=m.constructor===Object?r.indexOf(String(u.value))+g:r.indexOf(u.value)+g,q>=r.length&&(q=r.length-1),0>q&&(q=0),u.value=m.constructor===Array?m[q]:q;else{var z=m!=r?Object.values(m):m;new f.ContextMenu(z,{scale:Math.max(1,this.ds.scale),event:c,className:"dark",callback:function(a,b, -c){m!=r&&(a=z.indexOf(a));this.value=a;d(this,a);p.dirty_canvas=!0;return!1}.bind(u)},q)}}else c.type==f.pointerevents_method+"up"&&"number"==u.type&&(g=40>g?-1:g>v-40?1:0,200>c.click_time&&0==g&&this.prompt("Value",u.value,function(a){if(/^[0-9+\-*/()\s]+|\d+\.\d+$/.test(a))try{a=eval(a)}catch(O){}this.value=Number(a);d(this,this.value)}.bind(u),c));e!=u.value&&setTimeout(function(){d(this,this.value)}.bind(u),20);this.dirty_canvas=!0;break;case "toggle":c.type==f.pointerevents_method+"down"&&(u.value= -!u.value,setTimeout(function(){d(u,u.value)},20));break;case "string":case "text":c.type==f.pointerevents_method+"down"&&this.prompt("Value",u.value,function(a){d(this,a)}.bind(u),c,u.options?u.options.multiline:!1);break;default:u.mouse&&(this.dirty_canvas=u.mouse(c,[g,k],a))}if(e!=u.value){if(a.onWidgetChanged)a.onWidgetChanged(u.name,u.value,e,u);a.graph._version++}return u}}}return null};l.prototype.drawGroups=function(a,b){if(this.graph){a=this.graph._groups;b.save();b.globalAlpha=.5*this.editor_alpha; -for(var c=0;cc&&.01>b.editor_alpha&&(clearInterval(e),1>c&&(b.live_mode=!0));1c.pos[0]+c.size[0])c=f;if(null===e||n+p>e.pos[1]+e.size[1])e=f;if(null===d||l"+(h.label?h.label:n)+""+a+"",value:n})}if(k.length)return new f.ContextMenu(k,{event:c,callback:function(a,b,c,e){d&&(b=this.getBoundingClientRect(),g.showEditPropertyValue(d,a.value,{position:[b.left,b.top]}))},parentMenu:e,allow_html:!0, -node:d},b),!1}};l.decodeHTML=function(a){var b=document.createElement("div");b.innerText=a;return b.innerHTML};l.onMenuResizeNode=function(a,b,c,e,d){if(d){a=function(a){a.size=a.computeSize();if(a.onResize)a.onResize(a.size)};b=l.active_canvas;if(!b.selected_nodes||1>=Object.keys(b.selected_nodes).length)a(d);else for(var g in b.selected_nodes)a(b.selected_nodes[g]);d.setDirtyCanvas(!0,!0)}};l.prototype.showLinkMenu=function(a,b){var c=this,e=c.graph.getNodeById(a.origin_id),d=c.graph.getNodeById(a.target_id), -g=!1;e&&e.outputs&&e.outputs[a.origin_slot]&&(g=e.outputs[a.origin_slot].type);var k=!1;d&&d.outputs&&d.outputs[a.target_slot]&&(k=d.inputs[a.target_slot].type);var n=new f.ContextMenu(["Add Node",null,"Delete",null],{event:b,title:null!=a.data?a.data.constructor.name:null,callback:function(b,f,q){switch(b){case "Add Node":l.onMenuAdd(null,null,q,n,function(b){b.inputs&&b.inputs.length&&b.outputs&&b.outputs.length&&e.connectByType(a.origin_slot,b,g)&&(b.connectByType(a.target_slot,d,k),b.pos[0]-= -.5*b.size[0])});break;case "Delete":c.graph.removeLink(a.id)}}});return!1};l.prototype.createDefaultNodeForSlot=function(a){a=a||{};a=Object.assign({nodeFrom:null,slotFrom:null,nodeTo:null,slotTo:null,position:[],nodeType:null,posAdd:[0,0],posSizeFix:[0,0]},a);var b=a.nodeFrom&&null!==a.slotFrom,c=!b&&a.nodeTo&&null!==a.slotTo;if(!b&&!c)return console.warn("No data passed to createDefaultNodeForSlot "+a.nodeFrom+" "+a.slotFrom+" "+a.nodeTo+" "+a.slotTo),!1;if(!a.nodeType)return console.warn("No type to createDefaultNodeForSlot"), -!1;var e=b?a.nodeFrom:a.nodeTo,d=b?a.slotFrom:a.slotTo;switch(typeof d){case "string":c=b?e.findOutputSlot(d,!1):e.findInputSlot(d,!1);d=b?e.outputs[d]:e.inputs[d];break;case "object":c=b?e.findOutputSlot(d.name):e.findInputSlot(d.name);break;case "number":c=d;d=b?e.outputs[d]:e.inputs[d];break;default:return console.warn("Cant get slot information "+d),!1}!1!==d&&!1!==c||console.warn("createDefaultNodeForSlot bad slotX "+d+" "+c);e=d.type==f.EVENT?"_event_":d.type;if((d=b?f.slot_types_default_out: -f.slot_types_default_in)&&d[e]){nodeNewType=!1;if("object"==typeof d[e]||"array"==typeof d[e])for(var g in d[e]){if(a.nodeType==d[e][g]||"AUTO"==a.nodeType){nodeNewType=d[e][g];break}}else if(a.nodeType==d[e]||"AUTO"==a.nodeType)nodeNewType=d[e];if(nodeNewType){g=!1;"object"==typeof nodeNewType&&nodeNewType.node&&(g=nodeNewType,nodeNewType=nodeNewType.node);if(d=f.createNode(nodeNewType)){if(g){if(g.properties)for(var k in g.properties)d.addProperty(k,g.properties[k]);if(g.inputs)for(k in d.inputs= -[],g.inputs)d.addOutput(g.inputs[k][0],g.inputs[k][1]);if(g.outputs)for(k in d.outputs=[],g.outputs)d.addOutput(g.outputs[k][0],g.outputs[k][1]);g.title&&(d.title=g.title);g.json&&d.configure(g.json)}this.graph.add(d);d.pos=[a.position[0]+a.posAdd[0]+(a.posSizeFix[0]?a.posSizeFix[0]*d.size[0]:0),a.position[1]+a.posAdd[1]+(a.posSizeFix[1]?a.posSizeFix[1]*d.size[1]:0)];b?a.nodeFrom.connectByType(c,d,e):a.nodeTo.connectByTypeOutput(c,d,e);return!0}console.log("failed creating "+nodeNewType)}}return!1}; -l.prototype.showConnectionMenu=function(a){a=a||{};var b=Object.assign({nodeFrom:null,slotFrom:null,nodeTo:null,slotTo:null,e:null},a),c=this,e=b.nodeFrom&&b.slotFrom;a=!e&&b.nodeTo&&b.slotTo;if(!e&&!a)return console.warn("No data passed to showConnectionMenu"),!1;a=e?b.nodeFrom:b.nodeTo;var d=e?b.slotFrom:b.slotTo,g=!1;switch(typeof d){case "string":g=e?a.findOutputSlot(d,!1):a.findInputSlot(d,!1);d=e?a.outputs[d]:a.inputs[d];break;case "object":g=e?a.findOutputSlot(d.name):a.findInputSlot(d.name); -break;case "number":g=d;d=e?a.outputs[d]:a.inputs[d];break;default:return console.warn("Cant get slot information "+d),!1}a=["Add Node",null];c.allow_searchbox&&(a.push("Search"),a.push(null));var k=d.type==f.EVENT?"_event_":d.type,n=e?f.slot_types_default_out:f.slot_types_default_in;if(n&&n[k])if("object"==typeof n[k]||"array"==typeof n[k])for(var h in n[k])a.push(n[k][h]);else a.push(n[k]);var p=new f.ContextMenu(a,{event:b.e,title:(d&&""!=d.name?d.name+(k?" | ":""):"")+(d&&k?k:""),callback:function(a, -f,n){switch(a){case "Add Node":l.onMenuAdd(null,null,n,p,function(a){e?b.nodeFrom.connectByType(g,a,k):b.nodeTo.connectByTypeOutput(g,a,k)});break;case "Search":e?c.showSearchBox(n,{node_from:b.nodeFrom,slot_from:d,type_filter_in:k}):c.showSearchBox(n,{node_to:b.nodeTo,slot_from:d,type_filter_out:k});break;default:c.createDefaultNodeForSlot(Object.assign(b,{position:[b.e.canvasX,b.e.canvasY],nodeType:a}))}}});return!1};l.onShowPropertyEditor=function(a,b,c,e,d){function g(){if(h){var b=h.value;"Number"== -a.type?b=Number(b):"Boolean"==a.type&&(b=!!b);d[k]=b;n.parentNode&&n.parentNode.removeChild(n);d.setDirtyCanvas(!0,!0)}}var k=a.property||"title";b=d[k];var n=document.createElement("div");n.is_modified=!1;n.className="graphdialog";n.innerHTML="";n.close=function(){n.parentNode&&n.parentNode.removeChild(n)};n.querySelector(".name").innerText=k;var h=n.querySelector(".value");h&&(h.value=b,h.addEventListener("blur", -function(a){this.focus()}),h.addEventListener("keydown",function(a){n.is_modified=!0;if(27==a.keyCode)n.close();else if(13==a.keyCode)g();else if(13!=a.keyCode&&"textarea"!=a.target.localName)return;a.preventDefault();a.stopPropagation()}));b=l.active_canvas.canvas;c=b.getBoundingClientRect();var p=e=-20;c&&(e-=c.left,p-=c.top);event?(n.style.left=event.clientX+e+"px",n.style.top=event.clientY+p+"px"):(n.style.left=.5*b.width+e+"px",n.style.top=.5*b.height+p+"px");n.querySelector("button").addEventListener("click", -g);b.parentNode.appendChild(n);h&&h.focus();var q=null;n.addEventListener("mouseleave",function(a){f.dialog_close_on_mouse_leave&&!n.is_modified&&f.dialog_close_on_mouse_leave&&(q=setTimeout(n.close,f.dialog_close_on_mouse_leave_delay))});n.addEventListener("mouseenter",function(a){f.dialog_close_on_mouse_leave&&q&&clearTimeout(q)})};l.prototype.prompt=function(a,b,c,e,d){var g=this;a=a||"";var k=document.createElement("div");k.is_modified=!1;k.className="graphdialog rounded";k.innerHTML=d?" ": -" ";k.close=function(){g.prompt_box=null;k.parentNode&&k.parentNode.removeChild(k)};d=l.active_canvas.canvas;d.parentNode.appendChild(k);1l.search_limit))break}}u=null;if(Array.prototype.filter)u=Object.keys(f.registered_node_types).filter(e);else for(p in u=[],f.registered_node_types)e(p)&&u.push(p);for(p=0;pl.search_limit);p++);if(b.show_general_after_typefiltered&&(n.value|| -h.value)){filtered_extra=[];for(p in f.registered_node_types)e(p,{inTypeOverride:n&&n.value?"*":!1,outTypeOverride:h&&h.value?"*":!1})&&filtered_extra.push(p);for(p=0;pl.search_limit);p++);}if((n.value||h.value)&&0==v.childNodes.length&&b.show_general_if_none_on_typefilter){filtered_extra=[];for(p in f.registered_node_types)e(p,{skipFilter:!0})&&filtered_extra.push(p);for(p=0;pl.search_limit);p++);}}}b=Object.assign({slot_from:null,node_from:null,node_to:null,do_type_filter:f.search_filter_enabled,type_filter_in:!1,type_filter_out:!1,show_general_if_none_on_typefilter:!0,show_general_after_typefiltered:!0,hide_on_mouse_leave:f.search_hide_on_mouse_leave,show_all_if_empty:!0,show_all_on_open:f.search_show_all_on_open},b||{});var g=this,k=l.active_canvas,n=k.canvas,h=n.ownerDocument||document,p=document.createElement("div");p.className= -"litegraph litesearchbox graphdialog rounded";p.innerHTML="Search ";b.do_type_filter&&(p.innerHTML+="",p.innerHTML+="");p.innerHTML+="
";h.fullscreenElement?h.fullscreenElement.appendChild(p):(h.body.appendChild(p),h.body.style.overflow="hidden");if(b.do_type_filter)var q= -p.querySelector(".slot_in_type_filter"),x=p.querySelector(".slot_out_type_filter");p.close=function(){g.search_box=null;this.blur();n.focus();h.body.style.overflow="";setTimeout(function(){g.canvas.focus()},20);p.parentNode&&p.parentNode.removeChild(p)};1q.height-200&&(v.style.maxHeight=q.height-a.layerY-20+"px");w.focus();b.show_all_on_open&&d();return p};l.prototype.showEditPropertyValue=function(a,b,c){function e(){d(x.value)}function d(d){g&&g.values&&g.values.constructor===Object&&void 0!=g.values[d]&&(d=g.values[d]);"number"==typeof a.properties[b]&& -(d=Number(d));if("array"==f||"object"==f)d=JSON.parse(d);a.properties[b]=d;a.graph&&a.graph._version++;if(a.onPropertyChanged)a.onPropertyChanged(b,d);if(c.onclose)c.onclose();q.close();a.setDirtyCanvas(!0,!0)}if(a&&void 0!==a.properties[b]){c=c||{};var g=a.getPropertyInfo(b),f=g.type,n="";if("string"==f||"number"==f||"array"==f||"object"==f)n="";else if("enum"!=f&&"combo"!=f||!g.values)if("boolean"==f||"toggle"==f)n="";else{console.warn("unknown type: "+f);return}else{n=""}var q=this.createDialog(""+(g.label?g.label:b)+""+n+"",c),x=!1;if("enum"!=f&&"combo"!=f||!g.values)if("boolean"==f||"toggle"==f)(x=q.querySelector("input"))&& -x.addEventListener("click",function(a){q.modified();d(!!x.checked)});else{if(x=q.querySelector("input"))x.addEventListener("blur",function(a){this.focus()}),p=void 0!==a.properties[b]?a.properties[b]:"","string"!==f&&(p=JSON.stringify(p)),x.value=p,x.addEventListener("keydown",function(a){if(27==a.keyCode)q.close();else if(13==a.keyCode)e();else if(13!=a.keyCode){q.modified();return}a.preventDefault();a.stopPropagation()})}else x=q.querySelector("select"),x.addEventListener("change",function(a){q.modified(); -d(a.target.value)});x&&x.focus();q.querySelector("button").addEventListener("click",e);return q}};l.prototype.createDialog=function(a,b){b=Object.assign({checkForInput:!1,closeOnLeave:!0,closeOnLeave_checkModified:!0},b||{});var c=document.createElement("div");c.className="graphdialog";c.innerHTML=a;c.is_modified=!1;a=this.canvas.getBoundingClientRect();var e=-20,d=-20;a&&(e-=a.left,d-=a.top);b.position?(e+=b.position[0],d+=b.position[1]):b.event?(e+=b.event.clientX,d+=b.event.clientY):(e+=.5*this.canvas.width, -d+=.5*this.canvas.height);c.style.left=e+"px";c.style.top=d+"px";this.canvas.parentNode.appendChild(c);b.checkForInput&&(a=[],(a=c.querySelectorAll("input"))&&a.forEach(function(a){a.addEventListener("keydown",function(a){c.modified();if(27==a.keyCode)c.close();else if(13!=a.keyCode)return;a.preventDefault();a.stopPropagation()});a.focus()}));c.modified=function(){c.is_modified=!0};c.close=function(){c.parentNode&&c.parentNode.removeChild(c)};var g=null,k=!1;c.addEventListener("mouseleave",function(a){k|| -(b.closeOnLeave||f.dialog_close_on_mouse_leave)&&!c.is_modified&&f.dialog_close_on_mouse_leave&&(g=setTimeout(c.close,f.dialog_close_on_mouse_leave_delay))});c.addEventListener("mouseenter",function(a){(b.closeOnLeave||f.dialog_close_on_mouse_leave)&&g&&clearTimeout(g)});(a=c.querySelectorAll("select"))&&a.forEach(function(a){a.addEventListener("click",function(a){k++});a.addEventListener("blur",function(a){k=0});a.addEventListener("change",function(a){k=-1})});return c};l.prototype.createPanel=function(a, -b){b=b||{};var c=b.window||window,e=document.createElement("div");e.className="litegraph dialog";e.innerHTML="
";e.header=e.querySelector(".dialog-header");b.width&&(e.style.width=b.width+(b.width.constructor===Number?"px":""));b.height&&(e.style.height=b.height+(b.height.constructor===Number?"px":""));b.closable&& -(b=document.createElement("span"),b.innerHTML="✕",b.classList.add("close"),b.addEventListener("click",function(){e.close()}),e.header.appendChild(b));e.title_element=e.querySelector(".dialog-title");e.title_element.innerText=a;e.content=e.querySelector(".dialog-content");e.alt_content=e.querySelector(".dialog-alt-content");e.footer=e.querySelector(".dialog-footer");e.close=function(){if(e.onClose&&"function"==typeof e.onClose)e.onClose();e.parentNode&&e.parentNode.removeChild(e);this.parentNode&& -this.parentNode.removeChild(this)};e.toggleAltContent=function(a){if("undefined"!=typeof a){var b=a?"block":"none";a=a?"none":"block"}else b="block"!=e.alt_content.style.display?"block":"none",a="block"!=e.alt_content.style.display?"none":"block";e.alt_content.style.display=b;e.content.style.display=a};e.toggleFooterVisibility=function(a){e.footer.style.display="undefined"!=typeof a?a?"block":"none":"block"!=e.footer.style.display?"block":"none"};e.clear=function(){this.content.innerHTML=""};e.addHTML= -function(a,b,c){var d=document.createElement("div");b&&(d.className=b);d.innerHTML=a;c?e.footer.appendChild(d):e.content.appendChild(d);return d};e.addButton=function(a,b,c){var d=document.createElement("button");d.innerText=a;d.options=c;d.classList.add("btn");d.addEventListener("click",b);e.footer.appendChild(d);return d};e.addSeparator=function(){var a=document.createElement("div");a.className="separator";e.content.appendChild(a)};e.addWidget=function(a,b,k,n,h){function d(a,b){n.callback&&n.callback(a, -b,n);h&&h(a,b,n)}n=n||{};var g=String(k);a=a.toLowerCase();"number"==a&&(g=k.toFixed(3));var x=document.createElement("div");x.className="property";x.innerHTML="";x.querySelector(".property_name").innerText=n.label||b;var u=x.querySelector(".property_value");u.innerText=g;x.dataset.property=b;x.dataset.type=n.type||a;x.options=n;x.value=k;if("code"==a)x.addEventListener("click",function(a){e.inner_showCodePad(this.dataset.property)}); -else if("boolean"==a)x.classList.add("boolean"),k&&x.classList.add("bool-on"),x.addEventListener("click",function(){var a=this.dataset.property;this.value=!this.value;this.classList.toggle("bool-on");this.querySelector(".property_value").innerText=this.value?"true":"false";d(a,this.value)});else if("string"==a||"number"==a)u.setAttribute("contenteditable",!0),u.addEventListener("keydown",function(b){"Enter"!=b.code||"string"==a&&b.shiftKey||(b.preventDefault(),this.blur())}),u.addEventListener("blur", -function(){var a=this.innerText,b=this.parentNode.dataset.property;"number"==this.parentNode.dataset.type&&(a=Number(a));d(b,a)});else if("enum"==a||"combo"==a)g=l.getPropertyPrintableValue(k,n.values),u.innerText=g,u.addEventListener("click",function(a){var b=this.parentNode.dataset.property,e=this;new f.ContextMenu(n.values||[],{event:a,className:"dark",callback:function(a,c,f){e.innerText=a;d(b,a);return!1}},c)});e.content.appendChild(x);return x};if(e.onOpen&&"function"==typeof e.onOpen)e.onOpen(); -return e};l.getPropertyPrintableValue=function(a,b){if(!b||b.constructor===Array)return String(a);if(b.constructor===Object){var c="",e;for(e in b)if(b[e]==a){c=e;break}return String(a)+" ("+c+")"}};l.prototype.closePanels=function(){var a=document.querySelector("#node-panel");a&&a.close();(a=document.querySelector("#option-panel"))&&a.close()};l.prototype.showShowGraphOptionsPanel=function(a,b,c,e){if(this.constructor&&"HTMLDivElement"==this.constructor.name){if(!(b&&b.event&&b.event.target&&b.event.target.lgraphcanvas)){console.warn("Canvas not found"); -return}var d=b.event.target.lgraphcanvas}else d=this;d.closePanels();a=d.getCanvasWindow();panel=d.createPanel("Options",{closable:!0,window:a,onOpen:function(){d.OPTIONPANEL_IS_OPEN=!0},onClose:function(){d.OPTIONPANEL_IS_OPEN=!1;d.options_panel=null}});d.options_panel=panel;panel.id="option-panel";panel.classList.add("settings");(function(){panel.content.innerHTML="";var a=function(a,b,c){c&&c.key&&(a=c.key);c.values&&(b=Object.values(c.values).indexOf(b));d[a]=b},b=f.availableCanvasOptions;b.sort(); -for(var c in b){var e=b[c];panel.addWidget("boolean",e,d[e],{key:e,on:"True",off:"False"},a)}panel.addWidget("combo","Render mode",f.LINK_RENDER_MODES[d.links_render_mode],{key:"links_render_mode",values:f.LINK_RENDER_MODES},a);panel.addSeparator();panel.footer.innerHTML=""})();d.canvas.parentNode.appendChild(panel)};l.prototype.showShowNodePanel=function(a){function b(){d.content.innerHTML="";d.addHTML(""+a.type+""+(a.constructor.desc||"")+""); -d.addHTML("

Properties

");var b=function(b,c){e.graph.beforeChange(a);switch(b){case "Title":a.title=c;break;case "Mode":b=Object.values(f.NODE_MODES).indexOf(c);0<=b&&f.NODE_MODES[b]?a.changeMode(b):console.warn("unexpected mode: "+c);break;case "Color":l.node_colors[c]?(a.color=l.node_colors[c].color,a.bgcolor=l.node_colors[c].bgcolor):console.warn("unexpected color: "+c);break;default:a.setProperty(b,c)}e.graph.afterChange();e.dirty_canvas=!0};d.addWidget("string","Title",a.title,{},b); -d.addWidget("combo","Mode",f.NODE_MODES[a.mode],{values:f.NODE_MODES},b);var c="";void 0!==a.color&&(c=Object.keys(l.node_colors).filter(function(b){return l.node_colors[b].color==a.color}));d.addWidget("combo","Color",c,{values:Object.keys(l.node_colors)},b);for(var n in a.properties){c=a.properties[n];var h=a.getPropertyInfo(n);a.onAddPropertyToPanel&&a.onAddPropertyToPanel(n,d)||d.addWidget(h.widget||h.type,n,c,h,b)}d.addSeparator();if(a.onShowCustomPanelInfo)a.onShowCustomPanelInfo(d);d.footer.innerHTML= -"";d.addButton("Delete",function(){a.block_delete||(a.graph.remove(a),d.close())}).classList.add("delete")}this.SELECTED_NODE=a;this.closePanels();var c=this.getCanvasWindow(),e=this,d=this.createPanel(a.title||"",{closable:!0,window:c,onOpen:function(){e.NODEPANEL_IS_OPEN=!0},onClose:function(){e.NODEPANEL_IS_OPEN=!1;e.node_panel=null}});e.node_panel=d;d.id="node-panel";d.node=a;d.classList.add("settings");d.inner_showCodePad=function(c){d.classList.remove("settings");d.classList.add("centered"); -d.alt_content.innerHTML="";var e=d.alt_content.querySelector("textarea"),f=function(){d.toggleAltContent(!1);d.toggleFooterVisibility(!0);e.parentNode.removeChild(e);d.classList.add("settings");d.classList.remove("centered");b()};e.value=a.properties[c];e.addEventListener("keydown",function(b){"Enter"==b.code&&b.ctrlKey&&(a.setProperty(c,e.value),f())});d.toggleAltContent(!0);d.toggleFooterVisibility(!1);e.style.height="calc(100% - 40px)";var g=d.addButton("Assign", -function(){a.setProperty(c,e.value);f()});d.alt_content.appendChild(g);g=d.addButton("Close",f);g.style.float="right";d.alt_content.appendChild(g)};b();this.canvas.parentNode.appendChild(d)};l.prototype.showSubgraphPropertiesDialog=function(a){function b(){e.clear();if(a.inputs)for(var c=0;c","subgraph_property"); -k.dataset.name=f.name;k.dataset.slot=c;k.querySelector(".name").innerText=f.name;k.querySelector(".type").innerText=f.type;k.querySelector("button").addEventListener("click",function(c){a.removeInput(Number(this.parentNode.dataset.slot));b()})}}}console.log("showing subgraph properties dialog");var c=this.canvas.parentNode.querySelector(".subgraph_dialog");c&&c.close();var e=this.createPanel("Subgraph Inputs",{closable:!0,width:500});e.node=a;e.classList.add("subgraph_dialog");e.addHTML(" + NameType", -"subgraph_property extra",!0).querySelector("button").addEventListener("click",function(c){c=this.parentNode;var d=c.querySelector(".name").value,e=c.querySelector(".type").value;d&&-1==a.findInputSlot(d)&&(a.addInput(d,e),c.querySelector(".name").value="",c.querySelector(".type").value="",b())});b();this.canvas.parentNode.appendChild(e);return e};l.prototype.showSubgraphPropertiesDialogRight=function(a){function b(){d.clear();if(a.outputs)for(var c=0;c","subgraph_property");f.dataset.name=e.name;f.dataset.slot=c;f.querySelector(".name").innerText=e.name;f.querySelector(".type").innerText=e.type;f.querySelector("button").addEventListener("click",function(c){a.removeOutput(Number(this.parentNode.dataset.slot));b()})}}}function c(){var c=this.parentNode,d=c.querySelector(".name").value,e=c.querySelector(".type").value;d&&-1==a.findOutputSlot(d)&& -(a.addOutput(d,e),c.querySelector(".name").value="",c.querySelector(".type").value="",b())}var e=this.canvas.parentNode.querySelector(".subgraph_dialog");e&&e.close();var d=this.createPanel("Subgraph Outputs",{closable:!0,width:500});d.node=a;d.classList.add("subgraph_dialog");e=d.addHTML(" + NameType","subgraph_property extra",!0);e.querySelector(".name").addEventListener("keydown", -function(a){13==a.keyCode&&c.apply(this)});e.querySelector("button").addEventListener("click",function(a){c.apply(this)});b();this.canvas.parentNode.appendChild(d);return d};l.prototype.checkPanels=function(){if(this.canvas)for(var a=this.canvas.parentNode.querySelectorAll(".litegraph.dialog"),b=0;b=Object.keys(a.selected_nodes).length)d.collapse(); -else for(var f in a.selected_nodes)a.selected_nodes[f].collapse();d.graph.afterChange()};l.onMenuNodePin=function(a,b,c,e,d){d.pin()};l.onMenuNodeMode=function(a,b,c,e,d){new f.ContextMenu(f.NODE_MODES,{event:c,callback:function(a){if(d){var b=Object.values(f.NODE_MODES).indexOf(a),c=function(c){0<=b&&f.NODE_MODES[b]?c.changeMode(b):(console.warn("unexpected mode: "+a),c.changeMode(f.ALWAYS))},e=l.active_canvas;if(!e.selected_nodes||1>=Object.keys(e.selected_nodes).length)c(d);else for(var g in e.selected_nodes)c(e.selected_nodes[g])}}, -parentMenu:e,node:d});return!1};l.onMenuNodeColors=function(a,b,c,e,d){if(!d)throw"no node for color";b=[];b.push({value:null,content:"No color"});for(var g in l.node_colors)a=l.node_colors[g],a={value:g,content:""+g+""},b.push(a);new f.ContextMenu(b,{event:c,callback:function(a){if(d){var b=a.value?l.node_colors[a.value]: -null;a=function(a){b?a.constructor===f.LGraphGroup?a.color=b.groupcolor:(a.color=b.color,a.bgcolor=b.bgcolor):(delete a.color,delete a.bgcolor)};var c=l.active_canvas;if(!c.selected_nodes||1>=Object.keys(c.selected_nodes).length)a(d);else for(var e in c.selected_nodes)a(c.selected_nodes[e]);d.setDirtyCanvas(!0,!0)}},parentMenu:e,node:d});return!1};l.onMenuNodeShapes=function(a,b,c,e,d){if(!d)throw"no node passed";new f.ContextMenu(f.VALID_SHAPES,{event:c,callback:function(a){if(d){d.graph.beforeChange(); -var b=l.active_canvas;if(!b.selected_nodes||1>=Object.keys(b.selected_nodes).length)d.shape=a;else for(var c in b.selected_nodes)b.selected_nodes[c].shape=a;d.graph.afterChange();d.setDirtyCanvas(!0)}},parentMenu:e,node:d});return!1};l.onMenuNodeRemove=function(a,b,c,e,d){if(!d)throw"no node passed";a=d.graph;a.beforeChange();b=l.active_canvas;if(!b.selected_nodes||1>=Object.keys(b.selected_nodes).length)!1!==d.removable&&a.remove(d);else for(var f in b.selected_nodes)c=b.selected_nodes[f],!1!==c.removable&& -a.remove(c);a.afterChange();d.setDirtyCanvas(!0,!0)};l.onMenuNodeToSubgraph=function(a,b,c,e,d){a=d.graph;if(b=l.active_canvas)c=Object.values(b.selected_nodes||{}),c.length||(c=[d]),e=f.createNode("graph/subgraph"),e.pos=d.pos.concat(),a.add(e),e.buildFromNodes(c),b.deselectAllNodes(),d.setDirtyCanvas(!0,!0)};l.onMenuNodeClone=function(a,b,c,e,d){d.graph.beforeChange();var f={};a=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),f[b.id]=b)}};b=l.active_canvas; -if(!b.selected_nodes||1>=Object.keys(b.selected_nodes).length)a(d);else for(var k in b.selected_nodes)a(b.selected_nodes[k]);Object.keys(f).length&&b.selectNodes(f);d.graph.afterChange();d.setDirtyCanvas(!0,!0)};l.node_colors={red:{color:"#322",bgcolor:"#533",groupcolor:"#A88"},brown:{color:"#332922",bgcolor:"#593930",groupcolor:"#b06634"},green:{color:"#232",bgcolor:"#353",groupcolor:"#8A8"},blue:{color:"#223",bgcolor:"#335",groupcolor:"#88A"},pale_blue:{color:"#2a363b",bgcolor:"#3f5159",groupcolor:"#3f789e"}, -cyan:{color:"#233",bgcolor:"#355",groupcolor:"#8AA"},purple:{color:"#323",bgcolor:"#535",groupcolor:"#a1309b"},yellow:{color:"#432",bgcolor:"#653",groupcolor:"#b58b2a"},black:{color:"#222",bgcolor:"#000",groupcolor:"#444"}};l.prototype.getCanvasMenuOptions=function(){if(this.getMenuOptions)var a=this.getMenuOptions();else a=[{content:"Add Node",has_submenu:!0,callback:l.onMenuAdd},{content:"Add Group",callback:l.onGroupAdd}],1Name", -d),p=g.querySelector("input");p&&f&&(p.value=f.label||"");var k=function(){a.graph.beforeChange();p.value&&(f&&(f.label=p.value),c.setDirty(!0));g.close();a.graph.afterChange()};g.querySelector("button").addEventListener("click",k);p.addEventListener("keydown",function(a){g.is_modified=!0;if(27==a.keyCode)g.close();else if(13==a.keyCode)k();else if(13!=a.keyCode&&"textarea"!=a.target.localName)return;a.preventDefault();a.stopPropagation()});p.focus()}},extra:a};a&&(g.title=a.type);var k=null;a&&(k= -a.getSlotInPosition(b.canvasX,b.canvasY),l.active_node=a);k?(d=[],a.getSlotMenuOptions?d=a.getSlotMenuOptions(k):(k&&k.output&&k.output.links&&k.output.links.length&&d.push({content:"Disconnect Links",slot:k}),b=k.input||k.output,b.removable&&d.push(b.locked?"Cannot remove":{content:"Remove Slot",slot:k}),b.nameLocked||d.push({content:"Rename Slot",slot:k})),g.title=(k.input?k.input.type:k.output.type)||"*",k.input&&k.input.type==f.ACTION&&(g.title="Action"),k.output&&k.output.type==f.EVENT&&(g.title= -"Event")):a?d=this.getNodeMenuOptions(a):(d=this.getCanvasMenuOptions(),(k=this.graph.getGroupOnPos(b.canvasX,b.canvasY))&&d.push(null,{content:"Edit Group",has_submenu:!0,submenu:{title:"Group",extra:k,options:this.getGroupMenuOptions(k)}}));d&&new f.ContextMenu(d,g,e)};f.compareObjects=function(a,b){for(var c in a)if(a[c]!=b[c])return!1;return!0};f.distance=C;f.colorToString=function(a){return"rgba("+Math.round(255*a[0]).toFixed()+","+Math.round(255*a[1]).toFixed()+","+Math.round(255*a[2]).toFixed()+ -","+(4==a.length?a[3].toFixed(2):"1.0")+")"};f.isInsideRectangle=B;f.growBounding=function(a,b,c){ba[2]&&(a[2]=b);ca[3]&&(a[3]=c)};f.isInsideBounding=function(a,b){return a[0]b[1][0]||a[1]>b[1][1]?!1:!0};f.overlapBounding=D;f.hex2num=function(a){"#"==a.charAt(0)&&(a=a.slice(1));a=a.toUpperCase();for(var b=Array(3),c=0,e,d,f=0;6>f;f+=2)e="0123456789ABCDEF".indexOf(a.charAt(f)),d="0123456789ABCDEF".indexOf(a.charAt(f+1)),b[c]=16*e+d,c++;return b}; -f.num2hex=function(a){for(var b="#",c,e,d=0;3>d;d++)c=a[d]/16,e=a[d]%16,b+="0123456789ABCDEF".charAt(c)+"0123456789ABCDEF".charAt(e);return b};G.prototype.addItem=function(a,b,c){function e(a){var b=this.value;b&&b.has_submenu&&d.call(this,a)}function d(a){var b=this.value,d=!0;g.current_submenu&&g.current_submenu.close(a);if(c.callback){var e=c.callback.call(this,b,c,a,g,c.node);!0===e&&(d=!1)}if(b&&(b.callback&&!c.ignore_item_callbacks&&!0!==b.disabled&&(e=b.callback.call(this,b,c,a,g,c.extra), -!0===e&&(d=!1)),b.submenu)){if(!b.submenu.options)throw"ContextMenu submenu needs options";new g.constructor(b.submenu.options,{callback:b.submenu.callback,event:a,parentMenu:g,ignore_item_callbacks:b.submenu.ignore_item_callbacks,title:b.submenu.title,extra:b.submenu.extra,autoopen:c.autoopen});d=!1}d&&!g.lock&&g.close()}var g=this;c=c||{};var k=document.createElement("div");k.className="litemenu-entry submenu";var h=!1;if(null===b)k.classList.add("separator");else{k.innerHTML=b&&b.title?b.title: -a;if(k.value=b)b.disabled&&(h=!0,k.classList.add("disabled")),(b.submenu||b.has_submenu)&&k.classList.add("has_submenu");"function"==typeof b?(k.dataset.value=a,k.onclick_callback=b):k.dataset.value=b;b.className&&(k.className+=" "+b.className)}this.root.appendChild(k);h||k.addEventListener("click",d);!h&&c.autoopen&&f.pointerListenerAdd(k,"enter",e);return k};G.prototype.close=function(a,b){this.root.parentNode&&this.root.parentNode.removeChild(this.root);this.parentMenu&&!b&&(this.parentMenu.lock= -!1,this.parentMenu.current_submenu=null,void 0===a?this.parentMenu.close():a&&!G.isCursorOverElement(a,this.parentMenu.root)&&G.trigger(this.parentMenu.root,f.pointerevents_method+"leave",a));this.current_submenu&&this.current_submenu.close(a,!0);this.root.closing_timer&&clearTimeout(this.root.closing_timer)};G.trigger=function(a,b,c,e){var d=document.createEvent("CustomEvent");d.initCustomEvent(b,!0,!0,c);d.srcElement=e;a.dispatchEvent?a.dispatchEvent(d):a.__events&&a.__events.dispatchEvent(d);return d}; -G.prototype.getTopMenu=function(){return this.options.parentMenu?this.options.parentMenu.getTopMenu():this};G.prototype.getFirstEvent=function(){return this.options.parentMenu?this.options.parentMenu.getFirstEvent():this.options.event};G.isCursorOverElement=function(a,b){var c=a.clientX;a=a.clientY;return(b=b.getBoundingClientRect())?a>b.top&&ab.left&&cMath.abs(b))return e[1];a=(a-e[0])/b;return e[1]*(1-a)+d[1]*a}}return 0}};H.prototype.draw=function(a,b,c,e,d,f){if(c=this.points){this.size=b;var g=b[0]-2*this.margin;b=b[1]-2*this.margin;d=d||"#666";a.save();a.translate(this.margin,this.margin);e&&(a.fillStyle="#111",a.fillRect(0,0,g,b),a.fillStyle="#222",a.fillRect(.5*g,0,1,b),a.strokeStyle="#333", -a.strokeRect(0,0,g,b));a.strokeStyle=d;f&&(a.globalAlpha=.5);a.beginPath();for(e=0;ea[1])){var e=this.size[0]-2*this.margin,d=this.size[1]-2*this.margin,f=a[0]-this.margin;a=a[1]-this.margin; -this.selected=this.getCloserPoint([f,a],30/b.ds.scale);-1==this.selected&&(b=[f/e,1-a/d],c.push(b),c.sort(function(a,b){return a[0]-b[0]}),this.selected=c.indexOf(b),this.must_update=!0);if(-1!=this.selected)return!0}};H.prototype.onMouseMove=function(a,b){var c=this.points;if(c){var e=this.selected;if(!(0>e)){var d=(a[0]-this.margin)/(this.size[0]-2*this.margin),f=(a[1]-this.margin)/(this.size[1]-2*this.margin);this._nearest=this.getCloserPoint([a[0]-this.margin,a[1]-this.margin],30/b.ds.scale); -if(b=c[e]){var k=0==e||e==c.length-1;!k&&(-10>a[0]||a[0]>this.size[0]+10||-10>a[1]||a[1]>this.size[1]+10)?(c.splice(e,1),this.selected=-1):(b[0]=k?0==e?0:1:I(d,0,1),b[1]=1-I(f,0,1),c.sort(function(a,b){return a[0]-b[0]}),this.selected=c.indexOf(b),this.must_update=!0)}}}};H.prototype.onMouseUp=function(a,b){this.selected=-1;return!1};H.prototype.getCloserPoint=function(a,b){var c=this.points;if(!c)return-1;b=b||30;for(var e=this.size[0]-2*this.margin,d=this.size[1]-2*this.margin,f=c.length,k=[0,0], -h=1E6,l=-1,p=0;ph||q>b||(l=p,h=q)}return l};f.CurveEditor=H;f.getParameterNames=function(a){return(a+"").replace(/[/][/].*$/gm,"").replace(/\s+/g,"").replace(/[/][*][^/*]*[*][/]/g,"").split("){",1)[0].replace(/^[^(]*[(]/,"").replace(/=[^,]+/g,"").split(",").filter(Boolean)};f.pointerListenerAdd=function(a,b,c,e){e=void 0===e?!1:e;if(a&&a.addEventListener&&b&&"function"===typeof c){var d=f.pointerevents_method;if("pointer"==d&&!window.PointerEvent)switch(console.warn("sMethod=='pointer' && !window.PointerEvent"), -console.log("Converting pointer["+b+"] : down move up cancel enter TO touchstart touchmove touchend, etc .."),b){case "down":d="touch";b="start";break;case "move":d="touch";break;case "up":d="touch";b="end";break;case "cancel":d="touch";break;case "enter":console.log("debug: Should I send a move event?");break;default:console.warn("PointerEvent not available in this browser ? The event "+b+" would not be called")}switch(b){case "down":case "up":case "move":case "over":case "out":case "enter":a.addEventListener(d+ -b,c,e);case "leave":case "cancel":case "gotpointercapture":case "lostpointercapture":if("mouse"!=d)return a.addEventListener(d+b,c,e);default:return a.addEventListener(b,c,e)}}};f.pointerListenerRemove=function(a,b,c,e){e=void 0===e?!1:e;if(a&&a.removeEventListener&&b&&"function"===typeof c)switch(b){case "down":case "up":case "move":case "over":case "out":case "enter":"pointer"!=f.pointerevents_method&&"mouse"!=f.pointerevents_method||a.removeEventListener(f.pointerevents_method+b,c,e);case "leave":case "cancel":case "gotpointercapture":case "lostpointercapture":if("pointer"== -f.pointerevents_method)return a.removeEventListener(f.pointerevents_method+b,c,e);default:return a.removeEventListener(b,c,e)}};w.clamp=I;"undefined"==typeof window||window.requestAnimationFrame||(window.requestAnimationFrame=window.webkitRequestAnimationFrame||window.mozRequestAnimationFrame||function(a){window.setTimeout(a,1E3/60)})})(this); -"undefined"!=typeof exports&&(exports.LiteGraph=this.LiteGraph,exports.LGraph=this.LGraph,exports.LLink=this.LLink,exports.LGraphNode=this.LGraphNode,exports.LGraphGroup=this.LGraphGroup,exports.DragAndScale=this.DragAndScale,exports.LGraphCanvas=this.LGraphCanvas,exports.ContextMenu=this.ContextMenu); -(function(w){function m(){this.addOutput("in ms","number");this.addOutput("in sec","number")}function r(){this.size=[140,80];this.properties={enabled:!0};this.enabled=!0;this.subgraph=new t.LGraph;this.subgraph._subgraph_node=this;this.subgraph._is_subgraph=!0;this.subgraph.onTrigger=this.onSubgraphTrigger.bind(this);this.subgraph.onInputAdded=this.onSubgraphNewInput.bind(this);this.subgraph.onInputRenamed=this.onSubgraphRenamedInput.bind(this);this.subgraph.onInputTypeChanged=this.onSubgraphTypeChangeInput.bind(this); -this.subgraph.onInputRemoved=this.onSubgraphRemovedInput.bind(this);this.subgraph.onOutputAdded=this.onSubgraphNewOutput.bind(this);this.subgraph.onOutputRenamed=this.onSubgraphRenamedOutput.bind(this);this.subgraph.onOutputTypeChanged=this.onSubgraphTypeChangeOutput.bind(this);this.subgraph.onOutputRemoved=this.onSubgraphRemovedOutput.bind(this)}function h(){this.addOutput("","number");this.name_in_graph="";this.properties={name:"",type:"number",value:0};var a=this;this.name_widget=this.addWidget("text", -"Name",this.properties.name,function(b){b&&a.setProperty("name",b)});this.type_widget=this.addWidget("text","Type",this.properties.type,function(b){a.setProperty("type",b)});this.value_widget=this.addWidget("number","Value",this.properties.value,function(b){a.setProperty("value",b)});this.widgets_up=!0;this.size=[180,90]}function y(){this.addInput("","");this.name_in_graph="";this.properties={name:"",type:""};this.name_widget=this.addWidget("text","Name",this.properties.name,"name");this.type_widget= -this.addWidget("text","Type",this.properties.type,"type");this.widgets_up=!0;this.size=[180,60]}function z(){this.addOutput("value","number");this.addProperty("value",1);this.widget=this.addWidget("number","value",1,"value");this.widgets_up=!0;this.size=[180,30]}function l(){this.addOutput("bool","boolean");this.addProperty("value",!0);this.widget=this.addWidget("toggle","value",!0,"value");this.widgets_up=this.serialize_widgets=!0;this.size=[140,30]}function C(){this.addOutput("string","string"); -this.addProperty("value","");this.widget=this.addWidget("text","value","","value");this.widgets_up=!0;this.size=[180,30]}function B(){this.addOutput("obj","object");this.size=[120,30];this._object={}}function D(){this.addInput("url","string");this.addOutput("file","string");this.addProperty("url","");this.addProperty("type","text");this.widget=this.addWidget("text","url","","url");this._data=null}function G(){this.addInput("parse",t.ACTION);this.addInput("json","string");this.addOutput("done",t.EVENT); -this.addOutput("object","object");this.widget=this.addWidget("button","parse","",this.parse.bind(this));this._obj=this._str=null}function H(){this.addOutput("data","object");this.addProperty("value","");this.widget=this.addWidget("text","json","","value");this.widgets_up=!0;this.size=[140,30];this._value=null}function I(){this._value=[];this.addInput("json","");this.addOutput("arrayOut","array");this.addOutput("length","number");this.addProperty("value","[]");this.widget=this.addWidget("text","array", -this.properties.value,"value");this.widgets_up=!0;this.size=[140,50]}function f(){this.addInput("arr","array");this.addInput("value","");this.addOutput("arr","array");this.properties={index:0};this.widget=this.addWidget("number","i",this.properties.index,"index",{precision:0,step:10,min:0})}function J(){this.addInput("array","array,table,string");this.addInput("index","number");this.addOutput("value","");this.addProperty("index",0)}function K(){this.addInput("table","table");this.addInput("row","number"); -this.addInput("col","number");this.addOutput("value","");this.addProperty("row",0);this.addProperty("column",0)}function A(){this.addInput("obj","object");this.addOutput("property",0);this.addProperty("value",0);this.widget=this.addWidget("text","prop.","",this.setValue.bind(this));this.widgets_up=!0;this.size=[140,30];this._value=null}function L(){this.addInput("obj","");this.addOutput("keys","array");this.size=[140,30]}function E(){this.addInput("obj","");this.addInput("value","");this.addOutput("obj", -"");this.properties={property:""};this.name_widget=this.addWidget("text","prop.",this.properties.property,"property")}function N(){this.addInput("A","object");this.addInput("B","object");this.addOutput("out","object");this._result={};var a=this;this.addWidget("button","clear","",function(){a._result={}});this.size=this.computeSize()}function F(){this.size=[60,30];this.addInput("in");this.addOutput("out");this.properties={varname:"myname",container:F.LITEGRAPH};this.value=null}function a(a){return a&& -null!=a.length?Number(a.length):0}function a(a){return a&&null!=a.length?Number(a.length):0}function b(){this.size=[60,30];this.addInput("data",0);this.addInput("download",t.ACTION);this.properties={filename:"data.json"};this.value=null;var a=this;this.addWidget("button","Download","",function(b){a.value&&a.downloadAsFile()})}function c(){this.size=[60,30];this.addInput("value",0,{label:""});this.value=0}function e(){this.addInput("in",0);this.addOutput("out",0);this.size=[40,30]}function d(){this.mode= -t.ON_EVENT;this.size=[80,30];this.addProperty("msg","");this.addInput("log",t.EVENT);this.addInput("msg",0)}function g(){this.mode=t.ON_EVENT;this.addProperty("msg","");this.addInput("",t.EVENT);this.widget=this.addWidget("text","Text","","msg");this.widgets_up=!0;this.size=[200,30]}function k(){this.size=[60,30];this.addProperty("onExecute","return A;");this.addInput("A",0);this.addInput("B",0);this.addOutput("out",0);this._func=null;this.data={}}function n(){this.addInput("A",0);this.addInput("B", -0);this.addOutput("true","boolean");this.addOutput("false","boolean");this.addProperty("A",1);this.addProperty("B",1);this.addProperty("OP","==","enum",{values:n.values});this.addWidget("combo","Op.",this.properties.OP,{property:"OP",values:n.values});this.size=[80,60]}var t=w.LiteGraph;m.title="Time";m.desc="Time";m.prototype.onExecute=function(){this.setOutputData(0,1E3*this.graph.globaltime);this.setOutputData(1,this.graph.globaltime)};t.registerNodeType("basic/time",m);r.title="Subgraph";r.desc= -"Graph inside a node";r.title_color="#334";r.prototype.onGetInputs=function(){return[["enabled","boolean"]]};r.prototype.onDblClick=function(a,b,c){var d=this;setTimeout(function(){c.openSubgraph(d.subgraph)},10)};r.prototype.onAction=function(a,b){this.subgraph.onAction(a,b)};r.prototype.onExecute=function(){if(this.enabled=this.getInputOrProperty("enabled")){if(this.inputs)for(var a=0;aa&&(b[0]=f?this.trigger(null,h,l):this._pending.push([f,h])};B.prototype.onExecute= -function(f,h){f=1E3*this.graph.elapsed_time;this.isInputConnected(1)&&(this.properties.time_in_ms=this.getInputData(1));for(var l=0;lr?h.xbox.axes.lx:0,this._left_axis[1]=Math.abs(h.xbox.axes.ly)>r?h.xbox.axes.ly:0,this._right_axis[0]=Math.abs(h.xbox.axes.rx)>r?h.xbox.axes.rx:0,this._right_axis[1]=Math.abs(h.xbox.axes.ry)>r?h.xbox.axes.ry:0,this._triggers[0]=Math.abs(h.xbox.axes.ltrigger)>r?h.xbox.axes.ltrigger: -0,this._triggers[1]=Math.abs(h.xbox.axes.rtrigger)>r?h.xbox.axes.rtrigger:0);if(this.outputs)for(r=0;rr;r++)if(h[r]){h=h[r];r=this.xbox_mapping;r||(r=this.xbox_mapping={axes:[], -buttons:{},hat:"",hatmap:m.CENTER});r.axes.lx=h.axes[0];r.axes.ly=h.axes[1];r.axes.rx=h.axes[2];r.axes.ry=h.axes[3];r.axes.ltrigger=h.buttons[6].value;r.axes.rtrigger=h.buttons[7].value;r.hat="";r.hatmap=m.CENTER;for(var z=0;zz)r.buttons[m.mapping_array[z]]=h.buttons[z].pressed,h.buttons[z].was_pressed&&this.trigger(m.mapping_array[z]+"_button_event");else switch(z){case 12:h.buttons[z].pressed&&(r.hat+="up",r.hatmap|=m.UP); -break;case 13:h.buttons[z].pressed&&(r.hat+="down",r.hatmap|=m.DOWN);break;case 14:h.buttons[z].pressed&&(r.hat+="left",r.hatmap|=m.LEFT);break;case 15:h.buttons[z].pressed&&(r.hat+="right",r.hatmap|=m.RIGHT);break;case 16:r.buttons.home=h.buttons[z].pressed}h.xbox=r;return h}};m.prototype.onDrawBackground=function(h){if(!this.flags.collapsed){var m=this._left_axis,r=this._right_axis;h.strokeStyle="#88A";h.strokeRect(.5*(m[0]+1)*this.size[0]-4,.5*(m[1]+1)*this.size[1]-4,8,8);h.strokeStyle="#8A8"; -h.strokeRect(.5*(r[0]+1)*this.size[0]-4,.5*(r[1]+1)*this.size[1]-4,8,8);m=this.size[1]/this._current_buttons.length;h.fillStyle="#AEB";for(r=0;r","enum",{values:F.values});this.addWidget("combo", -"Cond.",this.properties.OP,{property:"OP",values:F.values});this.size=[80,60]}function a(){this.addInput("in",0);this.addInput("cond","boolean");this.addOutput("true",0);this.addOutput("false",0);this.size=[80,60]}function b(){this.addInput("inc","number");this.addOutput("total","number");this.addProperty("increment",1);this.addProperty("value",0)}function c(){this.addInput("v","number");this.addOutput("sin","number");this.addProperty("amplitude",1);this.addProperty("offset",0);this.bgImageUrl="nodes/imgs/icon-sin.png"} -function e(){this.addInput("x","number");this.addInput("y","number");this.addOutput("","number");this.properties={x:1,y:1,formula:"x+y"};this.code_widget=this.addWidget("text","F(x,y)",this.properties.formula,function(a,b,c){c.properties.formula=a});this.addWidget("toggle","allow",q.allow_scripts,function(a){q.allow_scripts=a});this._func=null}function d(){this.addInput("vec2","vec2");this.addOutput("x","number");this.addOutput("y","number")}function g(){this.addInputs([["x","number"],["y","number"]]); -this.addOutput("vec2","vec2");this.properties={x:0,y:0};this._data=new Float32Array(2)}function k(){this.addInput("vec3","vec3");this.addOutput("x","number");this.addOutput("y","number");this.addOutput("z","number")}function n(){this.addInputs([["x","number"],["y","number"],["z","number"]]);this.addOutput("vec3","vec3");this.properties={x:0,y:0,z:0};this._data=new Float32Array(3)}function t(){this.addInput("vec4","vec4");this.addOutput("x","number");this.addOutput("y","number");this.addOutput("z", -"number");this.addOutput("w","number")}function p(){this.addInputs([["x","number"],["y","number"],["z","number"],["w","number"]]);this.addOutput("vec4","vec4");this.properties={x:0,y:0,z:0,w:0};this._data=new Float32Array(4)}var q=w.LiteGraph;m.title="Converter";m.desc="type A to type B";m.prototype.onExecute=function(){var a=this.getInputData(0);if(null!=a&&this.outputs)for(var b=0;ba&&(a+=1024);var d=Math.floor(a);a-=d;c=l.data[d];d=l.data[1023==d?0:d+1];b&&(a=a*a*a*(a*(6*a-15)+10));return c*(1-a)+d*a};l.prototype.onExecute=function(){var a=this.getInputData(0)|| -0,b=this.properties.octaves||1,c=0,d=1;a+=this.properties.seed||0;for(var e=this.properties.speed||1,f=0,g=0;gd);++g);a=this.properties.min;this._last_v=c/f*(this.properties.max-a)+a;this.setOutputData(0,this._last_v)};l.prototype.onDrawBackground=function(a){this.outputs[0].label=(this._last_v||0).toFixed(3)};q.registerNodeType("math/noise",l);C.title="Spikes";C.desc="spike every random time";C.prototype.onExecute= -function(){var a=this.graph.elapsed_time;this._remaining_time-=a;this._blink_time-=a;a=0;0this._remaining_time?(this._remaining_time=Math.random()*(this.properties.max_time-this.properties.min_time)+this.properties.min_time,this._blink_time=this.properties.duration,this.boxcolor="#FFF"):this.boxcolor="#000";this.setOutputData(0,a)};q.registerNodeType("math/spikes",C);B.title="Clamp";B.desc="Clamp number between min and max"; -B.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&(a=Math.max(this.properties.min,a),a=Math.min(this.properties.max,a),this.setOutputData(0,a))};B.prototype.getCode=function(a){a="";this.isInputConnected(0)&&(a+="clamp({{0}},"+this.properties.min+","+this.properties.max+")");return a};q.registerNodeType("math/clamp",B);D.title="Lerp";D.desc="Linear Interpolation";D.prototype.onExecute=function(){var a=this.getInputData(0);null==a&&(a=0);var b=this.getInputData(1);null==b&&(b=0); -var c=this.properties.f,d=this.getInputData(2);void 0!==d&&(c=d);this.setOutputData(0,a*(1-c)+b*c)};D.prototype.onGetInputs=function(){return[["f","number"]]};q.registerNodeType("math/lerp",D);G.title="Abs";G.desc="Absolute";G.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&this.setOutputData(0,Math.abs(a))};q.registerNodeType("math/abs",G);H.title="Floor";H.desc="Floor number to remove fractional part";H.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&this.setOutputData(0, -Math.floor(a))};q.registerNodeType("math/floor",H);I.title="Frac";I.desc="Returns fractional part";I.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&this.setOutputData(0,a%1)};q.registerNodeType("math/frac",I);f.title="Smoothstep";f.desc="Smoothstep";f.prototype.onExecute=function(){var a=this.getInputData(0);if(void 0!==a){var b=this.properties.A;a=clamp((a-b)/(this.properties.B-b),0,1);this.setOutputData(0,a*a*(3-2*a))}};q.registerNodeType("math/smoothstep",f);J.title="Scale"; -J.desc="v * factor";J.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&this.setOutputData(0,a*this.properties.factor)};q.registerNodeType("math/scale",J);K.title="Gate";K.desc="if v is true, then outputs A, otherwise B";K.prototype.onExecute=function(){var a=this.getInputData(0);this.setOutputData(0,this.getInputData(a?1:2))};q.registerNodeType("math/gate",K);A.title="Average";A.desc="Average Filter";A.prototype.onExecute=function(){var a=this.getInputData(0);null==a&&(a=0);var b= -this._values.length;this._values[this._current%b]=a;this._current+=1;this._current>b&&(this._current=0);for(var c=a=0;cb&&(b=1);this.properties.samples=Math.round(b);a=this._values;this._values=new Float32Array(this.properties.samples);a.length<=this._values.length?this._values.set(a):this._values.set(a.subarray(0,this._values.length))};q.registerNodeType("math/average",A);L.title="TendTo";L.desc="moves the output value always closer to the input"; -L.prototype.onExecute=function(){var a=this.getInputData(0);null==a&&(a=0);var b=this.properties.factor;this._value=null==this._value?a:this._value*(1-b)+a*b;this.setOutputData(0,this._value)};q.registerNodeType("math/tendTo",L);E.values="+ - * / % ^ max min".split(" ");E.funcs={"+":function(a,b){return a+b},"-":function(a,b){return a-b},x:function(a,b){return a*b},X:function(a,b){return a*b},"*":function(a,b){return a*b},"/":function(a,b){return a/b},"%":function(a,b){return a%b},"^":function(a, -b){return Math.pow(a,b)},max:function(a,b){return Math.max(a,b)},min:function(a,b){return Math.min(a,b)}};E.title="Operation";E.desc="Easy math operators";E["@OP"]={type:"enum",title:"operation",values:E.values};E.size=[100,60];E.prototype.getTitle=function(){return"max"==this.properties.OP||"min"==this.properties.OP?this.properties.OP+"(A,B)":"A "+this.properties.OP+" B"};E.prototype.setValue=function(a){"string"==typeof a&&(a=parseFloat(a));this.properties.value=a};E.prototype.onPropertyChanged= -function(a,b){"OP"==a&&(this._func=E.funcs[this.properties.OP],this._func||(console.warn("Unknown operation: "+this.properties.OP),this._func=function(a){return a}))};E.prototype.onExecute=function(){var a=this.getInputData(0),b=this.getInputData(1);null!=a?a.constructor===Number&&(this.properties.A=a):a=this.properties.A;null!=b?this.properties.B=b:b=this.properties.B;var c=E.funcs[this.properties.OP];if(a.constructor===Number)var d=c(a,b);else if(a.constructor===Array){d=this._result;d.length=a.length; -for(var e=0;eB":f=a>b;break;case "A=B":f=a>=b}this.setOutputData(c,f)}}};N.prototype.onGetOutputs=function(){return[["A==B","boolean"],["A!=B","boolean"],["A>B","boolean"],["A=B","boolean"],["A<=B","boolean"]]};q.registerNodeType("math/compare",N);q.registerSearchboxExtra("math/compare","==",{outputs:[["A==B","boolean"]],title:"A==B"});q.registerSearchboxExtra("math/compare","!=",{outputs:[["A!=B","boolean"]],title:"A!=B"});q.registerSearchboxExtra("math/compare",">",{outputs:[["A>B","boolean"]], -title:"A>B"});q.registerSearchboxExtra("math/compare","<",{outputs:[["A=",{outputs:[["A>=B","boolean"]],title:"A>=B"});q.registerSearchboxExtra("math/compare","<=",{outputs:[["A<=B","boolean"]],title:"A<=B"});F.values="> < == != <= >= || &&".split(" ");F["@OP"]={type:"enum",title:"operation",values:F.values};F.title="Condition";F.desc="evaluates condition between A and B";F.prototype.getTitle=function(){return"A "+this.properties.OP+ -" B"};F.prototype.onExecute=function(){var a=this.getInputData(0);void 0===a?a=this.properties.A:this.properties.A=a;var b=this.getInputData(1);void 0===b?b=this.properties.B:this.properties.B=b;var c=!0;switch(this.properties.OP){case ">":c=a>b;break;case "<":c=a=":c=a>=b;break;case "||":c=a||b;break;case "&&":c=a&&b}this.setOutputData(0,c);this.setOutputData(1,!c)};q.registerNodeType("math/condition",F);a.title= -"Branch";a.desc="If condition is true, outputs IN in true, otherwise in false";a.prototype.onExecute=function(){var a=this.getInputData(0);this.getInputData(1)?(this.setOutputData(0,a),this.setOutputData(1,null)):(this.setOutputData(0,null),this.setOutputData(1,a))};q.registerNodeType("math/branch",a);b.title="Accumulate";b.desc="Increments a value every time";b.prototype.onExecute=function(){null===this.properties.value&&(this.properties.value=0);var a=this.getInputData(0);this.properties.value= -null!==a?this.properties.value+a:this.properties.value+this.properties.increment;this.setOutputData(0,this.properties.value)};q.registerNodeType("math/accumulate",b);c.title="Trigonometry";c.desc="Sin Cos Tan";c.prototype.onExecute=function(){var a=this.getInputData(0);null==a&&(a=0);var b=this.properties.amplitude,c=this.findInputSlot("amplitude");-1!=c&&(b=this.getInputData(c));var d=this.properties.offset;c=this.findInputSlot("offset");-1!=c&&(d=this.getInputData(c));c=0;for(var e=this.outputs.length;c< -e;++c){switch(this.outputs[c].name){case "sin":var f=Math.sin(a);break;case "cos":f=Math.cos(a);break;case "tan":f=Math.tan(a);break;case "asin":f=Math.asin(a);break;case "acos":f=Math.acos(a);break;case "atan":f=Math.atan(a)}this.setOutputData(c,b*f+d)}};c.prototype.onGetInputs=function(){return[["v","number"],["amplitude","number"],["offset","number"]]};c.prototype.onGetOutputs=function(){return[["sin","number"],["cos","number"],["tan","number"],["asin","number"],["acos","number"],["atan","number"]]}; -q.registerNodeType("math/trigonometry",c);q.registerSearchboxExtra("math/trigonometry","SIN()",{outputs:[["sin","number"]],title:"SIN()"});q.registerSearchboxExtra("math/trigonometry","COS()",{outputs:[["cos","number"]],title:"COS()"});q.registerSearchboxExtra("math/trigonometry","TAN()",{outputs:[["tan","number"]],title:"TAN()"});e.title="Formula";e.desc="Compute formula";e.size=[160,100];A.prototype.onPropertyChanged=function(a,b){"formula"==a&&(this.code_widget.value=b)};e.prototype.onExecute= -function(){if(q.allow_scripts){var a=this.getInputData(0),b=this.getInputData(1);null!=a?this.properties.x=a:a=this.properties.x;null!=b?this.properties.y=b:b=this.properties.y;try{this._func&&this._func_code==this.properties.formula||(this._func=new Function("x","y","TIME","return "+this.properties.formula),this._func_code=this.properties.formula);var c=this._func(a,b,this.graph.globaltime);this.boxcolor=null}catch(v){this.boxcolor="red"}this.setOutputData(0,c)}};e.prototype.getTitle=function(){return this._func_code|| -"Formula"};e.prototype.onDrawBackground=function(){var a=this.properties.formula;this.outputs&&this.outputs.length&&(this.outputs[0].label=a)};q.registerNodeType("math/formula",e);d.title="Vec2->XY";d.desc="vector 2 to components";d.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&(this.setOutputData(0,a[0]),this.setOutputData(1,a[1]))};q.registerNodeType("math3d/vec2-to-xy",d);g.title="XY->Vec2";g.desc="components to vector2";g.prototype.onExecute=function(){var a=this.getInputData(0); -null==a&&(a=this.properties.x);var b=this.getInputData(1);null==b&&(b=this.properties.y);var c=this._data;c[0]=a;c[1]=b;this.setOutputData(0,c)};q.registerNodeType("math3d/xy-to-vec2",g);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]))};q.registerNodeType("math3d/vec3-to-xyz",k);n.title="XYZ->Vec3";n.desc="components to vector3";n.prototype.onExecute= -function(){var a=this.getInputData(0);null==a&&(a=this.properties.x);var b=this.getInputData(1);null==b&&(b=this.properties.y);var c=this.getInputData(2);null==c&&(c=this.properties.z);var d=this._data;d[0]=a;d[1]=b;d[2]=c;this.setOutputData(0,d)};q.registerNodeType("math3d/xyz-to-vec3",n);t.title="Vec4->XYZW";t.desc="vector 4 to components";t.prototype.onExecute=function(){var a=this.getInputData(0);null!=a&&(this.setOutputData(0,a[0]),this.setOutputData(1,a[1]),this.setOutputData(2,a[2]),this.setOutputData(3, -a[3]))};q.registerNodeType("math3d/vec4-to-xyzw",t);p.title="XYZW->Vec4";p.desc="components to vector4";p.prototype.onExecute=function(){var a=this.getInputData(0);null==a&&(a=this.properties.x);var b=this.getInputData(1);null==b&&(b=this.properties.y);var c=this.getInputData(2);null==c&&(c=this.properties.z);var d=this.getInputData(3);null==d&&(d=this.properties.w);var e=this._data;e[0]=a;e[1]=b;e[2]=c;e[3]=d;this.setOutputData(0,e)};q.registerNodeType("math3d/xyzw-to-vec4",p)})(this); -(function(w){function m(){this.addInput("","string");this.addOutput("table","table");this.addOutput("rows","number");this.addProperty("value","");this.addProperty("separator",",");this._table=null}w=w.LiteGraph;w.wrapFunctionAsNode("string/toString",function(m){if(m&&m.constructor===Object)try{return JSON.stringify(m)}catch(h){}return String(m)},[""],"string");w.wrapFunctionAsNode("string/compare",function(m,h){return m==h},["string","string"],"boolean");w.wrapFunctionAsNode("string/concatenate", -function(m,h){return void 0===m?h:void 0===h?m:m+h},["string","string"],"string");w.wrapFunctionAsNode("string/contains",function(m,h){return void 0===m||void 0===h?!1:-1!=m.indexOf(h)},["string","string"],"boolean");w.wrapFunctionAsNode("string/toUpperCase",function(m){return null!=m&&m.constructor===String?m.toUpperCase():m},["string"],"string");w.wrapFunctionAsNode("string/split",function(m,h){null==h&&(h=this.properties.separator);if(null==m)return[];if(m.constructor===String)return m.split(h|| -" ");if(m.constructor===Array){for(var r=[],z=0;z