diff --git a/css/litegraph-editor.css b/css/litegraph-editor.css
index 4b1239602..130ac0b97 100755
--- a/css/litegraph-editor.css
+++ b/css/litegraph-editor.css
@@ -1,213 +1,214 @@
-.litegraph-editor {
- width: 100%;
- height: 100%;
- margin: 0;
- padding: 0;
-
- background-color: #333;
- color: #eee;
- font: 14px Tahoma;
-
- position: relative;
-}
-
-.litegraph-editor h1 {
- font-family: "Metro Light", Tahoma;
- color: #ddd;
- font-size: 28px;
- padding-left: 10px;
- /*text-shadow: 0 1px 1px #333, 0 -1px 1px #777;*/
- margin: 0;
- font-weight: normal;
-}
-
-.litegraph-editor h1 span {
- font-family: "Arial";
- font-size: 14px;
- font-weight: normal;
- color: #aaa;
-}
-
-.litegraph-editor h2 {
- font-family: "Metro Light";
- padding: 5px;
- margin-left: 10px;
-}
-
-.litegraph-editor * {
- box-sizing: border-box;
- -moz-box-sizing: border-box;
-}
-
-.litegraph-editor .content {
- position: relative;
- width: 100%;
- height: calc(100% - 80px);
- background-color: #1a1a1a;
-}
-
-.litegraph-editor .header,
-.litegraph-editor .footer {
- position: relative;
- height: 40px;
- background-color: #333;
- /*border-radius: 10px 10px 0 0;*/
-}
-
-.litegraph-editor .tools,
-.litegraph-editor .tools-left,
-.litegraph-editor .tools-right {
- position: absolute;
- top: 2px;
- right: 0px;
- vertical-align: top;
-
- margin: 2px 5px 0 0px;
-}
-
-.litegraph-editor .tools-left {
- right: auto;
- left: 4px;
-}
-
-.litegraph-editor .footer {
- height: 40px;
- position: relative;
- /*border-radius: 0 0 10px 10px;*/
-}
-
-.litegraph-editor .miniwindow {
- background-color: #333;
- border: 1px solid #111;
-}
-
-.litegraph-editor .miniwindow .corner-button {
- position: absolute;
- top: 2px;
- right: 2px;
- font-family: "Tahoma";
- font-size: 14px;
- color: #aaa;
- cursor: pointer;
-}
-
-/* BUTTONS **********************/
-
-.litegraph-editor .btn {
- /*font-family: "Metro Light";*/
- color: #ccc;
- font-size: 20px;
- min-width: 30px;
- /*border-radius: 0.3em;*/
- border: 0 solid #666;
- background-color: #3f3f3f;
- /*box-shadow: 0 0 3px black;*/
- padding: 4px 10px;
- cursor: pointer;
- transition: all 1s;
- -moz-transition: all 1s;
- -webkit-transition: all 0.4s;
-}
-
-.litegraph-editor button:hover {
- background-color: #999;
- color: #fff;
- transition: all 1s;
- -moz-transition: all 1s;
- -webkit-transition: all 0.4s;
-}
-
-.litegraph-editor button:active {
- background-color: white;
-}
-
-.litegraph-editor button.fixed {
- position: absolute;
- top: 5px;
- right: 5px;
- font-size: 1.2em;
-}
-
-.litegraph-editor button img {
- margin: -4px;
- vertical-align: top;
- opacity: 0.8;
- transition: all 1s;
-}
-
-.litegraph-editor button:hover img {
- opacity: 1;
-}
-
-.litegraph-editor .header button {
- height: 32px;
- vertical-align: top;
-}
-
-.litegraph-editor .footer button {
- /*font-size: 16px;*/
-}
-
-.litegraph-editor .toolbar-widget {
- display: inline-block;
-}
-
-.litegraph-editor .editor-area {
- width: 100%;
- height: 100%;
-}
-
-/* METER *********************/
-
-.litegraph-editor .loadmeter {
- font-family: "Tahoma";
- color: #aaa;
- font-size: 12px;
- border-radius: 2px;
- width: 130px;
- vertical-align: top;
-}
-
-.litegraph-editor .strong {
- vertical-align: top;
- padding: 3px;
- width: 30px;
- display: inline-block;
- line-height: 8px;
-}
-
-.litegraph-editor .cpuload .bgload,
-.litegraph-editor .gpuload .bgload {
- display: inline-block;
- width: 90px;
- height: 15px;
- background-image: url("../editor/imgs/load-progress-empty.png");
-}
-
-.litegraph-editor .cpuload .fgload,
-.litegraph-editor .gpuload .fgload {
- display: inline-block;
- width: 4px;
- height: 15px;
- max-width: 90px;
- background-image: url("../editor/imgs/load-progress-full.png");
-}
-
-.litegraph-editor textarea.code, .litegraph-editor div.code {
- height: 100%;
- width: 100%;
- background-color: black;
- padding: 4px;
- font: 16px monospace;
- overflow: auto;
- resize: none;
- outline: none;
-}
-
-.litegraph-editor .codeflask {
- background-color: #2a2a2a;
-}
-
-.litegraph-editor .codeflask textarea {
- opacity: 0;
+.litegraph-editor {
+ width: 100%;
+ height: 100%;
+ margin: 0;
+ padding: 0;
+
+ background-color: #333;
+ color: #eee;
+ font: 14px Tahoma;
+
+ position: relative;
+}
+
+.litegraph-editor h1 {
+ font-family: "Metro Light", Tahoma;
+ color: #ddd;
+ font-size: 28px;
+ padding-left: 10px;
+ /*text-shadow: 0 1px 1px #333, 0 -1px 1px #777;*/
+ margin: 0;
+ font-weight: normal;
+}
+
+.litegraph-editor h1 span {
+ font-family: "Arial";
+ font-size: 14px;
+ font-weight: normal;
+ color: #aaa;
+}
+
+.litegraph-editor h2 {
+ font-family: "Metro Light";
+ padding: 5px;
+ margin-left: 10px;
+}
+
+.litegraph-editor * {
+ box-sizing: border-box;
+ -moz-box-sizing: border-box;
+}
+
+.litegraph-editor .content {
+ position: relative;
+ width: 100%;
+ height: calc(100% - 80px);
+ background-color: #1a1a1a;
+}
+
+.litegraph-editor .header,
+.litegraph-editor .footer {
+ position: relative;
+ height: 40px;
+ background-color: #333;
+ /*border-radius: 10px 10px 0 0;*/
+}
+
+.litegraph-editor .tools,
+.litegraph-editor .tools-left,
+.litegraph-editor .tools-right {
+ position: absolute;
+ top: 2px;
+ right: 0px;
+ vertical-align: top;
+
+ margin: 2px 5px 0 0px;
+}
+
+.litegraph-editor .tools-left {
+ right: auto;
+ left: 4px;
+}
+
+.litegraph-editor .footer {
+ height: 40px;
+ position: relative;
+ /*border-radius: 0 0 10px 10px;*/
+}
+
+.litegraph-editor .miniwindow {
+ background-color: #333;
+ border: 1px solid #111;
+}
+
+.litegraph-editor .miniwindow .corner-button {
+ position: absolute;
+ top: 2px;
+ right: 2px;
+ font-family: "Tahoma";
+ font-size: 14px;
+ color: #aaa;
+ cursor: pointer;
+}
+
+/* BUTTONS **********************/
+
+.litegraph-editor .btn {
+ /*font-family: "Metro Light";*/
+ color: #ccc;
+ font-size: 20px;
+ min-width: 30px;
+ /*border-radius: 0.3em;*/
+ border: 0 solid #666;
+ background-color: #3f3f3f;
+ /*box-shadow: 0 0 3px black;*/
+ padding: 4px 10px;
+ cursor: pointer;
+ transition: all 1s;
+ -moz-transition: all 1s;
+ -webkit-transition: all 0.4s;
+}
+
+.litegraph-editor button:hover {
+ background-color: #999;
+ color: #fff;
+ transition: all 1s;
+ -moz-transition: all 1s;
+ -webkit-transition: all 0.4s;
+}
+
+.litegraph-editor button:active {
+ background-color: white;
+}
+
+.litegraph-editor button.fixed {
+ position: absolute;
+ top: 5px;
+ right: 5px;
+ font-size: 1.2em;
+}
+
+.litegraph-editor button img {
+ margin: -4px;
+ vertical-align: top;
+ opacity: 0.8;
+ transition: all 1s;
+}
+
+.litegraph-editor button:hover img {
+ opacity: 1;
+}
+
+.litegraph-editor .header button {
+ height: 32px;
+ vertical-align: top;
+}
+
+.litegraph-editor .footer button {
+ /*font-size: 16px;*/
+}
+
+.litegraph-editor .toolbar-widget {
+ display: inline-block;
+}
+
+.litegraph-editor .editor-area {
+ width: 100%;
+ height: 100%;
+}
+
+/* METER *********************/
+
+.litegraph-editor .loadmeter {
+ font-family: "Tahoma";
+ color: #aaa;
+ font-size: 12px;
+ border-radius: 2px;
+ width: 130px;
+ vertical-align: top;
+}
+
+.litegraph-editor .strong {
+ vertical-align: top;
+ padding: 3px;
+ width: 30px;
+ display: inline-block;
+ line-height: 8px;
+}
+
+.litegraph-editor .cpuload .bgload,
+.litegraph-editor .gpuload .bgload {
+ display: inline-block;
+ width: 90px;
+ height: 15px;
+ background-image: url("../editor/imgs/load-progress-empty.png");
+}
+
+.litegraph-editor .cpuload .fgload,
+.litegraph-editor .gpuload .fgload {
+ display: inline-block;
+ width: 4px;
+ height: 15px;
+ max-width: 90px;
+ background-image: url("../editor/imgs/load-progress-full.png");
+}
+
+.litegraph-editor textarea.code, .litegraph-editor div.code {
+ height: 100%;
+ width: 100%;
+ background-color: black;
+ padding: 4px;
+ font: 16px monospace;
+ overflow: auto;
+ resize: none;
+ outline: none;
+ color: #DDD;
+}
+
+.litegraph-editor .codeflask {
+ background-color: #2a2a2a;
+}
+
+.litegraph-editor .codeflask textarea {
+ opacity: 0;
}
\ No newline at end of file
diff --git a/css/litegraph.css b/css/litegraph.css
index b32be79bf..918858f41 100755
--- a/css/litegraph.css
+++ b/css/litegraph.css
@@ -206,6 +206,20 @@
padding-top: 2px;
}
+.litegraph.lite-search-item.not_in_filter{
+ /*background-color: rgba(50, 50, 50, 0.5);*/
+ /*color: #999;*/
+ color: #B99;
+ font-style: italic;
+}
+
+.litegraph.lite-search-item.generic_type{
+ /*background-color: rgba(50, 50, 50, 0.5);*/
+ /*color: #DD9;*/
+ color: #999;
+ font-style: italic;
+}
+
.litegraph.lite-search-item:hover,
.litegraph.lite-search-item.selected {
cursor: pointer;
@@ -235,6 +249,18 @@
top: 10px;
height: calc( 100% - 20px );
margin: auto;
+ max-width: 50%;
+}
+
+.litegraph .dialog.centered {
+ top: 50px;
+ left: 50%;
+ position: absolute;
+ transform: translateX(-50%);
+ min-width: 600px;
+ min-height: 300px;
+ height: calc( 100% - 100px );
+ margin: auto;
}
.litegraph .dialog .close {
@@ -264,13 +290,14 @@
display: inline-block;
}
-.litegraph .dialog .dialog-content {
+.litegraph .dialog .dialog-content, .litegraph .dialog .dialog-alt-content {
height: calc(100% - 90px);
width: 100%;
min-height: 100px;
display: inline-block;
color: #AAA;
/*background-color: black;*/
+ overflow: auto;
}
.litegraph .dialog .dialog-content h3 {
@@ -315,14 +342,23 @@
padding: 4px;
}
+.litegraph .dialog .property:hover {
+ background: #545454;
+}
+
.litegraph .dialog .property_name {
color: #737373;
display: inline-block;
text-align: left;
vertical-align: top;
- width: 120px;
+ width: 160px;
padding-left: 4px;
overflow: hidden;
+ margin-right: 6px;
+}
+
+.litegraph .dialog .property:hover .property_name {
+ color: white;
}
.litegraph .dialog .property_value {
@@ -330,8 +366,11 @@
text-align: right;
color: #AAA;
background-color: #1A1A1A;
- width: calc( 100% - 122px );
+ /*width: calc( 100% - 122px );*/
+ max-width: calc( 100% - 162px );
+ min-width: 200px;
max-height: 300px;
+ min-height: 20px;
padding: 4px;
padding-right: 12px;
overflow: hidden;
@@ -345,6 +384,16 @@
.litegraph .dialog .property.boolean .property_value {
padding-right: 30px;
+ color: #A88;
+ /*width: auto;
+ float: right;*/
+}
+
+.litegraph .dialog .property.boolean.bool-on .property_name{
+ color: #8A8;
+}
+.litegraph .dialog .property.boolean.bool-on .property_value{
+ color: #8A8;
}
.litegraph .dialog .btn {
@@ -515,7 +564,7 @@
position: absolute;
top: 10px;
left: 10px;
- /*min-height: 2em;*/
+ min-height: 2em;
background-color: #333;
font-size: 1.2em;
box-shadow: 0 0 10px black !important;
diff --git a/editor/editor_mobile.html b/editor/editor_mobile.html
new file mode 100644
index 000000000..da5b1e104
--- /dev/null
+++ b/editor/editor_mobile.html
@@ -0,0 +1,144 @@
+
+
+
+
+ LiteGraph
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/editor/index.html b/editor/index.html
index 01dce5054..c43922b45 100755
--- a/editor/index.html
+++ b/editor/index.html
@@ -1,44 +1,47 @@
-
-
-
- LiteGraph
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+ LiteGraph
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/editor/js/code.js b/editor/js/code.js
index 28c8dc442..fe9e89076 100644
--- a/editor/js/code.js
+++ b/editor/js/code.js
@@ -1,6 +1,7 @@
var webgl_canvas = null;
LiteGraph.node_images_path = "../nodes_data/";
+
var editor = new LiteGraph.Editor("main",{miniwindow:false});
window.graphcanvas = editor.graphcanvas;
window.graph = editor.graph;
@@ -19,8 +20,10 @@ LiteGraph.allow_scripts = true;
//create scene selector
var elem = document.createElement("span");
+elem.id = "LGEditorTopBarSelector";
elem.className = "selector";
-elem.innerHTML = "Demo | ";
+elem.innerHTML = "";
+elem.innerHTML += "Demo | ";
editor.tools.appendChild(elem);
var select = elem.querySelector("select");
select.addEventListener("change", function(e){
@@ -167,4 +170,4 @@ function enableWebGL()
gl.viewport(0,0,gl.canvas.width, gl.canvas.height );
}
}
-}
+}
\ No newline at end of file
diff --git a/editor/js/defaults.js b/editor/js/defaults.js
new file mode 100644
index 000000000..3709e645a
--- /dev/null
+++ b/editor/js/defaults.js
@@ -0,0 +1,31 @@
+
+LiteGraph.debug = false;
+LiteGraph.catch_exceptions = true;
+LiteGraph.throw_errors = true;
+LiteGraph.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
+
+LiteGraph.searchbox_extras = {}; //used to add extra features to the search box
+LiteGraph.auto_sort_node_types = true; // [true!] If set to true; will automatically sort node types / categories in the context menus
+LiteGraph.node_box_coloured_when_on = true; // [true!] this make the nodes box (top left circle) coloured when triggered (execute/action); visual feedback
+LiteGraph.node_box_coloured_by_mode = true; // [true!] nodebox based on node mode; visual feedback
+LiteGraph.dialog_close_on_mouse_leave = true; // [false on mobile] better true if not touch device;
+LiteGraph.dialog_close_on_mouse_leave_delay = 500;
+LiteGraph.shift_click_do_break_link_from = false; // [false!] prefer false if results too easy to break links
+LiteGraph.click_do_break_link_to = false; // [false!]prefer false; way too easy to break links
+LiteGraph.search_hide_on_mouse_leave = true; // [false on mobile] better true if not touch device;
+LiteGraph.search_filter_enabled = true; // [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]
+LiteGraph.search_show_all_on_open = true; // [true!] opens the results list when opening the search widget
+
+LiteGraph.auto_load_slot_types = true; // [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
+LiteGraph.registered_slot_in_types = {}; // slot types for nodeclass
+LiteGraph.registered_slot_out_types = {}; // slot types for nodeclass
+LiteGraph.slot_types_in = []; // slot types IN
+LiteGraph.slot_types_out = []; // slot types OUT*/
+
+LiteGraph.alt_drag_do_clone_nodes = true; // [true!] very handy; ALT click to clone and drag the new node
+LiteGraph.do_add_triggers_slots = true; // [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
+LiteGraph.allow_multi_output_for_events = false; // [false!] being events; it is strongly reccomended to use them sequentually; one by one
+LiteGraph.middle_click_slot_add_default_node = true; //[true!] allows to create and connect a ndoe clicking with the third button (wheel)
+LiteGraph.release_link_on_empty_shows_menu = true; //[true!] dragging a link to empty space will open a menu, add from list, search or defaults
+LiteGraph.pointerevents_method = "pointer"; // "mouse"|"pointer" use mouse for retrocompatibility issues? (none found @ now)
\ No newline at end of file
diff --git a/editor/js/defaults_mobile.js b/editor/js/defaults_mobile.js
new file mode 100644
index 000000000..d4ef831c7
--- /dev/null
+++ b/editor/js/defaults_mobile.js
@@ -0,0 +1,31 @@
+
+LiteGraph.debug = false;
+LiteGraph.catch_exceptions = true;
+LiteGraph.throw_errors = true;
+LiteGraph.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
+
+LiteGraph.searchbox_extras = {}; //used to add extra features to the search box
+LiteGraph.auto_sort_node_types = true; // [true!] If set to true; will automatically sort node types / categories in the context menus
+LiteGraph.node_box_coloured_when_on = true; // [true!] this make the nodes box (top left circle) coloured when triggered (execute/action); visual feedback
+LiteGraph.node_box_coloured_by_mode = true; // [true!] nodebox based on node mode; visual feedback
+LiteGraph.dialog_close_on_mouse_leave = false; // [false on mobile] better true if not touch device;
+LiteGraph.dialog_close_on_mouse_leave_delay = 500;
+LiteGraph.shift_click_do_break_link_from = false; // [false!] prefer false if results too easy to break links
+LiteGraph.click_do_break_link_to = false; // [false!]prefer false; way too easy to break links
+LiteGraph.search_hide_on_mouse_leave = false; // [false on mobile] better true if not touch device;
+LiteGraph.search_filter_enabled = true; // [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]
+LiteGraph.search_show_all_on_open = true; // [true!] opens the results list when opening the search widget
+
+LiteGraph.auto_load_slot_types = true; // [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
+LiteGraph.registered_slot_in_types = {}; // slot types for nodeclass
+LiteGraph.registered_slot_out_types = {}; // slot types for nodeclass
+LiteGraph.slot_types_in = []; // slot types IN
+LiteGraph.slot_types_out = []; // slot types OUT*/
+
+LiteGraph.alt_drag_do_clone_nodes = true; // [true!] very handy; ALT click to clone and drag the new node
+LiteGraph.do_add_triggers_slots = true; // [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
+LiteGraph.allow_multi_output_for_events = false; // [false!] being events; it is strongly reccomended to use them sequentually; one by one
+LiteGraph.middle_click_slot_add_default_node = true; //[true!] allows to create and connect a ndoe clicking with the third button (wheel)
+LiteGraph.release_link_on_empty_shows_menu = true; //[true!] dragging a link to empty space will open a menu, add from list, search or defaults
+LiteGraph.pointerevents_method = "pointer"; // "mouse"|"pointer" use mouse for retrocompatibility issues? (none found @ now)
\ No newline at end of file
diff --git a/src/litegraph.js b/src/litegraph.js
old mode 100755
new mode 100644
index a9fa0061c..f372fb65c
--- a/src/litegraph.js
+++ b/src/litegraph.js
@@ -56,6 +56,7 @@
CIRCLE_SHAPE: 3,
CARD_SHAPE: 4,
ARROW_SHAPE: 5,
+ GRID_SHAPE: 6, // intended for slot arrays
//enums
INPUT: 1,
@@ -64,6 +65,8 @@
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
ALWAYS: 0,
ON_EVENT: 1,
NEVER: 2,
@@ -75,6 +78,7 @@
RIGHT: 4,
CENTER: 5,
+ LINK_RENDER_MODES: ["Straight", "Linear", "Spline"], // helper
STRAIGHT_LINK: 0,
LINEAR_LINK: 1,
SPLINE_LINK: 2,
@@ -97,11 +101,44 @@
Globals: {}, //used to store vars between graphs
searchbox_extras: {}, //used to add extra features to the search box
- auto_sort_node_types: false, // If set to true, will automatically sort node types / categories in the context menus
+ 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_by_mode: false, // [true!] nodebox based on node mode, visual feedback
- pointerevents_method: "pointer", // "mouse"|"pointer" use mouse for retrocompatibility issues
+ 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
+ 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) deafult 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) deafult 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 sequentually, 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: "pointer", // "mouse"|"pointer" use mouse for retrocompatibility issues? (none found @ now)
// TODO implement pointercancel, gotpointercapture, lostpointercapture, (pointerover, pointerout if necessary)
-
+
/**
* Register a node class so it can be listed when the user wants to create a new one
* @method registerNodeType
@@ -222,6 +259,10 @@
this.node_types_by_file_extension[ ext.toLowerCase() ] = base_class;
}
}
+
+ // TODO one would want to know input and ouput :: this would allow trought registerNodeAndSlotType to get all the slots types
+ //console.debug("Registering "+type);
+ if (this.auto_load_slot_types) nodeTmp = new base_class(base_class.title || "tmpnode");
},
/**
@@ -238,6 +279,50 @@
delete this.Nodes[base_class.constructor.name];
},
+ /**
+ * Save a slot type and his node
+ * @method registerSlotType
+ * @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){
+ out = out || false;
+ var base_class = type.constructor === String && this.registered_node_types[type] !== "anonymous" ? this.registered_node_types[type] : type;
+
+ var sCN = base_class.constructor.type;
+
+ if (typeof slot_type == "string"){
+ var aTypes = slot_type.split(",");
+ }else if (slot_type == this.EVENT || slot_type == this.ACTION){
+ var aTypes = ["_event_"];
+ }else{
+ var aTypes = ["*"];
+ }
+
+ for (var i = 0; i < aTypes.length; ++i) {
+ var sT = aTypes[i]; //.toLowerCase();
+ if (sT === ""){
+ sT = "*";
+ }
+ var registerTo = out ? "registered_slot_out_types" : "registered_slot_in_types";
+ if (typeof this[registerTo][sT] == "undefined") this[registerTo][sT] = {nodes: []};
+ this[registerTo][sT].nodes.push(sCN);
+
+ // check if is a new type
+ if (!out){
+ if (!this.slot_types_in.includes(sT.toLowerCase())){
+ this.slot_types_in.push(sT.toLowerCase());
+ this.slot_types_in.sort();
+ }
+ }else{
+ if (!this.slot_types_out.includes(sT.toLowerCase())){
+ this.slot_types_out.push(sT.toLowerCase());
+ this.slot_types_out.sort();
+ }
+ }
+ }
+ },
+
/**
* 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.
@@ -384,6 +469,11 @@
}
}
+ // callback
+ if ( node.onNodeCreated ) {
+ node.onNodeCreated();
+ }
+
return node;
},
@@ -521,11 +611,13 @@
* @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 || //generic output
- !type_b || //generic input
- type_a == type_b || //same type (is valid for triggers)
- (type_a == LiteGraph.EVENT && type_b == LiteGraph.ACTION)
+ !type_a //generic output
+ || !type_b // generic input
+ || type_a == type_b //same type (is valid for triggers)
+ || (type_a == LiteGraph.EVENT && type_b == LiteGraph.ACTION)
) {
return true;
}
@@ -546,7 +638,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 (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;
}
}
@@ -748,6 +841,10 @@
this.catch_errors = true;
+ this.nodes_executing = [];
+ this.nodes_actioning = [];
+ this.nodes_executedAction = [];
+
//subgraph_data
this.inputs = {};
this.outputs = {};
@@ -904,7 +1001,8 @@
for (var j = 0; j < limit; ++j) {
var node = nodes[j];
if (node.mode == LiteGraph.ALWAYS && node.onExecute) {
- node.onExecute(); //hard to send elapsed time
+ //wrap node.onExecute();
+ node.doExecute();
}
}
@@ -960,6 +1058,9 @@
this.iteration += 1;
this.elapsed_time = (now - this.last_update_time) * 0.001;
this.last_update_time = now;
+ this.nodes_executing = [];
+ this.nodes_actioning = [];
+ this.nodes_executedAction = [];
};
/**
@@ -1373,7 +1474,7 @@
return;
} //cannot be removed
- this.beforeChange(); //sure?
+ this.beforeChange(); //sure? - almost sure is wrong
//disconnect inputs
if (node.inputs) {
@@ -1433,7 +1534,7 @@
this.sendActionToCanvas("checkPanels");
this.setDirtyCanvas(true, true);
- this.afterChange(); //sure?
+ this.afterChange(); //sure? - almost sure is wrong
this.change();
this.updateExecutionOrder();
@@ -1528,13 +1629,19 @@
*/
LGraph.prototype.getNodeOnPos = function(x, y, nodes_list, margin) {
nodes_list = nodes_list || this._nodes;
+ var nRet = null;
for (var i = nodes_list.length - 1; i >= 0; i--) {
var n = nodes_list[i];
if (n.isPointInside(x, y, margin)) {
- return n;
+ // check for lesser interest nodes (TODO check for overlapping, use the top)
+ /*if (typeof n == "LGraphGroup"){
+ nRet = n;
+ }else{*/
+ return n;
+ /*}*/
}
}
- return null;
+ return nRet;
};
/**
@@ -1586,7 +1693,7 @@
// ********** GLOBALS *****************
- LGraph.prototype.onAction = function(action, param) {
+ LGraph.prototype.onAction = function(action, param, options) {
this._input_nodes = this.findNodesByClass(
LiteGraph.GraphInput,
this._input_nodes
@@ -1596,7 +1703,8 @@
if (node.properties.name != action) {
continue;
}
- node.onAction(action, param);
+ //wrap node.onAction(action, param);
+ node.actionDo(action, param, options);
break;
}
};
@@ -2938,13 +3046,130 @@
return r;
};
+ LGraphNode.prototype.addOnTriggerInput = function(){
+ var trigS = this.findInputSlot("onTrigger");
+ 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(){
+ var trigS = this.findOutputSlot("onExecuted");
+ 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){
+ var trigS = this.findOutputSlot("onExecuted");
+ 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){
+ 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;
+ };
+
+ /**
+ * Triggers the node code execution, place a boolean/counter to mark the node as being executed
+ * @method execute
+ * @param {*} param
+ * @param {*} options
+ */
+ LGraphNode.prototype.doExecute = function(param, options) {
+ options = options || {};
+ 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);
+
+ this.onExecute(param, options);
+
+ this.graph.nodes_executing[this.id] = false; //.pop();
+
+ // save execution/action ref
+ this.exec_version = this.graph.iteration;
+ if(options && options.action_call){
+ this.action_call = options.action_call; // if (param)
+ this.graph.nodes_executedAction[this.id] = options.action_call;
+ }
+ }
+ 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
+ * @param {String} action name
+ * @param {*} param
+ */
+ LGraphNode.prototype.actionDo = function(action, param, options) {
+ options = options || {};
+ 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.onAction(action, param, options);
+
+ this.graph.nodes_actioning[this.id] = false; //.pop();
+
+ // save execution/action ref
+ if(options && options.action_call){
+ this.action_call = options.action_call; // if (param)
+ this.graph.nodes_executedAction[this.id] = options.action_call;
+ }
+ }
+ 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
* @param {String} event name ( "on_play", ... ) if action is equivalent to false then the event is send to all
* @param {*} param
*/
- LGraphNode.prototype.trigger = function(action, param) {
+ LGraphNode.prototype.trigger = function(action, param, options) {
if (!this.outputs || !this.outputs.length) {
return;
}
@@ -2956,18 +3181,19 @@
var output = this.outputs[i];
if ( !output || output.type !== LiteGraph.EVENT || (action && output.name != action) )
continue;
- this.triggerSlot(i, param);
+ this.triggerSlot(i, param, null, options);
}
};
/**
- * Triggers an slot event in this node
+ * Triggers a slot event in this node: cycle output slots and launch execute/action on connected nodes
* @method triggerSlot
* @param {Number} slot the index of the output slot
* @param {*} param
* @param {Number} link_id [optional] in case you want to trigger and specific output link in a slot
*/
- LGraphNode.prototype.triggerSlot = function(slot, param, link_id) {
+ LGraphNode.prototype.triggerSlot = function(slot, param, link_id, options) {
+ options = options || {};
if (!this.outputs) {
return;
}
@@ -3010,12 +3236,20 @@
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) {
- node.onExecute(param);
+ // -- wrapping node.onExecute(param); --
+ node.doExecute(param, options);
}
}
else if (node.onAction) {
- node.onAction(target_connection.name, param);
+ // 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
+ var target_connection = node.inputs[link_info.target_slot];
+ // wrap node.onAction(target_connection.name, param);
+ node.actionDo(target_connection.name, param, options);
}
}
};
@@ -3124,6 +3358,9 @@
if (this.onOutputAdded) {
this.onOutputAdded(o);
}
+
+ if (LiteGraph.auto_load_slot_types) LiteGraph.registerNodeAndSlotType(this,type,true);
+
this.setSize( this.computeSize() );
this.setDirtyCanvas(true, true);
return o;
@@ -3151,6 +3388,9 @@
if (this.onOutputAdded) {
this.onOutputAdded(o);
}
+
+ if (LiteGraph.auto_load_slot_types) LiteGraph.registerNodeAndSlotType(this,info[1],true);
+
}
this.setSize( this.computeSize() );
@@ -3211,7 +3451,9 @@
if (this.onInputAdded) {
this.onInputAdded(o);
- }
+ }
+
+ LiteGraph.registerNodeAndSlotType(this,type);
this.setDirtyCanvas(true, true);
return o;
@@ -3239,6 +3481,8 @@
if (this.onInputAdded) {
this.onInputAdded(o);
}
+
+ LiteGraph.registerNodeAndSlotType(this,info[1]);
}
this.setSize( this.computeSize() );
@@ -3309,7 +3553,6 @@
rows = Math.max(rows, 1);
var font_size = LiteGraph.NODE_TEXT_SIZE; //although it should be graphcanvas.inner_text_font size
- var font_size = font_size;
var title_width = compute_text_size(this.title);
var input_width = 0;
var output_width = 0;
@@ -3610,15 +3853,16 @@
* returns the input slot with a given name (used for dynamic slots), -1 if not found
* @method findInputSlot
* @param {string} name the name of the slot
- * @return {number} the slot (-1 if not found)
+ * @param {boolean} returnObj if the obj itself wanted
+ * @return {number_or_object} the slot (-1 if not found)
*/
- LGraphNode.prototype.findInputSlot = function(name) {
+ LGraphNode.prototype.findInputSlot = function(name, returnObj) {
if (!this.inputs) {
return -1;
}
for (var i = 0, l = this.inputs.length; i < l; ++i) {
if (name == this.inputs[i].name) {
- return i;
+ return !returnObj ? i : this.inputs[i];
}
}
return -1;
@@ -3628,20 +3872,259 @@
* returns the output slot with a given name (used for dynamic slots), -1 if not found
* @method findOutputSlot
* @param {string} name the name of the slot
- * @return {number} the slot (-1 if not found)
+ * @param {boolean} returnObj if the obj itself wanted
+ * @return {number_or_object} the slot (-1 if not found)
*/
- LGraphNode.prototype.findOutputSlot = function(name) {
+ LGraphNode.prototype.findOutputSlot = function(name, returnObj) {
+ returnObj = returnObj || false;
if (!this.outputs) {
return -1;
}
for (var i = 0, l = this.outputs.length; i < l; ++i) {
if (name == this.outputs[i].name) {
- return i;
+ return !returnObj ? i : this.outputs[i];
+ }
+ }
+ return -1;
+ };
+
+ // TODO refactor: USE SINGLE findInput/findOutput functions! :: merge options
+
+ /**
+ * returns the first free input slot
+ * @method findInputSlotFree
+ * @param {object} options
+ * @return {number_or_object} the slot (-1 if not found)
+ */
+ LGraphNode.prototype.findInputSlotFree = function(optsIn) {
+ var optsIn = optsIn || {};
+ var optsDef = {returnObj: false
+ ,typesNotAccepted: []
+ };
+ var opts = Object.assign(optsDef,optsIn);
+ if (!this.inputs) {
+ return -1;
+ }
+ for (var i = 0, l = this.inputs.length; i < l; ++i) {
+ if (this.inputs[i].link && this.inputs[i].link != null) {
+ continue;
+ }
+ if (opts.typesNotAccepted && opts.typesNotAccepted.includes && opts.typesNotAccepted.includes(this.inputs[i].type)){
+ continue;
+ }
+ return !opts.returnObj ? i : this.inputs[i];
+ }
+ return -1;
+ };
+
+ /**
+ * returns the first output slot free
+ * @method findOutputSlotFree
+ * @param {object} options
+ * @return {number_or_object} the slot (-1 if not found)
+ */
+ LGraphNode.prototype.findOutputSlotFree = function(optsIn) {
+ var optsIn = optsIn || {};
+ var optsDef = { returnObj: false
+ ,typesNotAccepted: []
+ };
+ var opts = Object.assign(optsDef,optsIn);
+ if (!this.outputs) {
+ return -1;
+ }
+ for (var i = 0, l = this.outputs.length; i < l; ++i) {
+ if (this.outputs[i].links && this.outputs[i].links != null) {
+ continue;
+ }
+ 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
+ */
+ LGraphNode.prototype.findInputSlotByType = function(type, returnObj, preferFreeSlot, doNotUseOccupied) {
+ return this.findSlotByType(true, type, returnObj, preferFreeSlot, doNotUseOccupied);
+ };
+
+ /**
+ * findSlotByType for OUTPUTS
+ */
+ 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
+ * @param {boolean} input uise inputs instead of outputs
+ * @param {string} type the type of the slot
+ * @param {boolean} returnObj if the obj itself wanted
+ * @param {boolean} preferFreeSlot if we want a free slot (if not found, will return the first of the type anyway)
+ * @return {number_or_object} the slot (-1 if not found)
+ */
+ LGraphNode.prototype.findSlotByType = function(input, type, returnObj, preferFreeSlot, doNotUseOccupied) {
+ input = input || false;
+ returnObj = returnObj || false;
+ preferFreeSlot = preferFreeSlot || false;
+ doNotUseOccupied = doNotUseOccupied || false;
+ var aSlots = input ? this.inputs : this.outputs;
+ if (!aSlots) {
+ return -1;
+ }
+ // !! 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(sI=0;sI= 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){
+ // WILL CREATE THE onTrigger IN SLOT
+ //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){
+ 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){
+ 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 == "")){
+ 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("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
+ * @param {number_or_string} slot (could be the number of the slot or the string with the name of the slot)
+ * @param {LGraphNode} node the target node
+ * @param {string} target_type the output slot type of the target node
+ * @return {Object} the link_info is created, otherwise null
+ */
+ LGraphNode.prototype.connectByTypeOutput = function(slot, source_node, source_slotType, optsIn) {
+ var optsIn = optsIn || {};
+ 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);
+ }
+ 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)
+ 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){
+ var source_slot = source_node.findOutputSlotByType(0, false, true, true);
+ if (source_slot >= 0){
+ return source_node.connect(source_slot, this, slot);
+ }
+ }
+
+ 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);
+ }
+ }
+ // 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 == "")){
+ var source_slot = source_node.findOutputSlotFree({typesNotAccepted: [LiteGraph.EVENT] });
+ 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?")
+ return null;
+ }
+ }
+
/**
* connect this node output to the input of another node
* @method connect
@@ -3701,14 +4184,16 @@
return null;
}
} else if (target_slot === LiteGraph.EVENT) {
- //search for first slot with event?
- /*
- //create input for trigger
- var input = target_node.addInput("onTrigger", LiteGraph.EVENT );
- target_slot = target_node.inputs.length - 1; //last one is the one created
- target_node.mode = LiteGraph.ON_TRIGGER;
- */
- return null;
+
+ 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
@@ -3721,45 +4206,69 @@
var changed = false;
- //if there is something already plugged there, disconnect
- if (target_node.inputs[target_slot].link != null) {
- this.graph.beforeChange();
- target_node.disconnectInput(target_slot);
- changed = true;
- }
-
- //why here??
- //this.setDirtyCanvas(false,true);
- //this.graph.connectionChange( this );
-
- var output = this.outputs[slot];
-
- //allows nodes to block connection
- if (target_node.onConnectInput) {
- if ( target_node.onConnectInput(target_slot, output.type, output, this, slot) === false ) {
- return null;
- }
- }
-
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);
+ console.debug(this.outputs);*/
+ return null;
+ }
- //this slots cannot be connected (different types)
- if (!LiteGraph.isValidConnection(output.type, input.type))
+ // 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
+ }
+
+ //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);
}
- if(!changed)
- this.graph.beforeChange();
+ //allows nodes to block connection, callback
+ if (target_node.onConnectInput) {
+ if ( target_node.onConnectInput(target_slot, output.type, output, this, slot) === false ) {
+ return null;
+ }
+ }
+ if (this.onConnectOutput) { // callback
+ if ( this.onConnectOutput(slot, input.type, input, target_node, target_slot) === false ) {
+ return null;
+ }
+ }
+ //if there is something already plugged there, disconnect
+ if (target_node.inputs[target_slot] && target_node.inputs[target_slot].link != null) {
+ this.graph.beforeChange();
+ target_node.disconnectInput(target_slot, {doProcessChange: false});
+ changed = true;
+ }
+ if (output.links !== null && output.links.length){
+ switch(output.type){
+ case LiteGraph.EVENT:
+ if (!LiteGraph.allow_multi_output_for_events){
+ this.graph.beforeChange();
+ this.disconnectOutput(slot, false, {doProcessChange: false}); // Input(target_slot, {doProcessChange: false});
+ changed = true;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
//create link class
link_info = new LLink(
++this.graph.last_link_id,
- input.type,
+ input.type || output.type,
this.id,
slot,
target_node.id,
@@ -4671,9 +5180,19 @@ LGraphNode.prototype.executeAction = function(action)
this.default_link_color = LiteGraph.LINK_COLOR;
this.default_connection_color = {
input_off: "#778",
- input_on: "#7F7",
+ input_on: "#7F7", //"#BBD"
output_off: "#778",
- output_on: "#7F7"
+ output_on: "#7F7" //"#BBD"
+ };
+ this.default_connection_color_byType = {
+ /*number: "#7F7",
+ string: "#77F",
+ boolean: "#F77",*/
+ }
+ this.default_connection_color_byTypeOff = {
+ /*number: "#474",
+ string: "#447",
+ boolean: "#744",*/
};
this.highquality_render = true;
@@ -5032,7 +5551,7 @@ LGraphNode.prototype.executeAction = function(action)
this._mousemove_callback = this.processMouseMove.bind(this);
this._mouseup_callback = this.processMouseUp.bind(this);
- //touch events -- THIS WAY DOES NOT WORK, finish implementing pointerevents, than clean the touchevents
+ //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
@@ -5271,8 +5790,9 @@ LGraphNode.prototype.executeAction = function(action)
LiteGraph.pointerListenerAdd(ref_window.document,"up", this._mouseup_callback,true);
}
- if(!is_inside)
+ if(!is_inside){
return;
+ }
var node = this.graph.getNodeOnPos( e.canvasX, e.canvasY, this.visible_nodes, 5 );
var skip_dragging = false;
@@ -5317,6 +5837,27 @@ LGraphNode.prototype.executeAction = function(action)
skip_action = true;
}
+ // 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()){
+ cloned.pos[0] += 5;
+ cloned.pos[1] += 5;
+ this.graph.add(cloned,false,{doCalcSize: false});
+ node = cloned;
+ skip_action = true;
+ if (!block_drag_node) {
+ if (this.allow_dragnodes) {
+ this.graph.beforeChange();
+ this.node_dragged = node;
+ }
+ if (!this.selected_nodes[node.id]) {
+ this.processNodeSelected(node, e);
+ }
+ }
+ }
+ }
+
var clicking_canvas_bg = false;
//when clicked on top of a node
@@ -5327,7 +5868,11 @@ LGraphNode.prototype.executeAction = function(action)
} //if it wasn't selected?
//not dragging mouse to connect two slots
- if ( !this.connecting_node && !node.flags.collapsed && !this.live_mode ) {
+ if (
+ !this.connecting_node &&
+ !node.flags.collapsed &&
+ !this.live_mode
+ ) {
//Search for corner for resize
if (
!skip_action &&
@@ -5341,7 +5886,7 @@ LGraphNode.prototype.executeAction = function(action)
10
)
) {
- this.graph.beforeChange();
+ this.graph.beforeChange();
this.resizing_node = node;
this.canvas.style.cursor = "se-resize";
skip_action = true;
@@ -5363,11 +5908,14 @@ LGraphNode.prototype.executeAction = function(action)
) {
this.connecting_node = node;
this.connecting_output = output;
+ this.connecting_output.slot_index = i;
this.connecting_pos = node.getConnectionPos( false, i );
this.connecting_slot = i;
- if (e.shiftKey) {
- node.disconnectOutput(i);
+ if (LiteGraph.shift_click_do_break_link_from){
+ if (e.shiftKey) {
+ node.disconnectOutput(i);
+ }
}
if (is_double_click) {
@@ -5415,12 +5963,22 @@ LGraphNode.prototype.executeAction = function(action)
var link_info = this.graph.links[
input.link
]; //before disconnecting
- node.disconnectInput(i);
+ if (LiteGraph.click_do_break_link_to){
+ node.disconnectInput(i);
+ this.dirty_bgcanvas = true;
+ skip_action = true;
+ }else{
+ // do same action as has not node ?
+ }
if (
this.allow_reconnect_links ||
+ //this.move_destination_link_without_shift ||
e.shiftKey
) {
+ if (!LiteGraph.click_do_break_link_to){
+ node.disconnectInput(i);
+ }
this.connecting_node = this.graph._nodes_by_id[
link_info.origin_id
];
@@ -5430,8 +5988,24 @@ 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){
+ // 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;
}
@@ -5498,46 +6072,51 @@ LGraphNode.prototype.executeAction = function(action)
}
} //clicked outside of nodes
else {
- //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;
+ 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;
}
- //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;
- }
+ 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();
- }
- }
+ 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);
- }
+ if (is_double_click && !this.read_only && this.allow_searchbox) {
+ this.showSearchBox(e);
+ e.preventDefault();
+ e.stopPropagation();
+ }
- clicking_canvas_bg = true;
+ clicking_canvas_bg = true;
+ }
}
if (!skip_action && clicking_canvas_bg && this.allow_dragcanvas) {
@@ -5547,10 +6126,91 @@ LGraphNode.prototype.executeAction = function(action)
} 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 &&
+ !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 (e.which == 3 || this.pointer_is_double) {
+
//right button
- if(!this.read_only)
- this.processContextMenu(node, e);
+ 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
@@ -5713,23 +6373,47 @@ LGraphNode.prototype.executeAction = function(action)
//if dragging a link
if (this.connecting_node) {
- var pos = this._highlight_input || [0, 0]; //to store the output of isOverNodeInput
+
+ 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
- } else {
- //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];
- }
+ //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
} else {
- this._highlight_input = null;
- this._highlight_input_slot = null;
+ //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
+ }
+ } else {
+ this._highlight_input = null;
+ 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
+ if (this.isOverNodeBox(node, e.canvasX, e.canvasY)) {
+ //mouse on top of the corner box, don't know what to do
+ } else {
+ //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;
+ if ( LiteGraph.isValidConnection( this.connecting_input.type, slot_type ) ) {
+ this._highlight_output = pos;
+ }
+ } else {
+ this._highlight_output = null;
+ }
}
}
}
@@ -5897,11 +6581,17 @@ LGraphNode.prototype.executeAction = function(action)
}
this.selected_group_resizing = false;
+ 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);
- this.deselectAllNodes();
+
//compute bounding and flip if left to right
var w = Math.abs(this.dragging_rectangle[2]);
var h = Math.abs(this.dragging_rectangle[3]);
@@ -5918,24 +6608,31 @@ LGraphNode.prototype.executeAction = function(action)
this.dragging_rectangle[2] = w;
this.dragging_rectangle[3] = h;
- //test against all nodes (not visible because the rectangle maybe start outside
- var to_select = [];
- for (var i = 0; i < nodes.length; ++i) {
- var node = nodes[i];
- node.getBounding(node_bounding);
- if (
- !overlapBounding(
- this.dragging_rectangle,
- node_bounding
- )
- ) {
- continue;
- } //out of the visible area
- to_select.push(node);
- }
- if (to_select.length) {
- this.selectNodes(to_select);
- }
+ // 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) {
@@ -5943,66 +6640,86 @@ LGraphNode.prototype.executeAction = function(action)
this.dirty_canvas = true;
this.dirty_bgcanvas = true;
- var node = this.graph.getNodeOnPos(
- e.canvasX,
- e.canvasY,
- this.visible_nodes
- );
-
+ var connInOrOut = this.connecting_output || this.connecting_input;
+ var connType = connInOrOut.type;
+
//node below mouse
if (node) {
+
+ /* no need to condition on event type.. just another type
if (
- this.connecting_output.type == LiteGraph.EVENT &&
+ connType == LiteGraph.EVENT &&
this.isOverNodeBox(node, e.canvasX, e.canvasY)
) {
+
this.connecting_node.connect(
this.connecting_slot,
node,
LiteGraph.EVENT
);
- } else {
+
+ } else {*/
+
//slot below mouse? connect
- var slot = this.isOverNodeInput(
- node,
- e.canvasX,
- e.canvasY
- );
- if (slot != -1) {
- this.connecting_node.connect(
- this.connecting_slot,
+
+ if (this.connecting_output){
+
+ var slot = this.isOverNodeInput(
node,
- slot
+ e.canvasX,
+ e.canvasY
);
- } else {
- //not on top of an input
- var input = node.getInputInfo(0);
- //auto connect
- if (
- this.connecting_output.type == LiteGraph.EVENT
- ) {
- this.connecting_node.connect(
- this.connecting_slot,
- node,
- LiteGraph.EVENT
- );
- } else if (
- input &&
- !input.link &&
- LiteGraph.isValidConnection(
- input.type && this.connecting_output.type
- )
- ) {
- this.connecting_node.connect(
- this.connecting_slot,
- node,
- 0
- );
+ 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);
+ }
+
}
- }
+
+
+ //}
+
+ }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;
@@ -6151,7 +6868,7 @@ LGraphNode.prototype.executeAction = function(action)
};
/**
- * returns true if a position (in graph space) is on top of a node input slot
+ * returns the INDEX if a position (in graph space) is on top of a node input slot
* @method isOverNodeInput
**/
LGraphCanvas.prototype.isOverNodeInput = function(
@@ -6195,6 +6912,52 @@ 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
+ **/
+ LGraphCanvas.prototype.isOverNodeOutput = function(
+ node,
+ canvasx,
+ canvasy,
+ slot_pos
+ ) {
+ 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);
+ var is_inside = false;
+ if (node.horizontal) {
+ is_inside = isInsideRectangle(
+ canvasx,
+ canvasy,
+ link_pos[0] - 5,
+ link_pos[1] - 10,
+ 10,
+ 20
+ );
+ } else {
+ is_inside = isInsideRectangle(
+ canvasx,
+ canvasy,
+ link_pos[0] - 10,
+ link_pos[1] - 5,
+ 40,
+ 10
+ );
+ }
+ if (is_inside) {
+ if (slot_pos) {
+ slot_pos[0] = link_pos[0];
+ slot_pos[1] = link_pos[1];
+ }
+ return i;
+ }
+ }
+ }
+ return -1;
+ };
/**
* process a key event
@@ -6214,10 +6977,17 @@ LGraphNode.prototype.executeAction = function(action)
if (e.type == "keydown") {
if (e.keyCode == 32) {
- //esc
+ //space
this.dragging_canvas = true;
block_default = true;
}
+
+ if (e.keyCode == 27) {
+ //esc
+ if(this.node_panel) this.node_panel.close();
+ if(this.options_panel) this.options_panel.close();
+ block_default = true;
+ }
//select all Control A
if (e.keyCode == 65 && e.ctrlKey) {
@@ -6262,6 +7032,7 @@ LGraphNode.prototype.executeAction = function(action)
}
} else if (e.type == "keyup") {
if (e.keyCode == 32) {
+ // space
this.dragging_canvas = false;
}
@@ -6348,15 +7119,38 @@ LGraphNode.prototype.executeAction = function(action)
//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]){
+ posMin[0] = clipboard_info.nodes[i].pos[0];
+ posMinIndexes[0] = i;
+ }
+ if(posMin[1]>clipboard_info.nodes[i].pos[1]){
+ posMin[1] = clipboard_info.nodes[i].pos[1];
+ posMinIndexes[1] = i;
+ }
+ }
+ else{
+ posMin = [clipboard_info.nodes[i].pos[0], clipboard_info.nodes[i].pos[1]];
+ posMinIndexes = [i, i];
+ }
+ }
var nodes = [];
for (var i = 0; i < clipboard_info.nodes.length; ++i) {
var node_data = clipboard_info.nodes[i];
var node = LiteGraph.createNode(node_data.type);
if (node) {
node.configure(node_data);
- node.pos[0] += 5;
- node.pos[1] += 5;
- this.graph.add(node);
+
+ //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);
}
}
@@ -6387,8 +7181,10 @@ LGraphNode.prototype.executeAction = function(action)
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)
+ if(!is_inside){
return;
+ // --- BREAK ---
+ }
var pos = [e.canvasX, e.canvasY];
@@ -6491,7 +7287,7 @@ LGraphNode.prototype.executeAction = function(action)
};
LGraphCanvas.prototype.processNodeSelected = function(node, e) {
- this.selectNode(node, e && e.shiftKey);
+ this.selectNode(node, e && (e.shiftKey||e.ctrlKey));
if (this.onNodeSelected) {
this.onNodeSelected(node);
}
@@ -6518,12 +7314,13 @@ LGraphNode.prototype.executeAction = function(action)
**/
LGraphCanvas.prototype.selectNodes = function( nodes, add_to_current_selection )
{
- if (!add_to_current_selection) {
+ if (!add_to_current_selection) {
this.deselectAllNodes();
}
nodes = nodes || this.graph._nodes;
- for (var i = 0; i < nodes.length; ++i) {
+ if (typeof nodes == "string") nodes = [nodes];
+ for (var i in nodes) {
var node = nodes[i];
if (node.is_selected) {
continue;
@@ -6658,7 +7455,7 @@ LGraphNode.prototype.executeAction = function(action)
this.setDirty(true);
this.graph.afterChange();
};
-
+
/**
* centers the camera on a given node
* @method centerOnNode
@@ -6953,8 +7750,14 @@ LGraphNode.prototype.executeAction = function(action)
if (this.connecting_pos != null) {
ctx.lineWidth = this.connections_width;
var link_color = null;
+
+ var connInOrOut = this.connecting_output || this.connecting_input;
- switch (this.connecting_output.type) {
+ var connType = connInOrOut.type;
+ var connDir = connInOrOut.dir;
+ var connShape = connInOrOut.shape;
+
+ switch (connType) {
case LiteGraph.EVENT:
link_color = LiteGraph.EVENT_LINK_COLOR;
break;
@@ -6971,7 +7774,7 @@ LGraphNode.prototype.executeAction = function(action)
false,
null,
link_color,
- this.connecting_output.dir ||
+ connDir ||
(this.connecting_node.horizontal
? LiteGraph.DOWN
: LiteGraph.RIGHT),
@@ -6980,8 +7783,8 @@ LGraphNode.prototype.executeAction = function(action)
ctx.beginPath();
if (
- this.connecting_output.type === LiteGraph.EVENT ||
- this.connecting_output.shape === LiteGraph.BOX_SHAPE
+ connType === LiteGraph.EVENT ||
+ connShape === LiteGraph.BOX_SHAPE
) {
ctx.rect(
this.connecting_pos[0] - 6 + 0.5,
@@ -6997,7 +7800,7 @@ LGraphNode.prototype.executeAction = function(action)
14,
10
);
- } else if (this.connecting_output.shape === LiteGraph.ARROW_SHAPE) {
+ } 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);
@@ -7043,6 +7846,24 @@ LGraphNode.prototype.executeAction = function(action)
}
ctx.fill();
}
+ if (this._highlight_output) {
+ ctx.beginPath();
+ if (shape === LiteGraph.ARROW_SHAPE) {
+ ctx.moveTo(this._highlight_output[0] + 8, this._highlight_output[1] + 0.5);
+ ctx.lineTo(this._highlight_output[0] - 4, this._highlight_output[1] + 6 + 0.5);
+ ctx.lineTo(this._highlight_output[0] - 4, this._highlight_output[1] - 6 + 0.5);
+ ctx.closePath();
+ } else {
+ ctx.arc(
+ this._highlight_output[0],
+ this._highlight_output[1],
+ 6,
+ 0,
+ Math.PI * 2
+ );
+ }
+ ctx.fill();
+ }
}
//the selection rectangle
@@ -7081,7 +7902,7 @@ LGraphNode.prototype.executeAction = function(action)
this.onDrawOverlay(ctx);
}
- if (area) {
+ if (area){
ctx.restore();
}
@@ -7251,11 +8072,11 @@ LGraphNode.prototype.executeAction = function(action)
bgcolor = bgcolor || LiteGraph.NODE_DEFAULT_COLOR;
hovercolor = hovercolor || "#555";
textcolor = textcolor || LiteGraph.NODE_TEXT_COLOR;
-
+ var yFix = y + LiteGraph.NODE_TITLE_HEIGHT + 2; // fix the height with the title
var pos = this.mouse;
- var hover = LiteGraph.isInsideRectangle( pos[0], pos[1], x,y,w,h );
+ var hover = LiteGraph.isInsideRectangle( pos[0], pos[1], x,yFix,w,h );
pos = this.last_click_position;
- var clicked = pos && LiteGraph.isInsideRectangle( pos[0], pos[1], x,y,w,h );
+ var clicked = pos && LiteGraph.isInsideRectangle( pos[0], pos[1], x,yFix,w,h );
ctx.fillStyle = hover ? hovercolor : bgcolor;
if(clicked)
@@ -7404,7 +8225,7 @@ LGraphNode.prototype.executeAction = function(action)
} else {
ctx.globalAlpha = this.editor_alpha;
}
- ctx.imageSmoothingEnabled = ctx.mozImageSmoothingEnabled = ctx.imageSmoothingEnabled = false;
+ ctx.imageSmoothingEnabled = ctx.imageSmoothingEnabled = false; // ctx.mozImageSmoothingEnabled =
if (
!this._bg_img ||
this._bg_img.name != this.background_image
@@ -7438,7 +8259,7 @@ LGraphNode.prototype.executeAction = function(action)
}
ctx.globalAlpha = 1.0;
- ctx.imageSmoothingEnabled = ctx.mozImageSmoothingEnabled = ctx.imageSmoothingEnabled = true;
+ ctx.imageSmoothingEnabled = ctx.imageSmoothingEnabled = true; //= ctx.mozImageSmoothingEnabled
}
//groups
@@ -7614,6 +8435,7 @@ LGraphNode.prototype.executeAction = function(action)
var render_text = !low_quality;
var out_slot = this.connecting_output;
+ var in_slot = this.connecting_input;
ctx.lineWidth = 1;
var max_y = 0;
@@ -7625,18 +8447,24 @@ LGraphNode.prototype.executeAction = function(action)
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
- if ( this.connecting_node && !LiteGraph.isValidConnection( slot.type , out_slot.type) ) {
+ if ( this.connecting_output && !LiteGraph.isValidConnection( slot.type , out_slot.type) ) {
ctx.globalAlpha = 0.4 * editor_alpha;
}
ctx.fillStyle =
slot.link != null
? slot.color_on ||
+ this.default_connection_color_byType[slot_type] ||
this.default_connection_color.input_on
: slot.color_off ||
+ this.default_connection_color_byTypeOff[slot_type] ||
+ this.default_connection_color_byType[slot_type] ||
this.default_connection_color.input_off;
var pos = node.getConnectionPos(true, i, slot_pos);
@@ -7648,6 +8476,12 @@ LGraphNode.prototype.executeAction = function(action)
ctx.beginPath();
+ 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
@@ -7667,11 +8501,22 @@ LGraphNode.prototype.executeAction = function(action)
10
);
}
- } else if (slot.shape === LiteGraph.ARROW_SHAPE) {
+ } else if (slot_shape === LiteGraph.ARROW_SHAPE) {
ctx.moveTo(pos[0] + 8, pos[1] + 0.5);
ctx.lineTo(pos[0] - 4, pos[1] + 6 + 0.5);
ctx.lineTo(pos[0] - 4, pos[1] - 6 + 0.5);
ctx.closePath();
+ } 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);
+ ctx.rect(pos[0] - 4, pos[1] - 1, 2, 2);
+ ctx.rect(pos[0] - 1, pos[1] - 1, 2, 2);
+ ctx.rect(pos[0] + 2, pos[1] - 1, 2, 2);
+ ctx.rect(pos[0] - 4, pos[1] + 2, 2, 2);
+ ctx.rect(pos[0] - 1, pos[1] + 2, 2, 2);
+ 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
@@ -7696,16 +8541,21 @@ LGraphNode.prototype.executeAction = function(action)
}
//output connection slots
- if (this.connecting_node) {
- ctx.globalAlpha = 0.4 * editor_alpha;
- }
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
+ 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];
@@ -7716,15 +8566,24 @@ LGraphNode.prototype.executeAction = function(action)
ctx.fillStyle =
slot.links && slot.links.length
? slot.color_on ||
+ this.default_connection_color_byType[slot_type] ||
this.default_connection_color.output_on
: slot.color_off ||
+ this.default_connection_color_byTypeOff[slot_type] ||
+ 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);
+ if (slot_type == "array"){
+ slot_shape = LiteGraph.GRID_SHAPE;
+ }
+
+ var doStroke = true;
+
if (
- slot.type === LiteGraph.EVENT ||
- slot.shape === LiteGraph.BOX_SHAPE
+ slot_type === LiteGraph.EVENT ||
+ slot_shape === LiteGraph.BOX_SHAPE
) {
if (horizontal) {
ctx.rect(
@@ -7741,11 +8600,22 @@ LGraphNode.prototype.executeAction = function(action)
10
);
}
- } else if (slot.shape === LiteGraph.ARROW_SHAPE) {
+ } else if (slot_shape === LiteGraph.ARROW_SHAPE) {
ctx.moveTo(pos[0] + 8, pos[1] + 0.5);
ctx.lineTo(pos[0] - 4, pos[1] + 6 + 0.5);
ctx.lineTo(pos[0] - 4, pos[1] - 6 + 0.5);
ctx.closePath();
+ } 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);
+ ctx.rect(pos[0] - 4, pos[1] - 1, 2, 2);
+ ctx.rect(pos[0] - 1, pos[1] - 1, 2, 2);
+ ctx.rect(pos[0] + 2, pos[1] - 1, 2, 2);
+ ctx.rect(pos[0] - 4, pos[1] + 2, 2, 2);
+ ctx.rect(pos[0] - 1, pos[1] + 2, 2, 2);
+ 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 );
@@ -7759,7 +8629,7 @@ LGraphNode.prototype.executeAction = function(action)
//if(slot.links != null && slot.links.length)
ctx.fill();
- if(!low_quality)
+ if(!low_quality && doStroke)
ctx.stroke();
//render output name
@@ -8042,7 +8912,7 @@ LGraphNode.prototype.executeAction = function(action)
var grad = LGraphCanvas.gradients[title_color];
if (!grad) {
grad = LGraphCanvas.gradients[ title_color ] = ctx.createLinearGradient(0, 0, 400, 0);
- grad.addColorStop(0, title_color);
+ grad.addColorStop(0, title_color); // TODO refactor: validate color !! prevent DOMException
grad.addColorStop(1, "#000");
}
ctx.fillStyle = grad;
@@ -8067,6 +8937,16 @@ LGraphNode.prototype.executeAction = function(action)
ctx.shadowColor = "transparent";
}
+ var colState = false;
+ 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){
+ colState = node.action_triggered ? "#FFF" : (node.execute_triggered ? "#AAA" : colState);
+ }
+
//title box
var box_size = 10;
if (node.onDrawTitleBox) {
@@ -8088,8 +8968,8 @@ LGraphNode.prototype.executeAction = function(action)
);
ctx.fill();
}
-
- ctx.fillStyle = node.boxcolor || LiteGraph.NODE_DEFAULT_BOXCOLOR;
+
+ 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
@@ -8114,7 +8994,7 @@ LGraphNode.prototype.executeAction = function(action)
box_size + 2
);
}
- ctx.fillStyle = node.boxcolor || LiteGraph.NODE_DEFAULT_BOXCOLOR;
+ ctx.fillStyle = node.boxcolor || colState || LiteGraph.NODE_DEFAULT_BOXCOLOR;
ctx.fillRect(
(title_height - box_size) * 0.5,
(title_height + box_size) * -0.5,
@@ -8247,6 +9127,10 @@ 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--;
};
var margin_area = new Float32Array(4);
@@ -9290,8 +10174,6 @@ LGraphNode.prototype.executeAction = function(action)
};
/* this is an implementation for touch not in production and not ready
- * the idea is maybe good: simulate a similar event
- * so let's try with pointerevents (working with both mouse and touch), and simulate the old mouseevents IF NECESSARY for retrocompatibility: existing old good nodes
*/
/*LGraphCanvas.prototype.touchHandler = function(event) {
//alert("foo");
@@ -9317,7 +10199,13 @@ LGraphNode.prototype.executeAction = function(action)
// screenX, screenY, clientX, clientY, ctrlKey,
// altKey, shiftKey, metaKey, button, relatedTarget);
- var window = this.getCanvasWindow();
+ // this is eventually a Dom object, get the LGraphCanvas back
+ if(typeof this.getCanvasWindow == "undefined"){
+ var window = this.lgraphcanvas.getCanvasWindow();
+ }else{
+ var window = this.getCanvasWindow();
+ }
+
var document = window.document;
var simulatedEvent = document.createEvent("MouseEvent");
@@ -9472,8 +10360,9 @@ LGraphNode.prototype.executeAction = function(action)
}
}
- if (this.onMenuNodeInputs) {
- entries = this.onMenuNodeInputs(entries);
+ if (node.onMenuNodeInputs) {
+ var retEntries = node.onMenuNodeInputs(entries);
+ if(retEntries) entries = retEntries;
}
if (!entries.length) {
@@ -9504,6 +10393,10 @@ LGraphNode.prototype.executeAction = function(action)
if (v.value) {
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();
}
@@ -9567,6 +10460,16 @@ 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: {}
+ }
+ }
+ // add callback for modifing the menu elements onMenuNodeOutputs
+ if (node.onMenuNodeOutputs) {
+ var retEntries = node.onMenuNodeOutputs(entries);
+ if(retEntries) entries = retEntries;
+ }
if (!entries.length) {
return;
@@ -9617,6 +10520,10 @@ LGraphNode.prototype.executeAction = function(action)
} else {
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();
}
@@ -9697,20 +10604,42 @@ LGraphNode.prototype.executeAction = function(action)
return e.innerHTML;
};
- LGraphCanvas.onResizeNode = function(value, options, e, menu, node) {
+ LGraphCanvas.onMenuResizeNode = function(value, options, e, menu, node) {
if (!node) {
return;
}
- node.size = node.computeSize();
- if (node.onResize)
- node.onResize(node.size);
+
+ 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 options = ["Add Node",null,"Delete"];
+ // 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];
+
+
var menu = new LiteGraph.ContextMenu(options, {
event: e,
title: link.data != null ? link.data.constructor.name : null,
@@ -9721,38 +10650,314 @@ LGraphNode.prototype.executeAction = function(action)
switch (v) {
case "Add Node":
LGraphCanvas.onMenuAdd(null, null, e, menu, function(node){
- console.log("node autoconnect");
- var node_left = that.graph.getNodeById( link.origin_id );
- var node_right = that.graph.getNodeById( link.target_id );
- if(!node.inputs || !node.inputs.length || !node.outputs || !node.outputs.length)
+ // console.debug("node autoconnect");
+ if(!node.inputs || !node.inputs.length || !node.outputs || !node.outputs.length){
return;
- if( node_left.outputs[ link.origin_slot ].type == node.inputs[0].type && node.outputs[0].type == node_right.inputs[0].type )
- {
- node_left.connect( link.origin_slot, node, 0 );
- node.connect( 0, node_right, link.target_slot );
- node.pos[0] -= node.size[0] * 0.5;
}
+ // 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;
+
case "Delete":
that.graph.removeLink(link.id);
break;
default:
+ /*var nodeCreated = createDefaultNodeForSlot({ nodeFrom: node_left
+ ,slotFrom: link.origin_slot
+ ,nodeTo: node
+ ,slotTo: link.target_slot
+ ,e: e
+ ,nodeType: "AUTO"
+ });
+ if(nodeCreated) console.log("new node in beetween "+v+" created");*/
}
}
return false;
};
+
+ 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 that = this;
+
+ var isFrom = opts.nodeFrom && opts.slotFrom!==null;
+ var isTo = !isFrom && opts.nodeTo && opts.slotTo!==null;
+
+ if (!isFrom && !isTo){
+ console.warn("No data passed to createDefaultNodeForSlot "+opts.nodeFrom+" "+opts.slotFrom+" "+opts.nodeTo+" "+opts.slotTo);
+ return false;
+ }
+ 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){
+ case "string":
+ iSlotConn = isFrom ? nodeX.findOutputSlot(slotX,false) : nodeX.findInputSlot(slotX,false);
+ slotX = isFrom ? nodeX.outputs[slotX] : nodeX.inputs[slotX];
+ break;
+ case "object":
+ // ok slotX
+ iSlotConn = isFrom ? nodeX.findOutputSlot(slotX.name) : nodeX.findInputSlot(slotX.name);
+ break;
+ case "number":
+ iSlotConn = slotX;
+ slotX = isFrom ? nodeX.outputs[slotX] : nodeX.inputs[slotX];
+ break;
+ case "undefined":
+ default:
+ // bad ?
+ //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;
+ }
+
+ 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 that = this;
+
+ var isFrom = opts.nodeFrom && opts.slotFrom;
+ var isTo = !isFrom && opts.nodeTo && opts.slotTo;
+
+ 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){
+ case "string":
+ iSlotConn = isFrom ? nodeX.findOutputSlot(slotX,false) : nodeX.findInputSlot(slotX,false);
+ slotX = isFrom ? nodeX.outputs[slotX] : nodeX.inputs[slotX];
+ break;
+ case "object":
+ // ok slotX
+ iSlotConn = isFrom ? nodeX.findOutputSlot(slotX.name) : nodeX.findInputSlot(slotX.name);
+ break;
+ case "number":
+ iSlotConn = slotX;
+ slotX = isFrom ? nodeX.outputs[slotX] : nodeX.inputs[slotX];
+ break;
+ default:
+ // bad ?
+ //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 menu = new LiteGraph.ContextMenu(options, {
+ event: opts.e,
+ title: (slotX && slotX.name!="" ? (slotX.name + (fromSlotType?" | ":"")) : "")+(slotX && fromSlotType ? fromSlotType : ""),
+ callback: inner_clicked
+ });
+
+ // callback
+ function inner_clicked(v,options,e) {
+ //console.log("Process showConnectionMenu selection");
+ switch (v) {
+ case "Add Node":
+ 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;
+ 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;
+ }
+ }
+
+ return false;
+ };
+
+ // TODO refactor :: this is used fot title but not for properties!
LGraphCanvas.onShowPropertyEditor = function(item, options, e, menu, node) {
var input_html = "";
var property = item.property || "title";
var value = node[property];
+ // TODO refactor :: use createDialog ?
+
var dialog = document.createElement("div");
+ dialog.is_modified = false;
dialog.className = "graphdialog";
- dialog.innerHTML = "";
- //dialog.innerHTML = "";
+ dialog.innerHTML =
+ "";
+ dialog.close = function() {
+ if (dialog.parentNode) {
+ dialog.parentNode.removeChild(dialog);
+ }
+ };
var title = dialog.querySelector(".name");
title.innerText = property;
var input = dialog.querySelector(".value");
@@ -9762,10 +10967,15 @@ LGraphNode.prototype.executeAction = function(action)
this.focus();
});
input.addEventListener("keydown", function(e) {
- if (e.keyCode != 13 && e.target.localName != "textarea") {
+ dialog.is_modified = true;
+ if (e.keyCode == 27) {
+ //ESC
+ dialog.close();
+ } else if (e.keyCode == 13) {
+ inner(); // save
+ } else if (e.keyCode != 13 && e.target.localName != "textarea") {
return;
}
- inner();
e.preventDefault();
e.stopPropagation();
});
@@ -9794,8 +11004,21 @@ LGraphNode.prototype.executeAction = function(action)
button.addEventListener("click", inner);
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();
+ });
+ dialog.addEventListener("mouseenter", function(e) {
+ if(LiteGraph.dialog_close_on_mouse_leave)
+ if(dialogCloseTimer) clearTimeout(dialogCloseTimer);
+ });
+
function inner() {
- setValue(input.value);
+ if(input) setValue(input.value);
}
function setValue(value) {
@@ -9812,19 +11035,19 @@ LGraphNode.prototype.executeAction = function(action)
}
};
+ // refactor: there are different dialogs, some uses createDialog some dont
LGraphCanvas.prototype.prompt = function(title, value, callback, event, multiline) {
var that = this;
var input_html = "";
title = title || "";
- var modified = false;
-
var dialog = document.createElement("div");
+ dialog.is_modified = false;
dialog.className = "graphdialog rounded";
- if(multiline)
+ if(multiline)
dialog.innerHTML = " ";
else
- dialog.innerHTML = " ";
+ dialog.innerHTML = " ";
dialog.close = function() {
that.prompt_box = null;
if (dialog.parentNode) {
@@ -9832,15 +11055,42 @@ 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 + ")";
}
- LiteGraph.pointerListenerAdd(dialog,"leave", function(e) {
- if (!modified) {
- dialog.close();
- }
+ var dialogCloseTimer = null;
+ var prevent_timeout = false;
+ LiteGraph.pointerListenerAdd(dialog,"leave", function(e) {
+ if (prevent_timeout)
+ 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();
});
+ 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 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;
+ });
+ selIn.addEventListener("change", function(e) {
+ prevent_timeout = -1;
+ });
+ });
+ }
if (that.prompt_box) {
that.prompt_box.close();
@@ -9858,7 +11108,7 @@ LGraphNode.prototype.executeAction = function(action)
var input = value_element;
input.addEventListener("keydown", function(e) {
- modified = true;
+ dialog.is_modified = true;
if (e.keyCode == 27) {
//ESC
dialog.close();
@@ -9883,9 +11133,6 @@ LGraphNode.prototype.executeAction = function(action)
dialog.close();
});
- var graphcanvas = LGraphCanvas.active_canvas;
- var canvas = graphcanvas.canvas;
-
var rect = canvas.getBoundingClientRect();
var offsetx = -20;
var offsety = -20;
@@ -9902,7 +11149,6 @@ LGraphNode.prototype.executeAction = function(action)
dialog.style.top = canvas.height * 0.5 + offsety + "px";
}
- canvas.parentNode.appendChild(dialog);
setTimeout(function() {
input.focus();
}, 10);
@@ -9911,7 +11157,24 @@ LGraphNode.prototype.executeAction = function(action)
};
LGraphCanvas.search_limit = -1;
- LGraphCanvas.prototype.showSearchBox = function(event) {
+ LGraphCanvas.prototype.showSearchBox = function(event, options) {
+ // proposed defaults
+ 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);
+
var that = this;
var input_html = "";
var graphcanvas = LGraphCanvas.active_canvas;
@@ -9920,8 +11183,27 @@ LGraphNode.prototype.executeAction = function(action)
var dialog = document.createElement("div");
dialog.className = "litegraph litesearchbox graphdialog rounded";
- dialog.innerHTML =
- "Search ";
+ dialog.innerHTML = "Search ";
+ 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";
+ }
+ // dialog element has been appended
+
+ 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;
root_document.body.focus();
@@ -9935,25 +11217,50 @@ LGraphNode.prototype.executeAction = function(action)
}
};
- var timeout_close = null;
-
if (this.ds.scale > 1) {
dialog.style.transform = "scale(" + this.ds.scale + ")";
}
- LiteGraph.pointerListenerAdd(dialog,"enter", function(e) {
- if (timeout_close) {
- clearTimeout(timeout_close);
- timeout_close = null;
+ // hide on mouse leave
+ if(options.hide_on_mouse_leave){
+ var prevent_timeout = false;
+ var timeout_close = null;
+ LiteGraph.pointerListenerAdd(dialog,"enter", function(e) {
+ if (timeout_close) {
+ clearTimeout(timeout_close);
+ timeout_close = null;
+ }
+ });
+ LiteGraph.pointerListenerAdd(dialog,"leave", function(e) {
+ if (prevent_timeout){
+ return;
+ }
+ timeout_close = setTimeout(function() {
+ dialog.close();
+ }, 500);
+ });
+ // if filtering, check focus changed to comboboxes and prevent closing
+ if (options.do_type_filter){
+ selIn.addEventListener("click", function(e) {
+ prevent_timeout++;
+ });
+ selIn.addEventListener("blur", function(e) {
+ prevent_timeout = 0;
+ });
+ selIn.addEventListener("change", function(e) {
+ prevent_timeout = -1;
+ });
+ selOut.addEventListener("click", function(e) {
+ prevent_timeout++;
+ });
+ selOut.addEventListener("blur", function(e) {
+ prevent_timeout = 0;
+ });
+ selOut.addEventListener("change", function(e) {
+ prevent_timeout = -1;
+ });
}
- });
-
- LiteGraph.pointerListenerAdd(dialog,"leave", function(e) {
- //dialog.close();
- timeout_close = setTimeout(function() {
- dialog.close();
- }, 500);
- });
+ }
if (that.search_box) {
that.search_box.close();
@@ -9993,7 +11300,7 @@ LGraphNode.prototype.executeAction = function(action)
if (timeout) {
clearInterval(timeout);
}
- timeout = setTimeout(refreshHelper, 10);
+ timeout = setTimeout(refreshHelper, 250);
return;
}
e.preventDefault();
@@ -10002,15 +11309,62 @@ LGraphNode.prototype.executeAction = function(action)
return true;
});
}
-
- if( root_document.fullscreenElement )
- root_document.fullscreenElement.appendChild(dialog);
- else
- {
- root_document.body.appendChild(dialog);
- root_document.body.style.overflow = "hidden";
- }
-
+
+ // if should filter on type, load and fill selected and choose elements if passed
+ 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-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){
+ var iS = false;
+ switch (typeof options.slot_from){
+ case "string":
+ iS = options.node_to.findInputSlot(options.slot_from);
+ break;
+ case "object":
+ 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;
+ case "number":
+ iS = options.slot_from;
+ break;
+ default:
+ iS = 0; // try with first if no name set
+ }
+ 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);
+ }
+ }else{
+ // console.warn("cant find slot_nodeTO " + options.slot_from);
+ }
+ }
+
+ graphcanvas.graph.afterChange();
}
}
@@ -10130,7 +11545,7 @@ LGraphNode.prototype.executeAction = function(action)
var str = input.value;
first = null;
helper.innerHTML = "";
- if (!str) {
+ if (!str && !options.show_all_if_empty) {
return;
}
@@ -10146,15 +11561,26 @@ LGraphNode.prototype.executeAction = function(action)
str = str.toLowerCase();
var filter = graphcanvas.filter || graphcanvas.graph.filter;
+ // filter by type preprocess
+ 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
for (var i in LiteGraph.searchbox_extras) {
var extra = LiteGraph.searchbox_extras[i];
- if (extra.desc.toLowerCase().indexOf(str) === -1) {
+ 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;
+ if( ! inner_test_filter(extra.type) )
+ continue;
addResult( extra.desc, "searchbox_extra" );
if ( LGraphCanvas.search_limit !== -1 && c++ > LGraphCanvas.search_limit ) {
break;
@@ -10179,13 +11605,98 @@ LGraphNode.prototype.executeAction = function(action)
break;
}
}
-
- function inner_test_filter( type )
+
+ // add general type if filtering
+ if (options.show_general_after_typefiltered
+ && (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);
+ }
+ for (var i = 0; i < filtered_extra.length; i++) {
+ addResult(filtered_extra[i], "generic_type");
+ if ( LGraphCanvas.search_limit !== -1 && c++ > LGraphCanvas.search_limit ) {
+ break;
+ }
+ }
+ }
+
+ // check il filtering gave no results
+ 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);
+ }
+ for (var i = 0; i < filtered_extra.length; i++) {
+ addResult(filtered_extra[i], "not_in_filter");
+ if ( LGraphCanvas.search_limit !== -1 && c++ > LGraphCanvas.search_limit ) {
+ break;
+ }
+ }
+ }
+
+ function inner_test_filter( type, optsIn )
{
+ var optsIn = optsIn || {};
+ 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;
- return type.toLowerCase().indexOf(str) !== -1;
+ 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){
+ 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);
+ var doesInc = LiteGraph.registered_slot_in_types[sV].nodes.includes(sType);
+ if (doesInc!==false){
+ //console.log(sType+" HAS "+sV);
+ }else{
+ /*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);
+ var doesInc = LiteGraph.registered_slot_out_types[sV].nodes.includes(sType);
+ if (doesInc!==false){
+ //console.log(sType+" HAS "+sV);
+ }else{
+ /*console.debug(LiteGraph.registered_slot_out_types[sV]);
+ console.log(+" DONT includes "+type);*/
+ return false;
+ }
+ }
+ }
+ }
+ return true;
}
}
@@ -10242,7 +11753,7 @@ LGraphNode.prototype.executeAction = function(action)
"";
}
input_html += "";
- } else if (type == "boolean") {
+ } else if (type == "boolean" || type == "toggle") {
input_html =
"=0 && options.values[kV]){
+ console.debug("update graph options: "+options.key+": "+kV);
+ graphcanvas[options.key] = kV;
+ //console.debug(graphcanvas);
+ break;
+ }
+ }
+ console.warn("unexpected options");
+ console.debug(options);
+ break;*/
+ default:
+ //console.debug("want to update graph options: "+name+": "+value);
+ if (options && options.key){
+ name = options.key;
+ }
+ if (options.values){
+ value = Object.values(options.values).indexOf(value);
+ }
+ //console.debug("update graph option: "+name+": "+value);
+ graphcanvas[name] = value;
+ break;
+ }
+ };
+
+ // panel.addWidget( "string", "Graph name", "", {}, fUpdate); // implement
+
+ var aProps = LiteGraph.availableCanvasOptions;
+ aProps.sort();
+ for(pI in aProps){
+ var pX = aProps[pI];
+ panel.addWidget( "boolean", pX, graphcanvas[pX], {key: pX, on: "True", off: "False"}, fUpdate);
+ }
+
+ var aLinks = [ 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 = ""; // clear
+
+ }
+ inner_refresh();
+
+ graphcanvas.canvas.parentNode.appendChild( panel );
+ }
+
+ LGraphCanvas.prototype.showShowNodePanel = function( node )
+ {
+ this.SELECTED_NODE = node;
+ this.closePanels();
var ref_window = this.getCanvasWindow();
- panel = this.createPanel(node.title || "",{closable: true, window: ref_window });
+ var that = this;
+ var graphcanvas = this;
+ panel = this.createPanel(node.title || "",{
+ closable: true
+ ,window: ref_window
+ ,onOpen: function(){
+ graphcanvas.NODEPANEL_IS_OPEN = true;
+ }
+ ,onClose: function(){
+ graphcanvas.NODEPANEL_IS_OPEN = false;
+ graphcanvas.node_panel = null;
+ }
+ });
+ graphcanvas.node_panel = panel;
panel.id = "node-panel";
panel.node = node;
panel.classList.add("settings");
- var that = this;
- var graphcanvas = this;
function inner_refresh()
{
@@ -10586,22 +12301,58 @@ LGraphNode.prototype.executeAction = function(action)
panel.addHTML("Properties
");
- for(var i in node.properties)
+ var fUpdate = function(name,value){
+ graphcanvas.graph.beforeChange(node);
+ switch(name){
+ case "Title":
+ node.title = value;
+ break;
+ case "Mode":
+ var kV = Object.values(LiteGraph.NODE_MODES).indexOf(value);
+ if (kV>=0 && LiteGraph.NODE_MODES[kV]){
+ node.changeMode(kV);
+ }else{
+ console.warn("unexpected mode: "+value);
+ }
+ break;
+ case "Color":
+ if (LGraphCanvas.node_colors[value]){
+ node.color = LGraphCanvas.node_colors[value].color;
+ node.bgcolor = LGraphCanvas.node_colors[value].bgcolor;
+ }else{
+ console.warn("unexpected color: "+value);
+ }
+ break;
+ default:
+ node.setProperty(name,value);
+ break;
+ }
+ graphcanvas.graph.afterChange();
+ graphcanvas.dirty_canvas = true;
+ };
+
+ panel.addWidget( "string", "Title", node.title, {}, fUpdate);
+
+ panel.addWidget( "combo", "Mode", LiteGraph.NODE_MODES[node.mode], {values: LiteGraph.NODE_MODES}, fUpdate);
+
+ var nodeCol = "";
+ if (node.color !== undefined){
+ 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);
+
+ for(var pName in node.properties)
{
- var value = node.properties[i];
- var info = node.getPropertyInfo(i);
+ var value = node.properties[pName];
+ var info = node.getPropertyInfo(pName);
var type = info.type || "string";
//in case the user wants control over the side panel widget
- if( node.onAddPropertyToPanel && node.onAddPropertyToPanel(i,panel) )
+ if( node.onAddPropertyToPanel && node.onAddPropertyToPanel(pName,panel) )
continue;
- panel.addWidget( info.widget || info.type, i, value, info, function(name,value){
- graphcanvas.graph.beforeChange(node);
- node.setProperty(name,value);
- graphcanvas.graph.afterChange();
- graphcanvas.dirty_canvas = true;
- });
+ panel.addWidget( info.widget || info.type, pName, value, info, fUpdate);
}
panel.addSeparator();
@@ -10609,13 +12360,7 @@ LGraphNode.prototype.executeAction = function(action)
if(node.onShowCustomPanelInfo)
node.onShowCustomPanelInfo(panel);
- /*
- panel.addHTML("Connections
");
- var connection_containers = panel.addHTML("","connections");
- var inputs = connection_containers.querySelector(".inputs");
- var outputs = connection_containers.querySelector(".outputs");
- */
-
+ panel.footer.innerHTML = ""; // clear
panel.addButton("Delete",function(){
if(node.block_delete)
return;
@@ -10624,14 +12369,13 @@ LGraphNode.prototype.executeAction = function(action)
}).classList.add("delete");
}
- function inner_showCodePad( node, propname )
+ panel.inner_showCodePad = function( propname )
{
- panel.style.top = "calc( 50% - 250px)";
- panel.style.left = "calc( 50% - 400px)";
- panel.style.width = "800px";
- panel.style.height = "500px";
+ panel.classList.remove("settings");
+ panel.classList.add("centered");
- if(window.CodeFlask) //disabled for now
+
+ /*if(window.CodeFlask) //disabled for now
{
panel.content.innerHTML = "";
var flask = new CodeFlask( "div.code", { language: 'js' });
@@ -10641,30 +12385,37 @@ LGraphNode.prototype.executeAction = function(action)
});
}
else
- {
- panel.content.innerHTML = "";
- var textarea = panel.content.querySelector("textarea");
+ {*/
+ 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){
- //console.log(e);
if(e.code == "Enter" && e.ctrlKey )
{
- console.log("Assigned");
node.setProperty(propname, textarea.value);
+ fDoneWith();
}
});
+ panel.toggleAltContent(true);
+ panel.toggleFooterVisibility(false);
textarea.style.height = "calc(100% - 40px)";
- }
- var assign = that.createButton( "Assign", null, function(){
+ /*}*/
+ var assign = panel.addButton( "Assign", function(){
node.setProperty(propname, textarea.value);
+ fDoneWith();
});
- panel.content.appendChild(assign);
- var button = that.createButton( "Close", null, function(){
- panel.style.height = "";
- inner_refresh();
- });
+ panel.alt_content.appendChild(assign); //panel.content.appendChild(assign);
+ var button = panel.addButton( "Close", fDoneWith);
button.style.float = "right";
- panel.content.appendChild(button);
+ panel.alt_content.appendChild(button); // panel.content.appendChild(button);
}
inner_refresh();
@@ -10804,9 +12555,22 @@ LGraphNode.prototype.executeAction = function(action)
}
LGraphCanvas.onMenuNodeCollapse = function(value, options, e, menu, node) {
- node.graph.beforeChange(node);
- node.collapse();
- node.graph.afterChange(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(/*?*/);
};
LGraphCanvas.onMenuNodePin = function(value, options, e, menu, node) {
@@ -10815,7 +12579,7 @@ LGraphNode.prototype.executeAction = function(action)
LGraphCanvas.onMenuNodeMode = function(value, options, e, menu, node) {
new LiteGraph.ContextMenu(
- ["Always", "On Event", "On Trigger", "Never"],
+ LiteGraph.NODE_MODES,
{ event: e, callback: inner_clicked, parentMenu: menu, node: node }
);
@@ -10823,21 +12587,24 @@ LGraphNode.prototype.executeAction = function(action)
if (!node) {
return;
}
- switch (v) {
- case "On Event":
- node.mode = LiteGraph.ON_EVENT;
- break;
- case "On Trigger":
- node.mode = LiteGraph.ON_TRIGGER;
- break;
- case "Never":
- node.mode = LiteGraph.NEVER;
- break;
- case "Always":
- default:
- node.mode = LiteGraph.ALWAYS;
- break;
- }
+ 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]);
+ }
+ }
}
return false;
@@ -10883,17 +12650,29 @@ LGraphNode.prototype.executeAction = function(action)
}
var color = v.value ? LGraphCanvas.node_colors[v.value] : null;
- 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 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);
}
@@ -10916,9 +12695,22 @@ LGraphNode.prototype.executeAction = function(action)
if (!node) {
return;
}
- node.graph.beforeChange(node);
- node.shape = v;
- node.graph.afterChange(node);
+ node.graph.beforeChange(/*?*/); //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);
}
@@ -10930,13 +12722,26 @@ LGraphNode.prototype.executeAction = function(action)
throw "no node passed";
}
- if (node.removable === false) {
- return;
- }
-
var graph = node.graph;
graph.beforeChange();
- graph.remove(node);
+
+
+ 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);
};
@@ -10962,17 +12767,37 @@ LGraphNode.prototype.executeAction = function(action)
};
LGraphCanvas.onMenuNodeClone = function(value, options, e, menu, 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.beforeChange();
- node.graph.add(newnode);
+
+ 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);
@@ -10996,6 +12821,7 @@ LGraphNode.prototype.executeAction = function(action)
LGraphCanvas.prototype.getCanvasMenuOptions = function() {
var options = null;
+ var that = this;
if (this.getMenuOptions) {
options = this.getMenuOptions();
} else {
@@ -11005,9 +12831,13 @@ LGraphNode.prototype.executeAction = function(action)
has_submenu: true,
callback: LGraphCanvas.onMenuAdd
},
- { content: "Add Group", callback: LGraphCanvas.onGroupAdd }
+ { content: "Add Group", callback: LGraphCanvas.onGroupAdd },
+ //{ content: "Arrange", callback: that.graph.arrange },
//{content:"Collapse All", callback: LGraphCanvas.onMenuCollapseAll }
];
+ /*if (LiteGraph.showCanvasOptions){
+ options.push({ content: "Options", callback: that.showShowGraphOptionsPanel });
+ }*/
if (this._graph_stack && this._graph_stack.length > 0) {
options.push(null, {
@@ -11062,13 +12892,13 @@ LGraphNode.prototype.executeAction = function(action)
content: "Mode",
has_submenu: true,
callback: LGraphCanvas.onMenuNodeMode
- },
- {
- content: "Resize", callback: function() {
- if(node.resizable)
- return LGraphCanvas.onResizeNode;
- }
- },
+ }];
+ if(node.resizable !== false){
+ options.push({
+ content: "Resize", callback: LGraphCanvas.onMenuResizeNode
+ });
+ }
+ options.push(
{
content: "Collapse",
callback: LGraphCanvas.onMenuNodeCollapse
@@ -11085,7 +12915,7 @@ LGraphNode.prototype.executeAction = function(action)
callback: LGraphCanvas.onMenuNodeShapes
},
null
- ];
+ );
}
if (node.onGetInputs) {
@@ -11194,16 +13024,16 @@ LGraphNode.prototype.executeAction = function(action)
menu_info.push({ content: "Disconnect Links", slot: slot });
}
var _slot = slot.input || slot.output;
- menu_info.push(
- _slot.locked || !_slot.removable
- ? "Cannot remove"
- : { content: "Remove Slot", slot: slot }
- );
- menu_info.push(
- _slot.nameLocked
- ? "Cannot rename"
- : { 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 =
@@ -11253,19 +13083,23 @@ LGraphNode.prototype.executeAction = function(action)
if (v.content == "Remove Slot") {
var info = v.slot;
+ node.graph.beforeChange();
if (info.input) {
node.removeInput(info.slot);
} else if (info.output) {
node.removeOutput(info.slot);
}
+ node.graph.afterChange();
return;
} else if (v.content == "Disconnect Links") {
var info = v.slot;
+ node.graph.beforeChange();
if (info.output) {
node.disconnectOutput(info.slot);
} else if (info.input) {
node.disconnectInput(info.slot);
}
+ node.graph.afterChange();
return;
} else if (v.content == "Rename Slot") {
var info = v.slot;
@@ -11280,17 +13114,32 @@ LGraphNode.prototype.executeAction = function(action)
if (input && slot_info) {
input.value = slot_info.label || "";
}
- dialog
- .querySelector("button")
- .addEventListener("click", function(e) {
- if (input.value) {
- if (slot_info) {
- slot_info.label = input.value;
- }
- that.setDirty(true);
+ var inner = function(){
+ node.graph.beforeChange();
+ if (input.value) {
+ if (slot_info) {
+ slot_info.label = input.value;
}
+ that.setDirty(true);
+ }
+ dialog.close();
+ node.graph.afterChange();
+ }
+ dialog.querySelector("button").addEventListener("click", inner);
+ input.addEventListener("keydown", function(e) {
+ dialog.is_modified = true;
+ if (e.keyCode == 27) {
+ //ESC
dialog.close();
- });
+ } else if (e.keyCode == 13) {
+ inner(); // save
+ } else if (e.keyCode != 13 && e.target.localName != "textarea") {
+ return;
+ }
+ e.preventDefault();
+ e.stopPropagation();
+ });
+ input.focus();
}
//if(v.callback)
@@ -11853,6 +13702,9 @@ 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
@@ -12160,23 +14012,65 @@ LGraphNode.prototype.executeAction = function(action)
.filter(Boolean); // split & filter [""]
};
- // helper pointerListener vs mouseListener, used by LGraphCanvas DragAndScale ContextMenu
- LiteGraph.pointerListenerAdd = function(oDOM, sEvent, fCall, capture=false) {
- if (!oDOM || !oDOM.addEventListener || !sEvent || typeof fCall!=="function"){
- console.log("cant pointerListenerAdd "+oDOM+", "+sEvent+", "+fCall);
+ /* 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(LiteGraph.pointerevents_method+sEvent, fCall, capture);
+ oDOM.addEventListener(sMethod+sEvent, fCall, capture);
}
// only pointerevents
case "leave": case "cancel": case "gotpointercapture": case "lostpointercapture":
{
- if (LiteGraph.pointerevents_method!="mouse"){
- return oDOM.addEventListener(LiteGraph.pointerevents_method+sEvent, fCall, capture);
+ if (sMethod!="mouse"){
+ return oDOM.addEventListener(sMethod+sEvent, fCall, capture);
}
}
// not "pointer" || "mouse"
@@ -12186,19 +14080,21 @@ LGraphNode.prototype.executeAction = function(action)
}
LiteGraph.pointerListenerRemove = function(oDOM, sEvent, fCall, capture=false) {
if (!oDOM || !oDOM.removeEventListener || !sEvent || typeof fCall!=="function"){
- console.log("cant pointerListenerRemove "+oDOM+", "+sEvent+", "+fCall);
+ //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":
{
- oDOM.removeEventListener(LiteGraph.pointerevents_method+sEvent, fCall, capture);
+ 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!="mouse"){
+ if (LiteGraph.pointerevents_method=="pointer"){
return oDOM.removeEventListener(LiteGraph.pointerevents_method+sEvent, fCall, capture);
}
}
diff --git a/src/nodes/audio.js b/src/nodes/audio.js
index ca35c6990..2c02bdf33 100644
--- a/src/nodes/audio.js
+++ b/src/nodes/audio.js
@@ -997,7 +997,7 @@ LiteGraph.registerNodeType("audio/waveShaper", LGAudioWaveShaper);
this.audionode = LGAudio.getAudioContext().createGain();
this.audionode.gain.value = 0;
this.addInput("in", "audio");
- this.addInput("gate", "bool");
+ this.addInput("gate", "boolean");
this.addOutput("out", "audio");
this.gate = false;
}
diff --git a/src/nodes/base.js b/src/nodes/base.js
index 7546d67af..867e4f984 100755
--- a/src/nodes/base.js
+++ b/src/nodes/base.js
@@ -724,9 +724,10 @@
LiteGraph.registerNodeType("basic/const", ConstantNumber);
function ConstantBoolean() {
- this.addOutput("", "boolean");
+ 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];
}
@@ -753,7 +754,7 @@
LiteGraph.registerNodeType("basic/boolean", ConstantBoolean);
function ConstantString() {
- this.addOutput("", "string");
+ this.addOutput("string", "string");
this.addProperty("value", "");
this.widget = this.addWidget("text","value","","value"); //link to property value
this.widgets_up = true;
@@ -800,8 +801,8 @@
LiteGraph.registerNodeType( "basic/object", ConstantObject );
function ConstantFile() {
- this.addInput("url", "");
- this.addOutput("", "");
+ this.addInput("url", "string");
+ this.addOutput("file", "string");
this.addProperty("url", "");
this.addProperty("type", "text");
this.widget = this.addWidget("text","url","","url");
@@ -899,7 +900,7 @@
//to store json objects
function ConstantData() {
- this.addOutput("", "");
+ this.addOutput("data", "object");
this.addProperty("value", "");
this.widget = this.addWidget("text","json","","value");
this.widgets_up = true;
@@ -934,10 +935,10 @@
//to store json objects
function ConstantArray() {
- this._value = [];
- this.addInput("", "");
- this.addOutput("", "array");
- this.addOutput("length", "number");
+ 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;
@@ -974,7 +975,7 @@
for(var i = 0; i < v.length; ++i)
this._value[i] = v[i];
}
- this.setOutputData(0, this._value );
+ this.setOutputData(0, this._value);
this.setOutputData(1, this._value ? ( this._value.length || 0) : 0 );
};
@@ -988,7 +989,7 @@
this.addInput("value", "");
this.addOutput("arr", "array");
this.properties = { index: 0 };
- this.widget = this.addWidget("number","i",this.properties.index,"index");
+ this.widget = this.addWidget("number","i",this.properties.index,"index",{precision: 0, step: 10, min: 0});
}
SetArray.title = "Set Array";
@@ -1062,9 +1063,9 @@
LiteGraph.registerNodeType("basic/table[][]", TableElement);
function ObjectProperty() {
- this.addInput("obj", "");
- this.addOutput("", "");
- this.addProperty("value", "");
+ 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];
@@ -1146,9 +1147,9 @@
function MergeObjects() {
- this.addInput("A", "");
- this.addInput("B", "");
- this.addOutput("", "");
+ this.addInput("A", "object");
+ this.addInput("B", "object");
+ this.addOutput("out", "object");
this._result = {};
var that = this;
this.addWidget("button","clear","",function(){
@@ -1396,21 +1397,27 @@
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(param);
+ console.log(msg);
} else if (action == "warn") {
- console.warn(param);
+ console.warn(msg);
} else if (action == "error") {
- console.error(param);
+ console.error(msg);
}
};
Console.prototype.onExecute = function() {
- var msg = this.getInputData(1);
- if (msg !== null) {
+ 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.log(msg);
};
Console.prototype.onGetInputs = function() {
@@ -1455,9 +1462,9 @@
function NodeScript() {
this.size = [60, 30];
this.addProperty("onExecute", "return A;");
- this.addInput("A", "");
- this.addInput("B", "");
- this.addOutput("out", "");
+ this.addInput("A", 0);
+ this.addInput("B", 0);
+ this.addOutput("out", 0);
this._func = null;
this.data = {};
@@ -1534,4 +1541,102 @@
};
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);
diff --git a/src/nodes/events.js b/src/nodes/events.js
index af73fb591..00f147ae6 100644
--- a/src/nodes/events.js
+++ b/src/nodes/events.js
@@ -11,7 +11,7 @@
LogEvent.title = "Log Event";
LogEvent.desc = "Log event in console";
- LogEvent.prototype.onAction = function(action, param) {
+ LogEvent.prototype.onAction = function(action, param, options) {
console.log(action, param);
};
@@ -31,18 +31,18 @@
TriggerEvent.title = "TriggerEvent";
TriggerEvent.desc = "Triggers event if input evaluates to true";
- TriggerEvent.prototype.onExecute = function(action, param) {
+ 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);
+ this.triggerSlot(0, param, null, options);
if(!v && must_resend)
- this.triggerSlot(2, param);
+ this.triggerSlot(2, param, null, options);
if(changed)
- this.triggerSlot(1, param);
+ this.triggerSlot(1, param, null, options);
this.prev = v;
};
@@ -50,8 +50,8 @@
//Sequencer for events
function Sequencer() {
- var that = this;
- this.addInput("", LiteGraph.ACTION);
+ var that = this;
+ this.addInput("", LiteGraph.ACTION);
this.addInput("", LiteGraph.ACTION);
this.addInput("", LiteGraph.ACTION);
this.addOutput("", LiteGraph.EVENT);
@@ -72,10 +72,13 @@
return "";
};
- Sequencer.prototype.onAction = function(action, param) {
+ Sequencer.prototype.onAction = function(action, param, options) {
if (this.outputs) {
+ options = options || {};
for (var i = 0; i < this.outputs.length; ++i) {
- this.triggerSlot(i, param);
+ // CREATE A NEW ID FOR THE ACTION
+ options.action_call = options.action_call?options.action_call+"_seq_"+i:this.id+"_"+(action?action:"action")+"_seq_"+i+"_"+Math.floor(Math.random()*9999);
+ this.triggerSlot(i, param, null, options);
}
}
};
@@ -97,7 +100,7 @@
FilterEvent.title = "Filter Event";
FilterEvent.desc = "Blocks events that do not match the filter";
- FilterEvent.prototype.onAction = function(action, param) {
+ FilterEvent.prototype.onAction = function(action, param, options) {
if (param == null) {
return;
}
@@ -120,7 +123,7 @@
}
}
- this.triggerSlot(0, param);
+ this.triggerSlot(0, param, null, options);
};
LiteGraph.registerNodeType("events/filter", FilterEvent);
@@ -142,8 +145,9 @@
this._value = this.getInputData(1);
}
- EventBranch.prototype.onAction = function(action, param) {
- this.triggerSlot(this._value ? 0 : 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);
@@ -155,6 +159,8 @@
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;
}
@@ -168,7 +174,7 @@
return this.title;
};
- EventCounter.prototype.onAction = function(action, param) {
+ EventCounter.prototype.onAction = function(action, param, options) {
var v = this.num;
if (action == "inc") {
this.num += 1;
@@ -193,6 +199,9 @@
};
EventCounter.prototype.onExecute = function() {
+ if(this.properties.doCountExecution){
+ this.num += 1;
+ }
this.setOutputData(1, this.num);
};
@@ -211,16 +220,16 @@
DelayEvent.title = "Delay";
DelayEvent.desc = "Delays one event";
- DelayEvent.prototype.onAction = function(action, param) {
+ DelayEvent.prototype.onAction = function(action, param, options) {
var time = this.properties.time_in_ms;
if (time <= 0) {
- this.trigger(null, param);
+ this.trigger(null, param, options);
} else {
this._pending.push([time, param]);
}
};
- DelayEvent.prototype.onExecute = function() {
+ DelayEvent.prototype.onExecute = function(param, options) {
var dt = this.graph.elapsed_time * 1000; //in ms
if (this.isInputConnected(1)) {
@@ -228,9 +237,9 @@
}
for (var i = 0; i < this._pending.length; ++i) {
- var action = this._pending[i];
- action[0] -= dt;
- if (action[0] > 0) {
+ var actionPass = this._pending[i];
+ actionPass[0] -= dt;
+ if (actionPass[0] > 0) {
continue;
}
@@ -239,7 +248,7 @@
--i;
//trigger
- this.trigger(null, action[1]);
+ this.trigger(null, actionPass[1], options);
}
};
@@ -359,9 +368,9 @@
function DataStore() {
- this.addInput("data", "");
+ this.addInput("data", 0);
this.addInput("assign", LiteGraph.ACTION);
- this.addOutput("data", "");
+ this.addOutput("data", 0);
this._last_value = null;
this.properties = { data: null, serialize: true };
var that = this;
@@ -379,7 +388,7 @@
this.setOutputData(0, this.properties.data );
}
- DataStore.prototype.onAction = function(action, param) {
+ DataStore.prototype.onAction = function(action, param, options) {
this.properties.data = this._last_value;
};
diff --git a/src/nodes/interface.js b/src/nodes/interface.js
index 41556429a..62cb51e82 100755
--- a/src/nodes/interface.js
+++ b/src/nodes/interface.js
@@ -71,6 +71,7 @@
local_pos[1] < this.size[1] - 2
) {
this.clicked = true;
+ this.setOutputData(1, this.clicked);
this.triggerSlot(0, this.properties.message);
return true;
}
@@ -672,7 +673,7 @@
typeof v == "number" ? v.toFixed(this.properties["decimals"]) : v;
if (typeof this.str == "string") {
- var lines = this.str.split("\\n");
+ var lines = this.str.replace(/[\r\n]/g, "\\n").split("\\n");
for (var i=0; i < lines.length; i++) {
ctx.fillText(
lines[i],
diff --git a/src/nodes/logic.js b/src/nodes/logic.js
index e13c419ff..fe273dc3f 100755
--- a/src/nodes/logic.js
+++ b/src/nodes/logic.js
@@ -82,4 +82,120 @@
};
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() {
+ ret = true;
+ for (inX in this.inputs){
+ if (!this.getInputData(inX)){
+ 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() {
+ ret = false;
+ for (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() {
+ last = null;
+ ret = true;
+ for (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);
diff --git a/src/nodes/math.js b/src/nodes/math.js
index 66fff8355..609eb3f6f 100755
--- a/src/nodes/math.js
+++ b/src/nodes/math.js
@@ -1,1329 +1,1329 @@
-(function(global) {
- var LiteGraph = global.LiteGraph;
-
- //Converter
- function Converter() {
- this.addInput("in", "");
- this.addOutput("out");
- 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, Math.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 = Math.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 = function(A,B) { return A + B; };
- this._result = []; //only used for arrays
- }
-
- MathOperation.values = ["+", "-", "*", "/", "%", "^", "max", "min"];
-
- 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;
- switch (this.properties.OP) {
- case "+": this._func = function(A,B) { return A + B; }; break;
- case "-": this._func = function(A,B) { return A - B; }; break;
- case "x":
- case "X":
- case "*": this._func = function(A,B) { return A * B; }; break;
- case "/": this._func = function(A,B) { return A / B; }; break;
- case "%": this._func = function(A,B) { return A % B; }; break;
- case "^": this._func = function(A,B) { return Math.pow(A, B); }; break;
- case "max": this._func = function(A,B) { return Math.max(A, B); }; break;
- case "min": this._func = function(A,B) { return Math.min(A, B); }; break;
- default:
- console.warn("Unknown operation: " + this.properties.OP);
- this._func = function(A) { return A; };
- break;
- }
- }
-
- 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 result;
- if(A.constructor === Number)
- {
- result = 0;
- result = this._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] = this._func(A[i],B);
- }
- else
- {
- result = {};
- for(var i in A)
- result[i] = this._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", "");
- this.addInput("cond", "boolean");
- this.addOutput("true", "");
- this.addOutput("false", "");
- 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;
+
+ //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, Math.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 = Math.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 = function(A,B) { return A + B; };
+ this._result = []; //only used for arrays
+ }
+
+ MathOperation.values = ["+", "-", "*", "/", "%", "^", "max", "min"];
+
+ 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;
+ switch (this.properties.OP) {
+ case "+": this._func = function(A,B) { return A + B; }; break;
+ case "-": this._func = function(A,B) { return A - B; }; break;
+ case "x":
+ case "X":
+ case "*": this._func = function(A,B) { return A * B; }; break;
+ case "/": this._func = function(A,B) { return A / B; }; break;
+ case "%": this._func = function(A,B) { return A % B; }; break;
+ case "^": this._func = function(A,B) { return Math.pow(A, B); }; break;
+ case "max": this._func = function(A,B) { return Math.max(A, B); }; break;
+ case "min": this._func = function(A,B) { return Math.min(A, B); }; break;
+ default:
+ console.warn("Unknown operation: " + this.properties.OP);
+ this._func = function(A) { return A; };
+ break;
+ }
+ }
+
+ 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 result;
+ if(A.constructor === Number)
+ {
+ result = 0;
+ result = this._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] = this._func(A[i],B);
+ }
+ else
+ {
+ result = {};
+ for(var i in A)
+ result[i] = this._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);
diff --git a/src/nodes/others.js b/src/nodes/others.js
old mode 100755
new mode 100644
index e69de29bb..cca0e6f2b
--- a/src/nodes/others.js
+++ b/src/nodes/others.js
@@ -0,0 +1,37 @@
+(function(global) {
+ var LiteGraph = global.LiteGraph;
+
+ /* in types :: run in console :: var s=""; LiteGraph.slot_types_in.forEach(function(el){s+=el+"\n";}); console.log(s); */
+
+ if(typeof LiteGraph.slot_types_default_in == "undefined") LiteGraph.slot_types_default_in = {}; //[];
+ LiteGraph.slot_types_default_in["_event_"] = "widget/button";
+ LiteGraph.slot_types_default_in["array"] = "basic/array";
+ LiteGraph.slot_types_default_in["boolean"] = "basic/boolean";
+ LiteGraph.slot_types_default_in["number"] = "widget/number";
+ LiteGraph.slot_types_default_in["object"] = "basic/data";
+ LiteGraph.slot_types_default_in["string"] = ["basic/string","string/concatenate"];
+ LiteGraph.slot_types_default_in["vec2"] = "math3d/xy-to-vec2";
+ LiteGraph.slot_types_default_in["vec3"] = "math3d/xyz-to-vec3";
+ LiteGraph.slot_types_default_in["vec4"] = "math3d/xyzw-to-vec4";
+
+ /* out types :: run in console :: var s=""; LiteGraph.slot_types_out.forEach(function(el){s+=el+"\n";}); console.log(s); */
+ if(typeof LiteGraph.slot_types_default_out == "undefined") LiteGraph.slot_types_default_out = {};
+ LiteGraph.slot_types_default_out["_event_"] = ["logic/IF","events/sequencer","events/log","events/counter"];
+ LiteGraph.slot_types_default_out["array"] = ["basic/watch","basic/set_array","basic/array[]"];
+ LiteGraph.slot_types_default_out["boolean"] = ["logic/IF","basic/watch","math/branch","math/gate"];
+ LiteGraph.slot_types_default_out["number"] = ["basic/watch"
+ ,{node:"math/operation",properties:{OP:"*"},title:"A*B"}
+ ,{node:"math/operation",properties:{OP:"/"},title:"A/B"}
+ ,{node:"math/operation",properties:{OP:"+"},title:"A+B"}
+ ,{node:"math/operation",properties:{OP:"-"},title:"A-B"}
+ ,{node:"math/compare",outputs:[["A==B", "boolean"]],title:"A==B"}
+ ,{node:"math/compare",outputs:[["A>B", "boolean"]],title:"A>B"}
+ ,{node:"math/compare",outputs:[["A