Merge remote-tracking branch 'upstream/master'
Catching up to external updates and checking in array fix (this time without build artifacts).
9
CONTRIBUTING.md
Normal file
@@ -0,0 +1,9 @@
|
||||
# Contribution Rules
|
||||
There are some simple rules that everyone should follow:
|
||||
|
||||
### Do not commit files from bulid folder
|
||||
> I usually have horrible merge conflicts when I upload the build version that take me too much time to solve, but I want to keep the build version in the repo, so I guess it would be better if only one of us does the built, which would be me.
|
||||
> https://github.com/jagenjo/litegraph.js/pull/155#issuecomment-656602861
|
||||
Those files will be updated by owner.
|
||||
|
||||
|
||||
11
README.md
@@ -1,15 +1,15 @@
|
||||
# litegraph.js
|
||||
|
||||
A library in Javascript to create graphs in the browser similar to Unreal Blueprints. Nodes can be programmed easily and it includes an editor to construct the graphs.
|
||||
A library in Javascript to create graphs in the browser similar to Unreal Blueprints. Nodes can be programmed easily and it includes an editor to construct and tests the graphs.
|
||||
|
||||
It can be integrated easily in any existing web applications and graphs can be run without the need of the editor.
|
||||
|
||||
Try it in the [demo site](https://tamats.com/projects/litegraph/demo).
|
||||
Try it in the [demo site](https://tamats.com/projects/litegraph/editor).
|
||||
|
||||

|
||||
|
||||
## Features
|
||||
- Renders on Canvas2D (zoom in, zoom out, panning, can be used inside a WebGLTexture)
|
||||
- Renders on Canvas2D (zoom in/out and panning, easy to render complex interfaces, can be used inside a WebGLTexture)
|
||||
- Easy to use editor (searchbox, keyboard shortcuts, multiple selection, context menu, ...)
|
||||
- Optimized to support hundreds of nodes per graph (on editor but also on execution)
|
||||
- Customizable theme (colors, shapes, background)
|
||||
@@ -19,6 +19,7 @@ Try it in the [demo site](https://tamats.com/projects/litegraph/demo).
|
||||
- Graphs can be executed in NodeJS
|
||||
- Highly customizable nodes (color, shape, slots vertical or horizontal, widgets, custom rendering)
|
||||
- Easy to integrate in any JS application (one single file, no dependencies)
|
||||
- Typescript support
|
||||
|
||||
## Nodes provided
|
||||
Although it is easy to create new node types, LiteGraph comes with some default nodes that could be useful for many cases:
|
||||
@@ -35,7 +36,7 @@ You can install it using npm
|
||||
npm install litegraph.js
|
||||
```
|
||||
|
||||
Or downloading the ```build/litegraph.js``` version from this repository.
|
||||
Or downloading the ```build/litegraph.js``` and ```css/litegraph.css``` version from this repository.
|
||||
|
||||
## First project ##
|
||||
|
||||
@@ -179,6 +180,8 @@ You can write any feedback to javi.agenjo@gmail.com
|
||||
- rappestad
|
||||
- InventivetalentDev
|
||||
- NateScarlet
|
||||
- coderofsalvation
|
||||
- ilyabesk
|
||||
|
||||
|
||||
|
||||
|
||||
24065
build/litegraph.js
10005
build/litegraph.min.js
vendored
@@ -93,7 +93,7 @@
|
||||
|
||||
/* BUTTONS **********************/
|
||||
|
||||
.litegraph-editor button {
|
||||
.litegraph-editor .btn {
|
||||
/*font-family: "Metro Light";*/
|
||||
color: #ccc;
|
||||
font-size: 20px;
|
||||
@@ -181,7 +181,7 @@
|
||||
display: inline-block;
|
||||
width: 90px;
|
||||
height: 15px;
|
||||
background-image: url("../demo/imgs/load-progress-empty.png");
|
||||
background-image: url("../editor/imgs/load-progress-empty.png");
|
||||
}
|
||||
|
||||
.litegraph-editor .cpuload .fgload,
|
||||
@@ -190,39 +190,24 @@
|
||||
width: 4px;
|
||||
height: 15px;
|
||||
max-width: 90px;
|
||||
background-image: url("../demo/imgs/load-progress-full.png");
|
||||
background-image: url("../editor/imgs/load-progress-full.png");
|
||||
}
|
||||
|
||||
.litegraph-editor .dialog {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
margin-top: -150px;
|
||||
margin-left: -200px;
|
||||
|
||||
background-color: #151515;
|
||||
|
||||
min-width: 400px;
|
||||
min-height: 300px;
|
||||
box-shadow: 0 0 2px black;
|
||||
.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 .dialog .dialog-header,
|
||||
.litegraph-editor .dialog .dialog-footer {
|
||||
height: 40px;
|
||||
.litegraph-editor .codeflask {
|
||||
background-color: #2a2a2a;
|
||||
}
|
||||
|
||||
.litegraph-editor .dialog .dialog-header .dialog-title {
|
||||
font: 20px "Arial";
|
||||
margin: 4px;
|
||||
padding: 4px 10px;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.litegraph-editor .dialog .dialog-content {
|
||||
height: calc(100% - 40px);
|
||||
width: calc(100% - 10px);
|
||||
background-color: black;
|
||||
margin: 4px;
|
||||
display: inline-block;
|
||||
}
|
||||
.litegraph-editor .codeflask textarea {
|
||||
opacity: 0;
|
||||
}
|
||||
@@ -5,6 +5,7 @@
|
||||
user-select: none;
|
||||
-moz-user-select: none;
|
||||
-webkit-user-select: none;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.litegraph.litecontextmenu {
|
||||
@@ -207,6 +208,233 @@
|
||||
color: black;
|
||||
}
|
||||
|
||||
/* DIALOGs ******/
|
||||
|
||||
.litegraph .dialog {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
margin-top: -150px;
|
||||
margin-left: -200px;
|
||||
|
||||
background-color: #2A2A2A;
|
||||
|
||||
min-width: 400px;
|
||||
min-height: 200px;
|
||||
box-shadow: 0 0 4px #111;
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.litegraph .dialog.settings {
|
||||
left: 10px;
|
||||
top: 10px;
|
||||
height: calc( 100% - 20px );
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
.litegraph .dialog .close {
|
||||
float: right;
|
||||
margin: 4px;
|
||||
margin-right: 10px;
|
||||
cursor: pointer;
|
||||
font-size: 1.4em;
|
||||
}
|
||||
|
||||
.litegraph .dialog .close:hover {
|
||||
color: white;
|
||||
}
|
||||
|
||||
.litegraph .dialog .dialog-header {
|
||||
color: #AAA;
|
||||
border-bottom: 1px solid #161616;
|
||||
}
|
||||
|
||||
.litegraph .dialog .dialog-header { height: 40px; }
|
||||
.litegraph .dialog .dialog-footer { height: 50px; padding: 10px; border-top: 1px solid #1a1a1a;}
|
||||
|
||||
.litegraph .dialog .dialog-header .dialog-title {
|
||||
font: 20px "Arial";
|
||||
margin: 4px;
|
||||
padding: 4px 10px;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.litegraph .dialog .dialog-content {
|
||||
height: calc(100% - 90px);
|
||||
width: 100%;
|
||||
min-height: 100px;
|
||||
display: inline-block;
|
||||
color: #AAA;
|
||||
/*background-color: black;*/
|
||||
}
|
||||
|
||||
.litegraph .dialog .dialog-content h3 {
|
||||
margin: 10px;
|
||||
}
|
||||
|
||||
.litegraph .dialog .dialog-content .connections {
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.litegraph .dialog .dialog-content .connections .connections_side {
|
||||
width: calc(50% - 5px);
|
||||
min-height: 100px;
|
||||
background-color: black;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.litegraph .dialog .node_type {
|
||||
font-size: 1.2em;
|
||||
display: block;
|
||||
margin: 10px;
|
||||
}
|
||||
|
||||
.litegraph .dialog .node_desc {
|
||||
opacity: 0.5;
|
||||
display: block;
|
||||
margin: 10px;
|
||||
}
|
||||
|
||||
.litegraph .dialog .separator {
|
||||
display: block;
|
||||
width: calc( 100% - 4px );
|
||||
height: 1px;
|
||||
border-top: 1px solid #000;
|
||||
border-bottom: 1px solid #333;
|
||||
margin: 10px 2px;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.litegraph .dialog .property {
|
||||
margin-bottom: 2px;
|
||||
padding: 4px;
|
||||
}
|
||||
|
||||
.litegraph .dialog .property_name {
|
||||
color: #737373;
|
||||
display: inline-block;
|
||||
text-align: left;
|
||||
vertical-align: top;
|
||||
width: 120px;
|
||||
padding-left: 4px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.litegraph .dialog .property_value {
|
||||
display: inline-block;
|
||||
text-align: right;
|
||||
color: #AAA;
|
||||
background-color: #1A1A1A;
|
||||
width: calc( 100% - 122px );
|
||||
max-height: 300px;
|
||||
padding: 4px;
|
||||
padding-right: 12px;
|
||||
overflow: hidden;
|
||||
cursor: pointer;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.litegraph .dialog .property_value:hover {
|
||||
color: white;
|
||||
}
|
||||
|
||||
.litegraph .dialog .property.boolean .property_value {
|
||||
padding-right: 30px;
|
||||
}
|
||||
|
||||
.litegraph .dialog .btn {
|
||||
border-radius: 4px;
|
||||
padding: 4px 20px;
|
||||
margin-left: 0px;
|
||||
background-color: #060606;
|
||||
color: #8e8e8e;
|
||||
}
|
||||
|
||||
.litegraph .dialog .btn:hover {
|
||||
background-color: #111;
|
||||
color: #FFF;
|
||||
}
|
||||
|
||||
.litegraph .dialog .btn.delete:hover {
|
||||
background-color: #F33;
|
||||
color: black;
|
||||
}
|
||||
|
||||
.litegraph .subgraph_property {
|
||||
padding: 4px;
|
||||
}
|
||||
|
||||
.litegraph .subgraph_property:hover {
|
||||
background-color: #333;
|
||||
}
|
||||
|
||||
.litegraph .subgraph_property.extra {
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.litegraph .subgraph_property span.name {
|
||||
font-size: 1.3em;
|
||||
padding-left: 4px;
|
||||
}
|
||||
|
||||
.litegraph .subgraph_property span.type {
|
||||
opacity: 0.5;
|
||||
margin-right: 20px;
|
||||
padding-left: 4px;
|
||||
}
|
||||
|
||||
.litegraph .subgraph_property span.label {
|
||||
display: inline-block;
|
||||
width: 60px;
|
||||
padding: 0px 10px;
|
||||
}
|
||||
|
||||
.litegraph .subgraph_property input {
|
||||
width: 140px;
|
||||
color: #999;
|
||||
background-color: #1A1A1A;
|
||||
border-radius: 4px;
|
||||
border: 0;
|
||||
margin-right: 10px;
|
||||
padding: 4px;
|
||||
padding-left: 10px;
|
||||
}
|
||||
|
||||
.litegraph .subgraph_property button {
|
||||
background-color: #1c1c1c;
|
||||
color: #aaa;
|
||||
border: 0;
|
||||
border-radius: 2px;
|
||||
padding: 4px 10px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.litegraph .subgraph_property.extra {
|
||||
color: #ccc;
|
||||
}
|
||||
|
||||
.litegraph .subgraph_property.extra input {
|
||||
background-color: #111;
|
||||
}
|
||||
|
||||
.litegraph .bullet_icon {
|
||||
margin-left: 10px;
|
||||
border-radius: 10px;
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
background-color: #666;
|
||||
display: inline-block;
|
||||
margin-top: 2px;
|
||||
margin-right: 4px;
|
||||
transition: background-color 0.1s ease 0s;
|
||||
-moz-transition: background-color 0.1s ease 0s;
|
||||
}
|
||||
|
||||
.litegraph .bullet_icon:hover {
|
||||
background-color: #698;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/* OLD */
|
||||
|
||||
.graphcontextmenu {
|
||||
@@ -285,6 +513,7 @@
|
||||
background-color: #333;
|
||||
font-size: 1.2em;
|
||||
box-shadow: 0 0 10px black !important;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.graphdialog.rounded {
|
||||
|
||||
|
Before Width: | Height: | Size: 350 B After Width: | Height: | Size: 350 B |
|
Before Width: | Height: | Size: 368 B After Width: | Height: | Size: 368 B |
|
Before Width: | Height: | Size: 607 B After Width: | Height: | Size: 607 B |
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 346 B After Width: | Height: | Size: 346 B |
|
Before Width: | Height: | Size: 344 B After Width: | Height: | Size: 344 B |
|
Before Width: | Height: | Size: 349 B After Width: | Height: | Size: 349 B |
|
Before Width: | Height: | Size: 330 B After Width: | Height: | Size: 330 B |
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 179 B After Width: | Height: | Size: 179 B |
|
Before Width: | Height: | Size: 836 B After Width: | Height: | Size: 836 B |
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 281 B After Width: | Height: | Size: 281 B |
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
@@ -15,7 +15,10 @@
|
||||
|
||||
<script type="text/javascript" src="../external/jquery-1.6.2.min.js"></script>
|
||||
<script type="text/javascript" src="https://tamats.com/projects/sillyserver/src/sillyclient.js"></script>
|
||||
<!-- <script type="text/javascript" src="https://unpkg.com/codeflask/build/codeflask.min.js"></script> -->
|
||||
<script type="text/javascript" src="js/libs/gl-matrix-min.js"></script>
|
||||
<script type="text/javascript" src="js/libs/audiosynth.js"></script>
|
||||
<script type="text/javascript" src="js/libs/midi-parser.js"></script>
|
||||
|
||||
<script type="text/javascript" src="../src/litegraph.js"></script>
|
||||
<script type="text/javascript" src="../src/litegraph-editor.js"></script>
|
||||
@@ -28,6 +31,7 @@
|
||||
<script type="text/javascript" src="../src/nodes/strings.js"></script>
|
||||
<script type="text/javascript" src="../src/nodes/interface.js"></script>
|
||||
<script type="text/javascript" src="../src/nodes/geometry.js"></script>
|
||||
<script type="text/javascript" src="../src/nodes/graphics.js"></script>
|
||||
<script type="text/javascript" src="../src/nodes/input.js"></script>
|
||||
<script type="text/javascript" src="../src/nodes/midi.js"></script>
|
||||
<script type="text/javascript" src="../src/nodes/audio.js"></script>
|
||||
@@ -1,6 +1,7 @@
|
||||
var webgl_canvas = null;
|
||||
|
||||
LiteGraph.node_images_path = "../nodes_data/";
|
||||
var editor = new LiteGraph.Editor("main");
|
||||
var editor = new LiteGraph.Editor("main",{miniwindow:false});
|
||||
window.graphcanvas = editor.graphcanvas;
|
||||
window.graph = editor.graph;
|
||||
window.addEventListener("resize", function() { editor.graphcanvas.resize(); } );
|
||||
@@ -10,10 +11,13 @@ window.onbeforeunload = function(){
|
||||
localStorage.setItem("litegraphg demo backup", data );
|
||||
}
|
||||
|
||||
//enable scripting
|
||||
LiteGraph.allow_scripts = true;
|
||||
|
||||
//create scene selector
|
||||
var elem = document.createElement("span");
|
||||
elem.className = "selector";
|
||||
elem.innerHTML = "Demo <select><option>Empty</option></select> <button id='save'>Save</button><button id='load'>Load</button><button id='download'>Download</button>";
|
||||
elem.innerHTML = "Demo <select><option>Empty</option></select> <button class='btn' id='save'>Save</button><button class='btn' id='load'>Load</button><button class='btn' id='download'>Download</button> | <button class='btn' id='webgl'>WebGL</button>";
|
||||
editor.tools.appendChild(elem);
|
||||
var select = elem.querySelector("select");
|
||||
select.addEventListener("change", function(e){
|
||||
@@ -54,6 +58,9 @@ elem.querySelector("#download").addEventListener("click",function(){
|
||||
setTimeout( function(){ URL.revokeObjectURL( url ); }, 1000*60 ); //wait one minute to revoke url
|
||||
});
|
||||
|
||||
elem.querySelector("#webgl").addEventListener("click", enableWebGL );
|
||||
|
||||
|
||||
function addDemo( name, url )
|
||||
{
|
||||
var option = document.createElement("option");
|
||||
@@ -81,5 +88,79 @@ addDemo("autobackup", function(){
|
||||
graph.configure( graph_data );
|
||||
});
|
||||
|
||||
//allows to use the WebGL nodes like textures
|
||||
function enableWebGL()
|
||||
{
|
||||
if( webgl_canvas )
|
||||
{
|
||||
webgl_canvas.style.display = (webgl_canvas.style.display == "none" ? "block" : "none");
|
||||
return;
|
||||
}
|
||||
|
||||
var libs = [
|
||||
"js/libs/gl-matrix-min.js",
|
||||
"js/libs/litegl.js",
|
||||
"../src/nodes/gltextures.js",
|
||||
"../src/nodes/glfx.js",
|
||||
"../src/nodes/glshaders.js",
|
||||
"../src/nodes/geometry.js"
|
||||
];
|
||||
|
||||
function fetchJS()
|
||||
{
|
||||
if(libs.length == 0)
|
||||
return on_ready();
|
||||
|
||||
var script = null;
|
||||
script = document.createElement("script");
|
||||
script.onload = fetchJS;
|
||||
script.src = libs.shift();
|
||||
document.head.appendChild(script);
|
||||
}
|
||||
|
||||
fetchJS();
|
||||
|
||||
function on_ready()
|
||||
{
|
||||
console.log(this.src);
|
||||
if(!window.GL)
|
||||
return;
|
||||
webgl_canvas = document.createElement("canvas");
|
||||
webgl_canvas.width = 400;
|
||||
webgl_canvas.height = 300;
|
||||
webgl_canvas.style.position = "absolute";
|
||||
webgl_canvas.style.top = "0px";
|
||||
webgl_canvas.style.right = "0px";
|
||||
webgl_canvas.style.border = "1px solid #AAA";
|
||||
|
||||
webgl_canvas.addEventListener("click", function(){
|
||||
var rect = webgl_canvas.parentNode.getBoundingClientRect();
|
||||
if( webgl_canvas.width != rect.width )
|
||||
{
|
||||
webgl_canvas.width = rect.width;
|
||||
webgl_canvas.height = rect.height;
|
||||
}
|
||||
else
|
||||
{
|
||||
webgl_canvas.width = 400;
|
||||
webgl_canvas.height = 300;
|
||||
}
|
||||
});
|
||||
|
||||
var parent = document.querySelector(".editor-area");
|
||||
parent.appendChild( webgl_canvas );
|
||||
var gl = GL.create({ canvas: webgl_canvas });
|
||||
if(!gl)
|
||||
return;
|
||||
|
||||
editor.graph.onBeforeStep = ondraw;
|
||||
|
||||
console.log("webgl ready");
|
||||
function ondraw ()
|
||||
{
|
||||
gl.clearColor(0,0,0,0);
|
||||
gl.clear( gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT );
|
||||
gl.viewport(0,0,gl.canvas.width, gl.canvas.height );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -91,6 +91,8 @@ function TestWidgetsNode()
|
||||
this.text = this.addWidget("text","Text", "edit me", function(v){}, {} );
|
||||
this.toggle = this.addWidget("toggle","Toggle", true, function(v){}, { on: "enabled", off:"disabled"} );
|
||||
this.button = this.addWidget("button","Button", null, function(v){}, {} );
|
||||
this.toggle2 = this.addWidget("toggle","Disabled", true, function(v){}, { on: "enabled", off:"disabled"} );
|
||||
this.toggle2.disabled = true;
|
||||
this.size = this.computeSize();
|
||||
this.serialize_widgets = true;
|
||||
}
|
||||
@@ -99,7 +101,6 @@ TestWidgetsNode.title = "Widgets";
|
||||
|
||||
LiteGraph.registerNodeType("features/widgets", TestWidgetsNode );
|
||||
|
||||
|
||||
//Show value inside the debug console
|
||||
function TestSpecialNode()
|
||||
{
|
||||
28
editor/js/libs/gl-matrix-min.js
vendored
Normal file
13432
editor/js/libs/litegl.js
Normal file
356
editor/js/libs/midi-parser.js
Normal file
@@ -0,0 +1,356 @@
|
||||
/*
|
||||
Project Name : midi-parser-js
|
||||
Project Url : https://github.com/colxi/midi-parser-js/
|
||||
Author : colxi
|
||||
Author URL : http://www.colxi.info/
|
||||
Description : MidiParser library reads .MID binary files, Base64 encoded MIDI Data,
|
||||
or UInt8 Arrays, and outputs as a readable and structured JS object.
|
||||
*/
|
||||
|
||||
(function(){
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* CROSSBROWSER & NODEjs POLYFILL for ATOB() -
|
||||
* By: https://github.com/MaxArt2501 (modified)
|
||||
* @param {string} string [description]
|
||||
* @return {[type]} [description]
|
||||
*/
|
||||
const _atob = function(string) {
|
||||
// base64 character set, plus padding character (=)
|
||||
let b64 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
|
||||
// Regular expression to check formal correctness of base64 encoded strings
|
||||
let b64re = /^(?:[A-Za-z\d+\/]{4})*?(?:[A-Za-z\d+\/]{2}(?:==)?|[A-Za-z\d+\/]{3}=?)?$/;
|
||||
// remove data type signatures at the begining of the string
|
||||
// eg : "data:audio/mid;base64,"
|
||||
string = string.replace( /^.*?base64,/ , '');
|
||||
// atob can work with strings with whitespaces, even inside the encoded part,
|
||||
// but only \t, \n, \f, \r and ' ', which can be stripped.
|
||||
string = String(string).replace(/[\t\n\f\r ]+/g, '');
|
||||
if (!b64re.test(string))
|
||||
throw new TypeError('Failed to execute _atob() : The string to be decoded is not correctly encoded.');
|
||||
|
||||
// Adding the padding if missing, for semplicity
|
||||
string += '=='.slice(2 - (string.length & 3));
|
||||
let bitmap, result = '';
|
||||
let r1, r2, i = 0;
|
||||
for (; i < string.length;) {
|
||||
bitmap = b64.indexOf(string.charAt(i++)) << 18 | b64.indexOf(string.charAt(i++)) << 12
|
||||
| (r1 = b64.indexOf(string.charAt(i++))) << 6 | (r2 = b64.indexOf(string.charAt(i++)));
|
||||
|
||||
result += r1 === 64 ? String.fromCharCode(bitmap >> 16 & 255)
|
||||
: r2 === 64 ? String.fromCharCode(bitmap >> 16 & 255, bitmap >> 8 & 255)
|
||||
: String.fromCharCode(bitmap >> 16 & 255, bitmap >> 8 & 255, bitmap & 255);
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* [MidiParser description]
|
||||
* @type {Object}
|
||||
*/
|
||||
const MidiParser = {
|
||||
// debug (bool), when enabled will log in console unimplemented events
|
||||
// warnings and internal handled errors.
|
||||
debug: false,
|
||||
|
||||
/**
|
||||
* [parse description]
|
||||
* @param {[type]} input [description]
|
||||
* @param {[type]} _callback [description]
|
||||
* @return {[type]} [description]
|
||||
*/
|
||||
parse: function(input, _callback){
|
||||
if(input instanceof Uint8Array) return MidiParser.Uint8(input);
|
||||
else if(typeof input === 'string') return MidiParser.Base64(input);
|
||||
else if(input instanceof HTMLElement && input.type === 'file') return MidiParser.addListener(input , _callback);
|
||||
else throw new Error('MidiParser.parse() : Invalid input provided');
|
||||
},
|
||||
|
||||
/**
|
||||
* addListener() should be called in order attach a listener to the INPUT HTML element
|
||||
* that will provide the binary data automating the conversion, and returning
|
||||
* the structured data to the provided callback function.
|
||||
*/
|
||||
addListener: function(_fileElement, _callback){
|
||||
if(!File || !FileReader) throw new Error('The File|FileReader APIs are not supported in this browser. Use instead MidiParser.Base64() or MidiParser.Uint8()');
|
||||
|
||||
// validate provided element
|
||||
if( _fileElement === undefined ||
|
||||
!(_fileElement instanceof HTMLElement) ||
|
||||
_fileElement.tagName !== 'INPUT' ||
|
||||
_fileElement.type.toLowerCase() !== 'file'
|
||||
){
|
||||
console.warn('MidiParser.addListener() : Provided element is not a valid FILE INPUT element');
|
||||
return false;
|
||||
}
|
||||
_callback = _callback || function(){};
|
||||
|
||||
_fileElement.addEventListener('change', function(InputEvt){ // set the 'file selected' event handler
|
||||
if (!InputEvt.target.files.length) return false; // return false if no elements where selected
|
||||
console.log('MidiParser.addListener() : File detected in INPUT ELEMENT processing data..');
|
||||
let reader = new FileReader(); // prepare the file Reader
|
||||
reader.readAsArrayBuffer(InputEvt.target.files[0]); // read the binary data
|
||||
reader.onload = function(e){
|
||||
_callback( MidiParser.Uint8(new Uint8Array(e.target.result))); // encode data with Uint8Array and call the parser
|
||||
};
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Base64() : convert baset4 string into uint8 array buffer, before performing the
|
||||
* parsing subroutine.
|
||||
*/
|
||||
Base64 : function(b64String){
|
||||
b64String = String(b64String);
|
||||
|
||||
let raw = _atob(b64String);
|
||||
let rawLength = raw.length;
|
||||
let t_array = new Uint8Array(new ArrayBuffer(rawLength));
|
||||
|
||||
for(let i=0; i<rawLength; i++) t_array[i] = raw.charCodeAt(i);
|
||||
return MidiParser.Uint8(t_array) ;
|
||||
},
|
||||
|
||||
/**
|
||||
* parse() : function reads the binary data, interpreting and spliting each chuck
|
||||
* and parsing it to a structured Object. When job is finised returns the object
|
||||
* or 'false' if any error was generated.
|
||||
*/
|
||||
Uint8: function(FileAsUint8Array){
|
||||
let file = {
|
||||
data: null,
|
||||
pointer: 0,
|
||||
movePointer: function(_bytes){ // move the pointer negative and positive direction
|
||||
this.pointer += _bytes;
|
||||
return this.pointer;
|
||||
},
|
||||
readInt: function(_bytes){ // get integer from next _bytes group (big-endian)
|
||||
_bytes = Math.min(_bytes, this.data.byteLength-this.pointer);
|
||||
if (_bytes < 1) return -1; // EOF
|
||||
let value = 0;
|
||||
if(_bytes > 1){
|
||||
for(let i=1; i<= (_bytes-1); i++){
|
||||
value += this.data.getUint8(this.pointer) * Math.pow(256, (_bytes - i));
|
||||
this.pointer++;
|
||||
}
|
||||
}
|
||||
value += this.data.getUint8(this.pointer);
|
||||
this.pointer++;
|
||||
return value;
|
||||
},
|
||||
readStr: function(_bytes){ // read as ASCII chars, the followoing _bytes
|
||||
let text = '';
|
||||
for(let char=1; char <= _bytes; char++) text += String.fromCharCode(this.readInt(1));
|
||||
return text;
|
||||
},
|
||||
readIntVLV: function(){ // read a variable length value
|
||||
let value = 0;
|
||||
if ( this.pointer >= this.data.byteLength ){
|
||||
return -1; // EOF
|
||||
}else if(this.data.getUint8(this.pointer) < 128){ // ...value in a single byte
|
||||
value = this.readInt(1);
|
||||
}else{ // ...value in multiple bytes
|
||||
let FirstBytes = [];
|
||||
while(this.data.getUint8(this.pointer) >= 128){
|
||||
FirstBytes.push(this.readInt(1) - 128);
|
||||
}
|
||||
let lastByte = this.readInt(1);
|
||||
for(let dt = 1; dt <= FirstBytes.length; dt++){
|
||||
value += FirstBytes[FirstBytes.length - dt] * Math.pow(128, dt);
|
||||
}
|
||||
value += lastByte;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
};
|
||||
|
||||
file.data = new DataView(FileAsUint8Array.buffer, FileAsUint8Array.byteOffset, FileAsUint8Array.byteLength); // 8 bits bytes file data array
|
||||
// ** read FILE HEADER
|
||||
if(file.readInt(4) !== 0x4D546864){
|
||||
console.warn('Header validation failed (not MIDI standard or file corrupt.)');
|
||||
return false; // Header validation failed (not MIDI standard or file corrupt.)
|
||||
}
|
||||
let headerSize = file.readInt(4); // header size (unused var), getted just for read pointer movement
|
||||
let MIDI = {}; // create new midi object
|
||||
MIDI.formatType = file.readInt(2); // get MIDI Format Type
|
||||
MIDI.tracks = file.readInt(2); // get ammount of track chunks
|
||||
MIDI.track = []; // create array key for track data storing
|
||||
let timeDivisionByte1 = file.readInt(1); // get Time Division first byte
|
||||
let timeDivisionByte2 = file.readInt(1); // get Time Division second byte
|
||||
if(timeDivisionByte1 >= 128){ // discover Time Division mode (fps or tpf)
|
||||
MIDI.timeDivision = [];
|
||||
MIDI.timeDivision[0] = timeDivisionByte1 - 128; // frames per second MODE (1st byte)
|
||||
MIDI.timeDivision[1] = timeDivisionByte2; // ticks in each frame (2nd byte)
|
||||
}else MIDI.timeDivision = (timeDivisionByte1 * 256) + timeDivisionByte2;// else... ticks per beat MODE (2 bytes value)
|
||||
|
||||
// ** read TRACK CHUNK
|
||||
for(let t=1; t <= MIDI.tracks; t++){
|
||||
MIDI.track[t-1] = {event: []}; // create new Track entry in Array
|
||||
let headerValidation = file.readInt(4);
|
||||
if ( headerValidation === -1 ) break; // EOF
|
||||
if(headerValidation !== 0x4D54726B) return false; // Track chunk header validation failed.
|
||||
file.readInt(4); // move pointer. get chunk size (bytes length)
|
||||
let e = 0; // init event counter
|
||||
let endOfTrack = false; // FLAG for track reading secuence breaking
|
||||
// ** read EVENT CHUNK
|
||||
let statusByte;
|
||||
let laststatusByte;
|
||||
while(!endOfTrack){
|
||||
e++; // increase by 1 event counter
|
||||
MIDI.track[t-1].event[e-1] = {}; // create new event object, in events array
|
||||
MIDI.track[t-1].event[e-1].deltaTime = file.readIntVLV(); // get DELTA TIME OF MIDI event (Variable Length Value)
|
||||
statusByte = file.readInt(1); // read EVENT TYPE (STATUS BYTE)
|
||||
if(statusByte === -1) break; // EOF
|
||||
else if(statusByte >= 128) laststatusByte = statusByte; // NEW STATUS BYTE DETECTED
|
||||
else{ // 'RUNNING STATUS' situation detected
|
||||
statusByte = laststatusByte; // apply last loop, Status Byte
|
||||
file.movePointer(-1); // move back the pointer (cause readed byte is not status byte)
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// ** IS META EVENT
|
||||
//
|
||||
if(statusByte === 0xFF){ // Meta Event type
|
||||
MIDI.track[t-1].event[e-1].type = 0xFF; // assign metaEvent code to array
|
||||
MIDI.track[t-1].event[e-1].metaType = file.readInt(1); // assign metaEvent subtype
|
||||
let metaEventLength = file.readIntVLV(); // get the metaEvent length
|
||||
switch(MIDI.track[t-1].event[e-1].metaType){
|
||||
case 0x2F: // end of track, has no data byte
|
||||
case -1: // EOF
|
||||
endOfTrack = true; // change FLAG to force track reading loop breaking
|
||||
break;
|
||||
case 0x01: // Text Event
|
||||
case 0x02: // Copyright Notice
|
||||
case 0x03:
|
||||
case 0x04: // Instrument Name
|
||||
case 0x05: // Lyrics)
|
||||
case 0x07: // Cue point // Sequence/Track Name (documentation: http://www.ta7.de/txt/musik/musi0006.htm)
|
||||
case 0x06: // Marker
|
||||
MIDI.track[t-1].event[e-1].data = file.readStr(metaEventLength);
|
||||
break;
|
||||
case 0x21: // MIDI PORT
|
||||
case 0x59: // Key Signature
|
||||
case 0x51: // Set Tempo
|
||||
MIDI.track[t-1].event[e-1].data = file.readInt(metaEventLength);
|
||||
break;
|
||||
case 0x54: // SMPTE Offset
|
||||
MIDI.track[t-1].event[e-1].data = [];
|
||||
MIDI.track[t-1].event[e-1].data[0] = file.readInt(1);
|
||||
MIDI.track[t-1].event[e-1].data[1] = file.readInt(1);
|
||||
MIDI.track[t-1].event[e-1].data[2] = file.readInt(1);
|
||||
MIDI.track[t-1].event[e-1].data[3] = file.readInt(1);
|
||||
MIDI.track[t-1].event[e-1].data[4] = file.readInt(1);
|
||||
break;
|
||||
case 0x58: // Time Signature
|
||||
MIDI.track[t-1].event[e-1].data = [];
|
||||
MIDI.track[t-1].event[e-1].data[0] = file.readInt(1);
|
||||
MIDI.track[t-1].event[e-1].data[1] = file.readInt(1);
|
||||
MIDI.track[t-1].event[e-1].data[2] = file.readInt(1);
|
||||
MIDI.track[t-1].event[e-1].data[3] = file.readInt(1);
|
||||
break;
|
||||
default :
|
||||
// if user provided a custom interpreter, call it
|
||||
// and assign to event the returned data
|
||||
if( this.customInterpreter !== null){
|
||||
MIDI.track[t-1].event[e-1].data = this.customInterpreter( MIDI.track[t-1].event[e-1].metaType, file, metaEventLength);
|
||||
}
|
||||
// if no customInterpretr is provided, or returned
|
||||
// false (=apply default), perform default action
|
||||
if(this.customInterpreter === null || MIDI.track[t-1].event[e-1].data === false){
|
||||
file.readInt(metaEventLength);
|
||||
MIDI.track[t-1].event[e-1].data = file.readInt(metaEventLength);
|
||||
if (this.debug) console.info('Unimplemented 0xFF meta event! data block readed as Integer');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// IS REGULAR EVENT
|
||||
//
|
||||
else{ // MIDI Control Events OR System Exclusive Events
|
||||
statusByte = statusByte.toString(16).split(''); // split the status byte HEX representation, to obtain 4 bits values
|
||||
if(!statusByte[1]) statusByte.unshift('0'); // force 2 digits
|
||||
MIDI.track[t-1].event[e-1].type = parseInt(statusByte[0], 16);// first byte is EVENT TYPE ID
|
||||
MIDI.track[t-1].event[e-1].channel = parseInt(statusByte[1], 16);// second byte is channel
|
||||
switch(MIDI.track[t-1].event[e-1].type){
|
||||
case 0xF:{ // System Exclusive Events
|
||||
|
||||
// if user provided a custom interpreter, call it
|
||||
// and assign to event the returned data
|
||||
if( this.customInterpreter !== null){
|
||||
MIDI.track[t-1].event[e-1].data = this.customInterpreter( MIDI.track[t-1].event[e-1].type, file , false);
|
||||
}
|
||||
|
||||
// if no customInterpretr is provided, or returned
|
||||
// false (=apply default), perform default action
|
||||
if(this.customInterpreter === null || MIDI.track[t-1].event[e-1].data === false){
|
||||
let event_length = file.readIntVLV();
|
||||
MIDI.track[t-1].event[e-1].data = file.readInt(event_length);
|
||||
if (this.debug) console.info('Unimplemented 0xF exclusive events! data block readed as Integer');
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 0xA: // Note Aftertouch
|
||||
case 0xB: // Controller
|
||||
case 0xE: // Pitch Bend Event
|
||||
case 0x8: // Note off
|
||||
case 0x9: // Note On
|
||||
MIDI.track[t-1].event[e-1].data = [];
|
||||
MIDI.track[t-1].event[e-1].data[0] = file.readInt(1);
|
||||
MIDI.track[t-1].event[e-1].data[1] = file.readInt(1);
|
||||
break;
|
||||
case 0xC: // Program Change
|
||||
case 0xD: // Channel Aftertouch
|
||||
MIDI.track[t-1].event[e-1].data = file.readInt(1);
|
||||
break;
|
||||
case -1: // EOF
|
||||
endOfTrack = true; // change FLAG to force track reading loop breaking
|
||||
break;
|
||||
default:
|
||||
// if user provided a custom interpreter, call it
|
||||
// and assign to event the returned data
|
||||
if( this.customInterpreter !== null){
|
||||
MIDI.track[t-1].event[e-1].data = this.customInterpreter( MIDI.track[t-1].event[e-1].metaType, file , false);
|
||||
}
|
||||
|
||||
// if no customInterpretr is provided, or returned
|
||||
// false (=apply default), perform default action
|
||||
if(this.customInterpreter === null || MIDI.track[t-1].event[e-1].data === false){
|
||||
console.log('Unknown EVENT detected... reading cancelled!');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return MIDI;
|
||||
},
|
||||
|
||||
/**
|
||||
* custom function to handle unimplemented, or custom midi messages.
|
||||
* If message is a meta-event, the value of metaEventLength will be >0.
|
||||
* Function must return the value to store, and pointer of dataView needs
|
||||
* to be manually increased
|
||||
* If you want default action to be performed, return false
|
||||
*/
|
||||
customInterpreter : null // function( e_type , arrayByffer, metaEventLength){ return e_data_int }
|
||||
};
|
||||
|
||||
|
||||
// if running in NODE export module
|
||||
if(typeof module !== 'undefined') module.exports = MidiParser;
|
||||
else{
|
||||
// if running in Browser, set a global variable.
|
||||
let _global = typeof window === 'object' && window.self === window && window ||
|
||||
typeof self === 'object' && self.self === self && self ||
|
||||
typeof global === 'object' && global.global === global && global;
|
||||
|
||||
_global.MidiParser = MidiParser;
|
||||
}
|
||||
|
||||
|
||||
|
||||
})();
|
||||
@@ -123,7 +123,7 @@ label {
|
||||
color: #AAF;
|
||||
}
|
||||
|
||||
input,textarea {
|
||||
.header input {
|
||||
color: #EEE;
|
||||
background-color: #555;
|
||||
font-size: 1.2em;
|
||||
@@ -12,7 +12,7 @@ And in ```the src/``` folder there is also another class included:
|
||||
## LGraphNode
|
||||
|
||||
LGraphNode is the base class used for all the nodes classes.
|
||||
To extend the other classes all the methods contained in LGraphNode.prototype are copyed to the classes when registered.
|
||||
To extend the other classes all the methods contained in LGraphNode.prototype are copied to the classes when registered.
|
||||
|
||||
When you create a new node type you do not have to inherit from that class, when the node is registered all the methods are copied to your node prototype. This is done inside the functions ```LiteGraph.registerNodeType(...)```.
|
||||
|
||||
@@ -59,12 +59,15 @@ LiteGraph.registerNodeType("basic/sum", MyAddNode );
|
||||
There are several settings that could be defined or modified per node:
|
||||
* **size**: ```[width,height]``` the size of the area inside the node (excluding title). Every row is LiteGraph.NODE_SLOT_HEIGHT pixels height.
|
||||
* **properties**: object containing the properties that could be configured by the user, and serialized when saving the graph
|
||||
* **shape**: the shape of the object (could be LiteGraph.BOX,LiteGraph.ROUND,LiteGraph.CARD)
|
||||
* **flags**: several flags
|
||||
* **resizable**: if it can be resized dragging the corner
|
||||
* **horizontal**: if the slots should be placed horizontally on the top and bottom of the node
|
||||
* **clip_area**: clips the content when rendering the node
|
||||
* **shape**: the shape of the object (could be LiteGraph.BOX_SHAPE,LiteGraph.ROUND_SHAPE,LiteGraph.CARD_SHAPE)
|
||||
* **flags**: flags that can be changed by the user and will be stored when serialized
|
||||
* **collapsed**: if it is shown collapsed (small)
|
||||
* **redraw_on_mouse**: forces a redraw if the mouse passes over the widget
|
||||
* **widgets_up**: widgets do not start after the slots
|
||||
* **widgets_start_y**: widgets should start being drawn from this Y
|
||||
* **clip_area**: clips the content when rendering the node
|
||||
* **resizable**: if it can be resized dragging the corner
|
||||
* **horizontal**: if the slots should be placed horizontally on the top and bottom of the node
|
||||
|
||||
There are several callbacks that could be defined by the user:
|
||||
* **onAdded**: called when added to graph
|
||||
@@ -102,7 +105,7 @@ Slots have the next information:
|
||||
|
||||
* **name**: string with the name of the slot (used also to show in the canvas)
|
||||
* **type**: string specifying the data type traveling through this link
|
||||
* **link or links**: depending if the slot is input or ouput contains the id of the link or an array of ids
|
||||
* **link or links**: depending if the slot is input or output contains the id of the link or an array of ids
|
||||
* **label**: optional, string used to rename the name as shown in the canvas.
|
||||
* **dir**: optional, could be LiteGraph.UP, LiteGraph.RIGHT, LiteGraph.DOWN, LiteGraph.LEFT
|
||||
* **color_on**: color to render when it is connected
|
||||
@@ -186,7 +189,14 @@ function MyNodeType()
|
||||
This is the list of supported widgets:
|
||||
* **"number"** to change a value of a number, the syntax is ```this.addWidget("number","Number", current_value, callback, { min: 0, max: 100, step: 1} );```
|
||||
* **"slider"** to change a number by draging the mouse, the syntax is the same as number.
|
||||
* **"combo"** to select between multiple choices, the syntax is: ```this.addWidget("combo","Combo", "red", callback, { values:["red","green","blue"]} );```
|
||||
* **"combo"** to select between multiple choices, the syntax is:
|
||||
|
||||
```this.addWidget("combo","Combo", "red", callback, { values:["red","green","blue"]} );```
|
||||
|
||||
or if you want to use objects:
|
||||
|
||||
```this.addWidget("combo","Combo", value1, callback, { values: { "title1":value1, "title2":value2 } );```
|
||||
|
||||
* **"text"** to edit a short string
|
||||
* **"toggle"** like a checkbox
|
||||
* **"button"**
|
||||
@@ -244,3 +254,63 @@ If you want to start the graph then:
|
||||
```js
|
||||
graph.start();
|
||||
```
|
||||
|
||||
## Events
|
||||
|
||||
When we run a step in a graph (using ```graph.runStep()```) every node onExecute method will be called.
|
||||
But sometimes you want that actions are only performed when some trigger is activated, for this situations you can use Events.
|
||||
|
||||
Events allow to trigger executions in nodes only when an event is dispatched from one node.
|
||||
|
||||
To define slots for nodes you must use the type LiteGraph.ACTION for inputs, and LIteGraph.EVENT for outputs:
|
||||
|
||||
```js
|
||||
function MyNode()
|
||||
{
|
||||
this.addInput("play", LiteGraph.ACTION );
|
||||
this.addInput("onFinish", LiteGraph.EVENT );
|
||||
}
|
||||
```
|
||||
|
||||
Now to execute some code when an event is received from an input, you must define the method onAction:
|
||||
|
||||
```js
|
||||
MyNode.prototype.onAction = function(action, data)
|
||||
{
|
||||
if(action == "play")
|
||||
{
|
||||
//do your action...
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
And the last thing is to trigger events when something in your node happens. You could trigger them from inside the onExecute or from any other interaction:
|
||||
|
||||
```js
|
||||
MyNode.prototype.onAction = function(action, data)
|
||||
{
|
||||
if( this.button_was_clicked )
|
||||
this.triggerSlot(0); //triggers event in slot 0
|
||||
}
|
||||
```
|
||||
|
||||
There are some nodes already available to handle events, like delaying, counting, etc.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
Before Width: | Height: | Size: 85 KiB After Width: | Height: | Size: 92 KiB |
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "litegraph.js",
|
||||
"version": "0.7.3",
|
||||
"description": "A graph node editor similar to PD or UDK Blueprints, it works in a HTML5 Canvas and allow to exported graphs to be included in applications.",
|
||||
"version": "0.7.8",
|
||||
"description": "A graph node editor similar to PD or UDK Blueprints. It works in an HTML5 Canvas and allows to export graphs to be included in applications.",
|
||||
"main": "build/litegraph.js",
|
||||
"types": "src/litegraph.d.ts",
|
||||
"directories": {
|
||||
@@ -31,11 +31,11 @@
|
||||
"bugs": {
|
||||
"url": "https://github.com/jagenjo/litegraph.js/issues"
|
||||
},
|
||||
"homepage": "https://github.com/kriffe/litegraph.js#readme",
|
||||
"homepage": "https://github.com/jagenjo/litegraph.js#readme",
|
||||
"devDependencies": {
|
||||
"express": "^4.17.1",
|
||||
"google-closure-compiler": "^20171112.0.0",
|
||||
"grunt": "^1.0.4",
|
||||
"grunt": "^1.1.0",
|
||||
"grunt-cli": "^1.2.0",
|
||||
"grunt-closure-tools": "^1.0.0",
|
||||
"grunt-contrib-concat": "^1.0.1",
|
||||
|
||||
@@ -3,19 +3,17 @@ function Editor(container_id, options) {
|
||||
options = options || {};
|
||||
|
||||
//fill container
|
||||
var html =
|
||||
"<div class='header'><div class='tools tools-left'></div><div class='tools tools-right'></div></div>";
|
||||
html +=
|
||||
"<div class='content'><div class='editor-area'><canvas class='graphcanvas' width='1000' height='500' tabindex=10></canvas></div></div>";
|
||||
html +=
|
||||
"<div class='footer'><div class='tools tools-left'></div><div class='tools tools-right'></div></div>";
|
||||
var html = "<div class='header'><div class='tools tools-left'></div><div class='tools tools-right'></div></div>";
|
||||
html += "<div class='content'><div class='editor-area'><canvas class='graphcanvas' width='1000' height='500' tabindex=10></canvas></div></div>";
|
||||
html += "<div class='footer'><div class='tools tools-left'></div><div class='tools tools-right'></div></div>";
|
||||
|
||||
var root = document.createElement("div");
|
||||
this.root = root;
|
||||
root.className = "litegraph-editor";
|
||||
root.className = "litegraph litegraph-editor";
|
||||
root.innerHTML = html;
|
||||
|
||||
this.tools = root.querySelector(".tools");
|
||||
this.content = root.querySelector(".content");
|
||||
this.footer = root.querySelector(".footer");
|
||||
|
||||
var canvas = root.querySelector(".graphcanvas");
|
||||
@@ -28,6 +26,8 @@ function Editor(container_id, options) {
|
||||
graphcanvas.draw(true);
|
||||
};
|
||||
|
||||
graphcanvas.onDropItem = this.onDropItem.bind(this);
|
||||
|
||||
//add stuff
|
||||
//this.addToolsButton("loadsession_button","Load","imgs/icon-load.png", this.onLoadButton.bind(this), ".tools-left" );
|
||||
//this.addToolsButton("savesession_button","Save","imgs/icon-save.png", this.onSaveButton.bind(this), ".tools-left" );
|
||||
@@ -104,56 +104,31 @@ Editor.prototype.addLoadCounter = function() {
|
||||
}, 200);
|
||||
};
|
||||
|
||||
Editor.prototype.addToolsButton = function(
|
||||
id,
|
||||
name,
|
||||
icon_url,
|
||||
callback,
|
||||
container
|
||||
) {
|
||||
Editor.prototype.addToolsButton = function( id, name, icon_url, callback, container ) {
|
||||
if (!container) {
|
||||
container = ".tools";
|
||||
}
|
||||
|
||||
var button = this.createButton(name, icon_url);
|
||||
var button = this.createButton(name, icon_url, callback);
|
||||
button.id = id;
|
||||
button.addEventListener("click", callback);
|
||||
|
||||
this.root.querySelector(container).appendChild(button);
|
||||
};
|
||||
|
||||
Editor.prototype.createPanel = function(title, options) {
|
||||
var root = document.createElement("div");
|
||||
root.className = "dialog";
|
||||
root.innerHTML =
|
||||
"<div class='dialog-header'><span class='dialog-title'>" +
|
||||
title +
|
||||
"</span></div><div class='dialog-content'></div><div class='dialog-footer'></div>";
|
||||
root.header = root.querySelector(".dialog-header");
|
||||
root.content = root.querySelector(".dialog-content");
|
||||
root.footer = root.querySelector(".dialog-footer");
|
||||
|
||||
return root;
|
||||
};
|
||||
|
||||
Editor.prototype.createButton = function(name, icon_url) {
|
||||
Editor.prototype.createButton = function(name, icon_url, callback) {
|
||||
var button = document.createElement("button");
|
||||
if (icon_url) {
|
||||
button.innerHTML = "<img src='" + icon_url + "'/> ";
|
||||
}
|
||||
button.classList.add("btn");
|
||||
button.innerHTML += name;
|
||||
if(callback)
|
||||
button.addEventListener("click", callback );
|
||||
return button;
|
||||
};
|
||||
|
||||
Editor.prototype.onLoadButton = function() {
|
||||
var panel = this.createPanel("Load session");
|
||||
var close = this.createButton("Close");
|
||||
close.style.float = "right";
|
||||
close.addEventListener("click", function() {
|
||||
panel.parentNode.removeChild(panel);
|
||||
});
|
||||
panel.header.appendChild(close);
|
||||
panel.content.innerHTML = "test";
|
||||
var panel = this.graphcanvas.createPanel("Load session",{closable:true});
|
||||
//TO DO
|
||||
|
||||
this.root.appendChild(panel);
|
||||
};
|
||||
@@ -192,6 +167,25 @@ Editor.prototype.onLiveButton = function() {
|
||||
: "<img src='imgs/icon-gear.png'/> Edit";
|
||||
};
|
||||
|
||||
Editor.prototype.onDropItem = function(e)
|
||||
{
|
||||
var that = this;
|
||||
for(var i = 0; i < e.dataTransfer.files.length; ++i)
|
||||
{
|
||||
var file = e.dataTransfer.files[i];
|
||||
var ext = LGraphCanvas.getFileExtension(file.name);
|
||||
var reader = new FileReader();
|
||||
if(ext == "json")
|
||||
{
|
||||
reader.onload = function(event) {
|
||||
var data = JSON.parse( event.target.result );
|
||||
that.graph.configure(data);
|
||||
};
|
||||
reader.readAsText(file);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Editor.prototype.goFullscreen = function() {
|
||||
if (this.root.requestFullscreen) {
|
||||
this.root.requestFullscreen(Element.ALLOW_KEYBOARD_INPUT);
|
||||
@@ -225,7 +219,7 @@ Editor.prototype.addMiniWindow = function(w, h) {
|
||||
var canvas = miniwindow.querySelector("canvas");
|
||||
var that = this;
|
||||
|
||||
var graphcanvas = new LGraphCanvas(canvas, this.graph);
|
||||
var graphcanvas = new LGraphCanvas( canvas, this.graph );
|
||||
graphcanvas.show_info = false;
|
||||
graphcanvas.background_image = "imgs/grid.png";
|
||||
graphcanvas.scale = 0.25;
|
||||
@@ -263,7 +257,7 @@ Editor.prototype.addMiniWindow = function(w, h) {
|
||||
|
||||
var close_button = document.createElement("div");
|
||||
close_button.className = "corner-button";
|
||||
close_button.innerHTML = "X";
|
||||
close_button.innerHTML = "❌";
|
||||
close_button.addEventListener("click", function(e) {
|
||||
graphcanvas.setGraph(null);
|
||||
miniwindow.parentNode.removeChild(miniwindow);
|
||||
|
||||
108
src/litegraph.d.ts
vendored
@@ -11,11 +11,17 @@ export type widgetTypes =
|
||||
| "text"
|
||||
| "toggle"
|
||||
| "button";
|
||||
export type SlotShape =
|
||||
| typeof LiteGraph.BOX_SHAPE
|
||||
| typeof LiteGraph.CIRCLE_SHAPE
|
||||
| typeof LiteGraph.ARROW_SHAPE
|
||||
| typeof LiteGraph.SQUARE_SHAPE
|
||||
| number; // For custom shapes
|
||||
|
||||
/** https://github.com/jagenjo/litegraph.js/tree/master/guides#node-slots */
|
||||
export interface INodeSlot {
|
||||
name: string;
|
||||
type: string;
|
||||
type: string | -1;
|
||||
label?: string;
|
||||
dir?:
|
||||
| typeof LiteGraph.UP
|
||||
@@ -24,6 +30,7 @@ export interface INodeSlot {
|
||||
| typeof LiteGraph.LEFT;
|
||||
color_on?: string;
|
||||
color_off?: string;
|
||||
shape?: SlotShape;
|
||||
locked?: boolean;
|
||||
nameLocked?: boolean;
|
||||
}
|
||||
@@ -68,11 +75,12 @@ export interface IWidget<TValue = any, TOptions = any> {
|
||||
* https://github.com/jagenjo/litegraph.js/issues/76
|
||||
*/
|
||||
mouse?(
|
||||
ctx: undefined,
|
||||
event: MouseEvent,
|
||||
pos: Vector2,
|
||||
node: LGraphNode
|
||||
): void;
|
||||
): boolean;
|
||||
/** Called by `LGraphNode.computeSize` */
|
||||
computeSize?(width: number): [number, number];
|
||||
}
|
||||
export interface IButtonWidget extends IWidget<null, {}> {
|
||||
type: "button";
|
||||
@@ -173,6 +181,7 @@ export const LiteGraph: {
|
||||
CIRCLE_SHAPE: 3;
|
||||
CARD_SHAPE: 4;
|
||||
ARROW_SHAPE: 5;
|
||||
SQUARE_SHAPE: 6;
|
||||
|
||||
//enums
|
||||
INPUT: 1;
|
||||
@@ -228,6 +237,10 @@ export const LiteGraph: {
|
||||
createNode<T extends LGraphNode = LGraphNode>(type: string): T;
|
||||
/** Register a node class so it can be listed when the user wants to create a new one */
|
||||
registerNodeType(type: string, base: { new (): LGraphNode }): void;
|
||||
/** removes a node type from the system */
|
||||
unregisterNodeType(type: string): void;
|
||||
/** Removes all previously registered node's types. */
|
||||
clearRegisteredTypes(): void;
|
||||
/**
|
||||
* Create a new node type 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.
|
||||
@@ -240,7 +253,7 @@ export const LiteGraph: {
|
||||
wrapFunctionAsNode(
|
||||
name: string,
|
||||
func: (...args: any[]) => any,
|
||||
param_types?: [],
|
||||
param_types?: string[],
|
||||
return_type?: string,
|
||||
properties?: object
|
||||
): void;
|
||||
@@ -431,6 +444,11 @@ export declare class LGraph {
|
||||
* @param node the instance of the node
|
||||
*/
|
||||
add(node: LGraphNode, skip_compute_order?: boolean): void;
|
||||
/**
|
||||
* Called when a new node is added
|
||||
* @param node the instance of the node
|
||||
*/
|
||||
onNodeAdded(node: LGraphNode): void;
|
||||
/** Removes a node from the graph */
|
||||
remove(node: LGraphNode): void;
|
||||
/** Returns a node by its id. */
|
||||
@@ -593,7 +611,9 @@ export declare class LGraphNode {
|
||||
properties: Record<string, any>;
|
||||
properties_info: any[];
|
||||
|
||||
flags: object;
|
||||
flags: Partial<{
|
||||
collapsed: boolean
|
||||
}>;
|
||||
|
||||
color: string;
|
||||
bgcolor: string;
|
||||
@@ -615,6 +635,17 @@ export declare class LGraphNode {
|
||||
| typeof LiteGraph.NEVER
|
||||
| typeof LiteGraph.ALWAYS;
|
||||
|
||||
/** If set to true widgets do not start after the slots */
|
||||
widgets_up: boolean;
|
||||
/** widgets start at y distance from the top of the node */
|
||||
widgets_start_y: number;
|
||||
/** if you render outside the node, it will be clipped */
|
||||
clip_area: boolean;
|
||||
/** if set to false it wont be resizable with the mouse */
|
||||
resizable: boolean;
|
||||
/** slots are distributed horizontally */
|
||||
horizontal: boolean;
|
||||
|
||||
/** configure a node from an object containing the serialized info */
|
||||
configure(info: SerializedLGraphNode): void;
|
||||
/** serialize the content */
|
||||
@@ -707,7 +738,7 @@ export declare class LGraphNode {
|
||||
*/
|
||||
addOutput(
|
||||
name: string,
|
||||
type: string,
|
||||
type: string | -1,
|
||||
extra_info?: Partial<INodeOutputSlot>
|
||||
): void;
|
||||
/**
|
||||
@@ -715,7 +746,7 @@ export declare class LGraphNode {
|
||||
* @param array of triplets like [[name,type,extra_info],[...]]
|
||||
*/
|
||||
addOutputs(
|
||||
array: [string, string, Partial<INodeOutputSlot> | undefined][]
|
||||
array: [string, string | -1, Partial<INodeOutputSlot> | undefined][]
|
||||
): void;
|
||||
/** remove an existing output slot */
|
||||
removeOutput(slot: number): void;
|
||||
@@ -727,7 +758,7 @@ export declare class LGraphNode {
|
||||
*/
|
||||
addInput(
|
||||
name: string,
|
||||
type: string,
|
||||
type: string | -1,
|
||||
extra_info?: Partial<INodeInputSlot>
|
||||
): void;
|
||||
/**
|
||||
@@ -735,7 +766,7 @@ export declare class LGraphNode {
|
||||
* @param array of triplets like [[name,type,extra_info],[...]]
|
||||
*/
|
||||
addInputs(
|
||||
array: [string, string, Partial<INodeInputSlot> | undefined][]
|
||||
array: [string, string | -1, Partial<INodeInputSlot> | undefined][]
|
||||
): void;
|
||||
/** remove an existing input slot */
|
||||
removeInput(slot: number): void;
|
||||
@@ -769,7 +800,7 @@ export declare class LGraphNode {
|
||||
type: T["type"],
|
||||
name: string,
|
||||
value: T["value"],
|
||||
callback?: WidgetCallback<T>,
|
||||
callback?: WidgetCallback<T> | string,
|
||||
options?: T["options"]
|
||||
): T;
|
||||
|
||||
@@ -863,12 +894,12 @@ export declare class LGraphNode {
|
||||
|
||||
// https://github.com/jagenjo/litegraph.js/blob/master/guides/README.md#custom-node-appearance
|
||||
onDrawBackground?(
|
||||
canvas: HTMLCanvasElement,
|
||||
ctx: CanvasRenderingContext2D
|
||||
ctx: CanvasRenderingContext2D,
|
||||
canvas: HTMLCanvasElement
|
||||
): void;
|
||||
onDrawForeground?(
|
||||
canvas: HTMLCanvasElement,
|
||||
ctx: CanvasRenderingContext2D
|
||||
ctx: CanvasRenderingContext2D,
|
||||
canvas: HTMLCanvasElement
|
||||
): void;
|
||||
|
||||
// https://github.com/jagenjo/litegraph.js/blob/master/guides/README.md#custom-node-behaviour
|
||||
@@ -926,10 +957,34 @@ export declare class LGraphNode {
|
||||
onConnectInput?(
|
||||
inputIndex: number,
|
||||
type: INodeOutputSlot["type"],
|
||||
outputSlot: INodeOutputSlot
|
||||
outputSlot: INodeOutputSlot,
|
||||
_this: this,
|
||||
slotIndex: number
|
||||
): boolean;
|
||||
|
||||
/**
|
||||
* Called just before connection (or disconnect - if input is linked).
|
||||
* A convenient place to switch to another input, or create new one.
|
||||
* This allow for ability to automatically add slots if needed
|
||||
* @param inputIndex
|
||||
* @return selected input slot index, can differ from parameter value
|
||||
*/
|
||||
onBeforeConnectInput?(
|
||||
inputIndex: number
|
||||
): number;
|
||||
|
||||
/** a connection changed (new one or removed) (LiteGraph.INPUT or LiteGraph.OUTPUT, slot, true if connected, link_info, input_info or output_info ) */
|
||||
onConnectionsChange(
|
||||
type: number,
|
||||
slotIndex: number,
|
||||
isConnected: boolean,
|
||||
link: LLink,
|
||||
ioSlot: (INodeOutputSlot | INodeInputSlot)
|
||||
): void;
|
||||
|
||||
/** Called by `LGraphCanvas.processContextMenu` */
|
||||
getMenuOptions?(graphCanvas: LGraphCanvas): ContextMenuItem[];
|
||||
getSlotMenuOptions?(slot: INodeSlot): ContextMenuItem[];
|
||||
}
|
||||
|
||||
export type LGraphNodeConstructor<T extends LGraphNode = LGraphNode> = {
|
||||
@@ -1017,6 +1072,7 @@ export declare class LGraphCanvas {
|
||||
/** Create menu for `Add Node` */
|
||||
static onMenuAdd: ContextMenuEventListener;
|
||||
static showMenuNodeOptionalInputs: ContextMenuEventListener;
|
||||
static showMenuNodeOptionalOutputs: ContextMenuEventListener;
|
||||
static onShowMenuNodeProperties: ContextMenuEventListener;
|
||||
static onResizeNode: ContextMenuEventListener;
|
||||
static onMenuNodeCollapse: ContextMenuEventListener;
|
||||
@@ -1089,7 +1145,7 @@ export declare class LGraphCanvas {
|
||||
last_mouse_position: Vector2;
|
||||
/** Timestamp of last mouse click, defaults to 0 */
|
||||
last_mouseclick: number;
|
||||
link_render_mode:
|
||||
links_render_mode:
|
||||
| typeof LiteGraph.STRAIGHT_LINK
|
||||
| typeof LiteGraph.LINEAR_LINK
|
||||
| typeof LiteGraph.SPLINE_LINK;
|
||||
@@ -1111,6 +1167,20 @@ export declare class LGraphCanvas {
|
||||
onDrawOverlay: ((ctx: CanvasRenderingContext2D) => void) | null;
|
||||
/** Called by `LGraphCanvas.processMouseDown` */
|
||||
onMouse: ((event: MouseEvent) => boolean) | null;
|
||||
/** Called by `LGraphCanvas.drawFrontCanvas` and `LGraphCanvas.drawLinkTooltip` */
|
||||
onDrawLinkTooltip: ((ctx: CanvasRenderingContext2D, link: LLink, _this: this) => void) | null;
|
||||
/** Called by `LGraphCanvas.selectNodes` */
|
||||
onNodeMoved: ((node: LGraphNode) => void) | null;
|
||||
/** Called by `LGraphCanvas.processNodeSelected` */
|
||||
onNodeSelected: ((node: LGraphNode) => void) | null;
|
||||
/** Called by `LGraphCanvas.deselectNode` */
|
||||
onNodeDeselected: ((node: LGraphNode) => void) | null;
|
||||
/** Called by `LGraphCanvas.processNodeDblClicked` */
|
||||
onShowNodePanel: ((node: LGraphNode) => void) | null;
|
||||
/** Called by `LGraphCanvas.processNodeDblClicked` */
|
||||
onNodeDblClicked: ((node: LGraphNode) => void) | null;
|
||||
/** Called by `LGraphCanvas.selectNodes` */
|
||||
onSelectionChange: ((nodes: Record<number, LGraphNode>) => void) | null;
|
||||
/** Called by `LGraphCanvas.showSearchBox` */
|
||||
onSearchBox:
|
||||
| ((
|
||||
@@ -1213,7 +1283,7 @@ export declare class LGraphCanvas {
|
||||
/** selects a given node (or adds it to the current selection) */
|
||||
selectNode(node: LGraphNode, add?: boolean): void;
|
||||
/** selects several nodes (or adds them to the current selection) */
|
||||
selectNodes(nodes: LGraphNode[], add?: boolean): void;
|
||||
selectNodes(nodes?: LGraphNode[], add?: boolean): void;
|
||||
/** removes a node from the current selection */
|
||||
deselectNode(node: LGraphNode): void;
|
||||
/** removes all nodes from the current selection */
|
||||
@@ -1236,11 +1306,13 @@ export declare class LGraphCanvas {
|
||||
/** draws the front canvas (the one containing all the nodes) */
|
||||
drawFrontCanvas(): void;
|
||||
/** draws some useful stats in the corner of the canvas */
|
||||
renderInfo(): void;
|
||||
renderInfo(ctx: CanvasRenderingContext2D, x: number, y: number): void;
|
||||
/** draws the back canvas (the one containing the background and the connections) */
|
||||
drawBackCanvas(): void;
|
||||
/** draws the given node inside the canvas */
|
||||
drawNode(node: LGraphNode, ctx: CanvasRenderingContext2D): void;
|
||||
/** draws graphic for node's slot */
|
||||
drawSlotGraphic(ctx: CanvasRenderingContext2D, pos: number[], shape: SlotShape, horizontal: boolean): void;
|
||||
/** draws the shape of the given node in the canvas */
|
||||
drawNodeShape(
|
||||
node: LGraphNode,
|
||||
|
||||
1875
src/litegraph.js
@@ -275,6 +275,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
LGAudioSource.desc = "Plays an audio file";
|
||||
LGAudioSource["@src"] = { widget: "resource" };
|
||||
LGAudioSource.supported_extensions = ["wav", "ogg", "mp3"];
|
||||
|
||||
@@ -290,7 +291,7 @@
|
||||
}
|
||||
|
||||
if (this.properties.autoplay) {
|
||||
this.playBuffer(this._audiobuffer);
|
||||
this.playBuffer(this._audiobuffer);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -345,8 +346,10 @@
|
||||
if (v === undefined) {
|
||||
continue;
|
||||
}
|
||||
if (input.name == "gain") {
|
||||
if (input.name == "gain")
|
||||
this.audionode.gain.value = v;
|
||||
else if (input.name == "src") {
|
||||
this.setProperty("src",v);
|
||||
} else if (input.name == "playbackRate") {
|
||||
this.properties.playbackRate = v;
|
||||
for (var j = 0; j < this._audionodes.length; ++j) {
|
||||
@@ -401,7 +404,10 @@
|
||||
audionode.playbackRate.value = this.properties.playbackRate;
|
||||
this._audionodes.push(audionode);
|
||||
audionode.connect(this.audionode); //connect to gain
|
||||
this._audionodes.push(audionode);
|
||||
|
||||
this._audionodes.push(audionode);
|
||||
|
||||
this.trigger("start");
|
||||
|
||||
audionode.onended = function() {
|
||||
//console.log("ended!");
|
||||
@@ -458,13 +464,14 @@
|
||||
LGAudioSource.prototype.onGetInputs = function() {
|
||||
return [
|
||||
["playbackRate", "number"],
|
||||
["src","string"],
|
||||
["Play", LiteGraph.ACTION],
|
||||
["Stop", LiteGraph.ACTION]
|
||||
];
|
||||
};
|
||||
|
||||
LGAudioSource.prototype.onGetOutputs = function() {
|
||||
return [["buffer", "audiobuffer"], ["ended", LiteGraph.EVENT]];
|
||||
return [["buffer", "audiobuffer"], ["start", LiteGraph.EVENT], ["ended", LiteGraph.EVENT]];
|
||||
};
|
||||
|
||||
LGAudioSource.prototype.onDropFile = function(file) {
|
||||
@@ -1357,7 +1364,7 @@ LiteGraph.registerNodeType("audio/waveShaper", LGAudioWaveShaper);
|
||||
}
|
||||
};
|
||||
|
||||
LGAudioScript["@code"] = { widget: "code" };
|
||||
LGAudioScript["@code"] = { widget: "code", type: "code" };
|
||||
|
||||
LGAudioScript.prototype.onStart = function() {
|
||||
this.audionode.onaudioprocess = this._callback;
|
||||
|
||||
@@ -32,18 +32,15 @@
|
||||
|
||||
this.subgraph.onTrigger = this.onSubgraphTrigger.bind(this);
|
||||
|
||||
//nodes input node added inside
|
||||
this.subgraph.onInputAdded = this.onSubgraphNewInput.bind(this);
|
||||
this.subgraph.onInputRenamed = this.onSubgraphRenamedInput.bind(this);
|
||||
this.subgraph.onInputTypeChanged = this.onSubgraphTypeChangeInput.bind(
|
||||
this
|
||||
);
|
||||
this.subgraph.onInputTypeChanged = this.onSubgraphTypeChangeInput.bind(this);
|
||||
this.subgraph.onInputRemoved = this.onSubgraphRemovedInput.bind(this);
|
||||
|
||||
this.subgraph.onOutputAdded = this.onSubgraphNewOutput.bind(this);
|
||||
this.subgraph.onOutputRenamed = this.onSubgraphRenamedOutput.bind(this);
|
||||
this.subgraph.onOutputTypeChanged = this.onSubgraphTypeChangeOutput.bind(
|
||||
this
|
||||
);
|
||||
this.subgraph.onOutputTypeChanged = this.onSubgraphTypeChangeOutput.bind(this);
|
||||
this.subgraph.onOutputRemoved = this.onSubgraphRemovedOutput.bind(this);
|
||||
}
|
||||
|
||||
@@ -55,6 +52,7 @@
|
||||
return [["enabled", "boolean"]];
|
||||
};
|
||||
|
||||
/*
|
||||
Subgraph.prototype.onDrawTitle = function(ctx) {
|
||||
if (this.flags.collapsed) {
|
||||
return;
|
||||
@@ -71,6 +69,7 @@
|
||||
ctx.lineTo(x + w * 0.5, -w * 0.3);
|
||||
ctx.fill();
|
||||
};
|
||||
*/
|
||||
|
||||
Subgraph.prototype.onDblClick = function(e, pos, graphcanvas) {
|
||||
var that = this;
|
||||
@@ -79,6 +78,7 @@
|
||||
}, 10);
|
||||
};
|
||||
|
||||
/*
|
||||
Subgraph.prototype.onMouseDown = function(e, pos, graphcanvas) {
|
||||
if (
|
||||
!this.flags.collapsed &&
|
||||
@@ -91,6 +91,7 @@
|
||||
}, 10);
|
||||
}
|
||||
};
|
||||
*/
|
||||
|
||||
Subgraph.prototype.onAction = function(action, param) {
|
||||
this.subgraph.onAction(action, param);
|
||||
@@ -130,6 +131,46 @@
|
||||
}
|
||||
};
|
||||
|
||||
Subgraph.prototype.onDrawBackground = function(ctx, graphcanvas, canvas, pos)
|
||||
{
|
||||
if(this.flags.collapsed)
|
||||
return;
|
||||
|
||||
var y = this.size[1] - LiteGraph.NODE_TITLE_HEIGHT + 0.5;
|
||||
|
||||
//button
|
||||
var over = LiteGraph.isInsideRectangle(pos[0],pos[1],this.pos[0],this.pos[1] + y,this.size[0],LiteGraph.NODE_TITLE_HEIGHT);
|
||||
ctx.fillStyle = over ? "#555" : "#222";
|
||||
ctx.beginPath();
|
||||
if (this._shape == LiteGraph.BOX_SHAPE)
|
||||
ctx.rect(0, y, this.size[0]+1, LiteGraph.NODE_TITLE_HEIGHT);
|
||||
else
|
||||
ctx.roundRect( 0, y, this.size[0]+1, LiteGraph.NODE_TITLE_HEIGHT, 0, 8);
|
||||
ctx.fill();
|
||||
|
||||
//button
|
||||
ctx.textAlign = "center";
|
||||
ctx.font = "24px Arial";
|
||||
ctx.fillStyle = over ? "#DDD" : "#999";
|
||||
ctx.fillText( "+", this.size[0] * 0.5, y + 24 );
|
||||
}
|
||||
|
||||
Subgraph.prototype.onMouseDown = function(e, localpos, graphcanvas)
|
||||
{
|
||||
var y = this.size[1] - LiteGraph.NODE_TITLE_HEIGHT + 0.5;
|
||||
if(localpos[1] > y)
|
||||
{
|
||||
graphcanvas.showSubgraphPropertiesDialog(this);
|
||||
}
|
||||
}
|
||||
|
||||
Subgraph.prototype.computeSize = function()
|
||||
{
|
||||
var num_inputs = this.inputs ? this.inputs.length : 0;
|
||||
var num_outputs = this.outputs ? this.outputs.length : 0;
|
||||
return [ 200, Math.max(num_inputs,num_outputs) * LiteGraph.NODE_SLOT_HEIGHT + LiteGraph.NODE_TITLE_HEIGHT ];
|
||||
}
|
||||
|
||||
//**** INPUTS ***********************************
|
||||
Subgraph.prototype.onSubgraphTrigger = function(event, param) {
|
||||
var slot = this.findOutputSlot(event);
|
||||
@@ -224,7 +265,7 @@
|
||||
};
|
||||
|
||||
Subgraph.prototype.serialize = function() {
|
||||
var data = LGraphNode.prototype.serialize.call(this);
|
||||
var data = LiteGraph.LGraphNode.prototype.serialize.call(this);
|
||||
data.subgraph = this.subgraph.serialize();
|
||||
return data;
|
||||
};
|
||||
@@ -240,6 +281,95 @@
|
||||
return node;
|
||||
};
|
||||
|
||||
Subgraph.prototype.buildFromNodes = function(nodes)
|
||||
{
|
||||
//clear all?
|
||||
//TODO
|
||||
|
||||
//nodes that connect data between parent graph and subgraph
|
||||
var subgraph_inputs = [];
|
||||
var subgraph_outputs = [];
|
||||
|
||||
//mark inner nodes
|
||||
var ids = {};
|
||||
var min_x = 0;
|
||||
var max_x = 0;
|
||||
for(var i = 0; i < nodes.length; ++i)
|
||||
{
|
||||
var node = nodes[i];
|
||||
ids[ node.id ] = node;
|
||||
min_x = Math.min( node.pos[0], min_x );
|
||||
max_x = Math.max( node.pos[0], min_x );
|
||||
}
|
||||
|
||||
var last_input_y = 0;
|
||||
var last_output_y = 0;
|
||||
|
||||
for(var i = 0; i < nodes.length; ++i)
|
||||
{
|
||||
var node = nodes[i];
|
||||
//check inputs
|
||||
if( node.inputs )
|
||||
for(var j = 0; j < node.inputs.length; ++j)
|
||||
{
|
||||
var input = node.inputs[j];
|
||||
if( !input || !input.link )
|
||||
continue;
|
||||
var link = node.graph.links[ input.link ];
|
||||
if(!link)
|
||||
continue;
|
||||
if( ids[ link.origin_id ] )
|
||||
continue;
|
||||
//this.addInput(input.name,link.type);
|
||||
this.subgraph.addInput(input.name,link.type);
|
||||
/*
|
||||
var input_node = LiteGraph.createNode("graph/input");
|
||||
this.subgraph.add( input_node );
|
||||
input_node.pos = [min_x - 200, last_input_y ];
|
||||
last_input_y += 100;
|
||||
*/
|
||||
}
|
||||
|
||||
//check outputs
|
||||
if( node.outputs )
|
||||
for(var j = 0; j < node.outputs.length; ++j)
|
||||
{
|
||||
var output = node.outputs[j];
|
||||
if( !output || !output.links || !output.links.length )
|
||||
continue;
|
||||
var is_external = false;
|
||||
for(var k = 0; k < output.links.length; ++k)
|
||||
{
|
||||
var link = node.graph.links[ output.links[k] ];
|
||||
if(!link)
|
||||
continue;
|
||||
if( ids[ link.target_id ] )
|
||||
continue;
|
||||
is_external = true;
|
||||
break;
|
||||
}
|
||||
if(!is_external)
|
||||
continue;
|
||||
//this.addOutput(output.name,output.type);
|
||||
/*
|
||||
var output_node = LiteGraph.createNode("graph/output");
|
||||
this.subgraph.add( output_node );
|
||||
output_node.pos = [max_x + 50, last_output_y ];
|
||||
last_output_y += 100;
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
//detect inputs and outputs
|
||||
//split every connection in two data_connection nodes
|
||||
//keep track of internal connections
|
||||
//connect external connections
|
||||
|
||||
//clone nodes inside subgraph and try to reconnect them
|
||||
|
||||
//connect edge subgraph nodes to extarnal connections nodes
|
||||
}
|
||||
|
||||
LiteGraph.Subgraph = Subgraph;
|
||||
LiteGraph.registerNodeType("graph/subgraph", Subgraph);
|
||||
|
||||
@@ -297,16 +427,27 @@
|
||||
this.updateType();
|
||||
}
|
||||
|
||||
//ensures the type in the node output and the type in the associated graph input are the same
|
||||
GraphInput.prototype.updateType = function()
|
||||
{
|
||||
var type = this.properties.type;
|
||||
this.type_widget.value = type;
|
||||
|
||||
//update output
|
||||
if(this.outputs[0].type != type)
|
||||
{
|
||||
if (!LiteGraph.isValidConnection(this.outputs[0].type,type))
|
||||
this.disconnectOutput(0);
|
||||
this.outputs[0].type = type;
|
||||
}
|
||||
|
||||
//update widget
|
||||
if(type == "number")
|
||||
{
|
||||
this.value_widget.type = "number";
|
||||
this.value_widget.value = 0;
|
||||
}
|
||||
else if(type == "bool")
|
||||
else if(type == "boolean")
|
||||
{
|
||||
this.value_widget.type = "toggle";
|
||||
this.value_widget.value = true;
|
||||
@@ -322,8 +463,14 @@
|
||||
this.value_widget.value = null;
|
||||
}
|
||||
this.properties.value = this.value_widget.value;
|
||||
|
||||
//update graph
|
||||
if (this.graph && this.name_in_graph) {
|
||||
this.graph.changeInputType(this.name_in_graph, type);
|
||||
}
|
||||
}
|
||||
|
||||
//this is executed AFTER the property has changed
|
||||
GraphInput.prototype.onPropertyChanged = function(name,v)
|
||||
{
|
||||
if( name == "name" )
|
||||
@@ -345,8 +492,7 @@
|
||||
}
|
||||
else if( name == "type" )
|
||||
{
|
||||
v = v || "";
|
||||
this.updateType(v);
|
||||
this.updateType();
|
||||
}
|
||||
else if( name == "value" )
|
||||
{
|
||||
@@ -372,8 +518,10 @@
|
||||
var data = this.graph.inputs[name];
|
||||
if (!data) {
|
||||
this.setOutputData(0, this.properties.value );
|
||||
return;
|
||||
}
|
||||
this.setOutputData(0, data.value === undefined ? this.properties.value : data.value);
|
||||
|
||||
this.setOutputData(0, data.value !== undefined ? data.value : this.properties.value );
|
||||
};
|
||||
|
||||
GraphInput.prototype.onRemoved = function() {
|
||||
@@ -421,6 +569,8 @@
|
||||
if (v == "action" || v == "event") {
|
||||
v = LiteGraph.ACTION;
|
||||
}
|
||||
if (!LiteGraph.isValidConnection(that.inputs[0].type,v))
|
||||
that.disconnectInput(0);
|
||||
that.inputs[0].type = v;
|
||||
if (that.name_in_graph) {
|
||||
//already added
|
||||
@@ -434,27 +584,8 @@
|
||||
enumerable: true
|
||||
});
|
||||
|
||||
this.name_widget = this.addWidget(
|
||||
"text",
|
||||
"Name",
|
||||
this.properties.name,
|
||||
function(v) {
|
||||
if (!v) {
|
||||
return;
|
||||
}
|
||||
that.properties.name = v;
|
||||
}
|
||||
);
|
||||
this.type_widget = this.addWidget(
|
||||
"text",
|
||||
"Type",
|
||||
this.properties.type,
|
||||
function(v) {
|
||||
v = v || "";
|
||||
that.properties.type = v;
|
||||
}
|
||||
);
|
||||
|
||||
this.name_widget = this.addWidget("text","Name",this.properties.name,"name");
|
||||
this.type_widget = this.addWidget("text","Type",this.properties.type,"type");
|
||||
this.widgets_up = true;
|
||||
this.size = [180, 60];
|
||||
}
|
||||
@@ -493,6 +624,9 @@
|
||||
function ConstantNumber() {
|
||||
this.addOutput("value", "number");
|
||||
this.addProperty("value", 1.0);
|
||||
this.widget = this.addWidget("number","value",1,"value");
|
||||
this.widgets_up = true;
|
||||
this.size = [180, 30];
|
||||
}
|
||||
|
||||
ConstantNumber.title = "Const Number";
|
||||
@@ -509,9 +643,10 @@
|
||||
return this.title;
|
||||
};
|
||||
|
||||
ConstantNumber.prototype.setValue = function(v) {
|
||||
this.properties.value = v;
|
||||
};
|
||||
ConstantNumber.prototype.setValue = function(v)
|
||||
{
|
||||
this.setProperty("value",v);
|
||||
}
|
||||
|
||||
ConstantNumber.prototype.onDrawBackground = function(ctx) {
|
||||
//show the current value
|
||||
@@ -520,47 +655,170 @@
|
||||
|
||||
LiteGraph.registerNodeType("basic/const", ConstantNumber);
|
||||
|
||||
function ConstantBoolean() {
|
||||
this.addOutput("", "boolean");
|
||||
this.addProperty("value", true);
|
||||
this.widget = this.addWidget("toggle","value",true,"value");
|
||||
this.widgets_up = true;
|
||||
this.size = [140, 30];
|
||||
}
|
||||
|
||||
ConstantBoolean.title = "Const Boolean";
|
||||
ConstantBoolean.desc = "Constant boolean";
|
||||
ConstantBoolean.prototype.getTitle = ConstantNumber.prototype.getTitle;
|
||||
|
||||
ConstantBoolean.prototype.onExecute = function() {
|
||||
this.setOutputData(0, this.properties["value"]);
|
||||
};
|
||||
|
||||
ConstantBoolean.prototype.setValue = ConstantNumber.prototype.setValue;
|
||||
|
||||
ConstantBoolean.prototype.onGetInputs = function() {
|
||||
return [["toggle", LiteGraph.ACTION]];
|
||||
};
|
||||
|
||||
ConstantBoolean.prototype.onAction = function(action)
|
||||
{
|
||||
this.setValue( !this.properties.value );
|
||||
}
|
||||
|
||||
LiteGraph.registerNodeType("basic/boolean", ConstantBoolean);
|
||||
|
||||
function ConstantString() {
|
||||
this.addOutput("", "string");
|
||||
this.addProperty("value", "");
|
||||
this.widget = this.addWidget(
|
||||
"text",
|
||||
"value",
|
||||
"",
|
||||
this.setValue.bind(this)
|
||||
);
|
||||
this.widget = this.addWidget("text","value","","value"); //link to property value
|
||||
this.widgets_up = true;
|
||||
this.size = [100, 30];
|
||||
this.size = [180, 30];
|
||||
}
|
||||
|
||||
ConstantString.title = "Const String";
|
||||
ConstantString.desc = "Constant string";
|
||||
|
||||
ConstantString.prototype.setValue = function(v) {
|
||||
this.properties.value = v;
|
||||
};
|
||||
|
||||
ConstantString.prototype.onPropertyChanged = function(name, value) {
|
||||
this.widget.value = value;
|
||||
};
|
||||
|
||||
ConstantString.prototype.getTitle = ConstantNumber.prototype.getTitle;
|
||||
|
||||
ConstantString.prototype.onExecute = function() {
|
||||
this.setOutputData(0, this.properties["value"]);
|
||||
};
|
||||
|
||||
ConstantString.prototype.setValue = ConstantNumber.prototype.setValue;
|
||||
|
||||
ConstantString.prototype.onDropFile = function(file)
|
||||
{
|
||||
var that = this;
|
||||
var reader = new FileReader();
|
||||
reader.onload = function(e)
|
||||
{
|
||||
that.setProperty("value",e.target.result);
|
||||
}
|
||||
reader.readAsText(file);
|
||||
}
|
||||
|
||||
LiteGraph.registerNodeType("basic/string", ConstantString);
|
||||
|
||||
function ConstantFile() {
|
||||
this.addInput("url", "");
|
||||
this.addOutput("", "");
|
||||
this.addProperty("url", "");
|
||||
this.addProperty("type", "text");
|
||||
this.widget = this.addWidget("text","url","","url");
|
||||
this._data = null;
|
||||
}
|
||||
|
||||
ConstantFile.title = "Const File";
|
||||
ConstantFile.desc = "Fetches a file from an url";
|
||||
ConstantFile["@type"] = { type: "enum", values: ["text","arraybuffer","blob","json"] };
|
||||
|
||||
ConstantFile.prototype.onPropertyChanged = function(name, value) {
|
||||
if (name == "url")
|
||||
{
|
||||
if( value == null || value == "")
|
||||
this._data = null;
|
||||
else
|
||||
{
|
||||
this.fetchFile(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ConstantFile.prototype.onExecute = function() {
|
||||
var url = this.getInputData(0) || this.properties.url;
|
||||
if(url && (url != this._url || this._type != this.properties.type))
|
||||
this.fetchFile(url);
|
||||
this.setOutputData(0, this._data );
|
||||
};
|
||||
|
||||
ConstantFile.prototype.setValue = ConstantNumber.prototype.setValue;
|
||||
|
||||
ConstantFile.prototype.fetchFile = function(url) {
|
||||
var that = this;
|
||||
if(!url || url.constructor !== String)
|
||||
{
|
||||
that._data = null;
|
||||
that.boxcolor = null;
|
||||
return;
|
||||
}
|
||||
|
||||
this._url = url;
|
||||
this._type = this.properties.type;
|
||||
if (url.substr(0, 4) == "http" && LiteGraph.proxy) {
|
||||
url = LiteGraph.proxy + url.substr(url.indexOf(":") + 3);
|
||||
}
|
||||
fetch(url)
|
||||
.then(function(response) {
|
||||
if(!response.ok)
|
||||
throw new Error("File not found");
|
||||
|
||||
if(that.properties.type == "arraybuffer")
|
||||
return response.arrayBuffer();
|
||||
else if(that.properties.type == "text")
|
||||
return response.text();
|
||||
else if(that.properties.type == "json")
|
||||
return response.json();
|
||||
else if(that.properties.type == "blob")
|
||||
return response.blob();
|
||||
})
|
||||
.then(function(data) {
|
||||
that._data = data;
|
||||
that.boxcolor = "#AEA";
|
||||
})
|
||||
.catch(function(error) {
|
||||
that._data = null;
|
||||
that.boxcolor = "red";
|
||||
console.error("error fetching file:",url);
|
||||
});
|
||||
};
|
||||
|
||||
ConstantFile.prototype.onDropFile = function(file)
|
||||
{
|
||||
var that = this;
|
||||
this._url = file.name;
|
||||
this._type = this.properties.type;
|
||||
this.properties.url = file.name;
|
||||
var reader = new FileReader();
|
||||
reader.onload = function(e)
|
||||
{
|
||||
that.boxcolor = "#AEA";
|
||||
var v = e.target.result;
|
||||
if( that.properties.type == "json" )
|
||||
v = JSON.parse(v);
|
||||
that._data = v;
|
||||
}
|
||||
if(that.properties.type == "arraybuffer")
|
||||
reader.readAsArrayBuffer(file);
|
||||
else if(that.properties.type == "text" || that.properties.type == "json")
|
||||
reader.readAsText(file);
|
||||
else if(that.properties.type == "blob")
|
||||
return reader.readAsBinaryString(file);
|
||||
}
|
||||
|
||||
LiteGraph.registerNodeType("basic/file", ConstantFile);
|
||||
|
||||
//to store json objects
|
||||
function ConstantData() {
|
||||
this.addOutput("", "");
|
||||
this.addProperty("value", "");
|
||||
this.widget = this.addWidget(
|
||||
"text",
|
||||
"json",
|
||||
"",
|
||||
this.setValue.bind(this)
|
||||
);
|
||||
this.widget = this.addWidget("text","json","","value");
|
||||
this.widgets_up = true;
|
||||
this.size = [140, 30];
|
||||
this._value = null;
|
||||
@@ -569,11 +827,6 @@
|
||||
ConstantData.title = "Const Data";
|
||||
ConstantData.desc = "Constant Data";
|
||||
|
||||
ConstantData.prototype.setValue = function(v) {
|
||||
this.properties.value = v;
|
||||
this.onPropertyChanged("value", v);
|
||||
};
|
||||
|
||||
ConstantData.prototype.onPropertyChanged = function(name, value) {
|
||||
this.widget.value = value;
|
||||
if (value == null || value == "") {
|
||||
@@ -592,18 +845,120 @@
|
||||
this.setOutputData(0, this._value);
|
||||
};
|
||||
|
||||
ConstantData.prototype.setValue = ConstantNumber.prototype.setValue;
|
||||
|
||||
LiteGraph.registerNodeType("basic/data", ConstantData);
|
||||
|
||||
//to store json objects
|
||||
function ConstantArray() {
|
||||
this.addInput("", "");
|
||||
this.addOutput("", "array");
|
||||
this.addOutput("length", "number");
|
||||
this.addProperty("value", "");
|
||||
this.widget = this.addWidget("text","array","","value");
|
||||
this.widgets_up = true;
|
||||
this.size = [140, 50];
|
||||
this._value = null;
|
||||
}
|
||||
|
||||
ConstantArray.title = "Const Array";
|
||||
ConstantArray.desc = "Constant Array";
|
||||
|
||||
ConstantArray.prototype.onPropertyChanged = function(name, value) {
|
||||
this.widget.value = value;
|
||||
if (value == null || value == "") {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
if(value[0] != "[")
|
||||
this._value = JSON.parse("[" + value + "]");
|
||||
else
|
||||
this._value = JSON.parse(value);
|
||||
this.boxcolor = "#AEA";
|
||||
} catch (err) {
|
||||
this.boxcolor = "red";
|
||||
}
|
||||
};
|
||||
|
||||
ConstantArray.prototype.onExecute = function() {
|
||||
var v = this.getInputData(0);
|
||||
if(v && v.length) //clone
|
||||
{
|
||||
if(!this._value)
|
||||
this._value = new Array();
|
||||
this._value.length = v.length;
|
||||
for(var i = 0; i < v.length; ++i)
|
||||
this._value[i] = v[i];
|
||||
}
|
||||
this.setOutputData(0, this._value);
|
||||
this.setOutputData(1, this._value ? ( this._value.length || 0) : 0 );
|
||||
};
|
||||
|
||||
ConstantArray.prototype.setValue = ConstantNumber.prototype.setValue;
|
||||
|
||||
LiteGraph.registerNodeType("basic/array", ConstantArray);
|
||||
|
||||
function ArrayElement() {
|
||||
this.addInput("array", "array,table,string");
|
||||
this.addInput("index", "number");
|
||||
this.addOutput("value", "");
|
||||
this.addProperty("index",0);
|
||||
}
|
||||
|
||||
ArrayElement.title = "Array[i]";
|
||||
ArrayElement.desc = "Returns an element from an array";
|
||||
|
||||
ArrayElement.prototype.onExecute = function() {
|
||||
var array = this.getInputData(0);
|
||||
var index = this.getInputData(1);
|
||||
if(index == null)
|
||||
index = this.properties.index;
|
||||
if(array == null || index == null )
|
||||
return;
|
||||
this.setOutputData(0, array[Math.floor(Number(index))] );
|
||||
};
|
||||
|
||||
LiteGraph.registerNodeType("basic/array[]", ArrayElement);
|
||||
|
||||
|
||||
|
||||
function TableElement() {
|
||||
this.addInput("table", "table");
|
||||
this.addInput("row", "number");
|
||||
this.addInput("col", "number");
|
||||
this.addOutput("value", "");
|
||||
this.addProperty("row",0);
|
||||
this.addProperty("column",0);
|
||||
}
|
||||
|
||||
TableElement.title = "Table[row][col]";
|
||||
TableElement.desc = "Returns an element from a table";
|
||||
|
||||
TableElement.prototype.onExecute = function() {
|
||||
var table = this.getInputData(0);
|
||||
var row = this.getInputData(1);
|
||||
var col = this.getInputData(2);
|
||||
if(row == null)
|
||||
row = this.properties.row;
|
||||
if(col == null)
|
||||
col = this.properties.column;
|
||||
if(table == null || row == null || col == null)
|
||||
return;
|
||||
var row = table[Math.floor(Number(row))];
|
||||
if(row)
|
||||
this.setOutputData(0, row[Math.floor(Number(col))] );
|
||||
else
|
||||
this.setOutputData(0, null );
|
||||
};
|
||||
|
||||
LiteGraph.registerNodeType("basic/table[][]", TableElement);
|
||||
|
||||
function ObjectProperty() {
|
||||
this.addInput("obj", "");
|
||||
this.addOutput("", "");
|
||||
this.addProperty("value", "");
|
||||
this.widget = this.addWidget(
|
||||
"text",
|
||||
"prop.",
|
||||
"",
|
||||
this.setValue.bind(this)
|
||||
);
|
||||
this.widget = this.addWidget("text","prop.","",this.setValue.bind(this) );
|
||||
this.widgets_up = true;
|
||||
this.size = [140, 30];
|
||||
this._value = null;
|
||||
@@ -690,28 +1045,70 @@
|
||||
this.size = [60, 30];
|
||||
this.addInput("in");
|
||||
this.addOutput("out");
|
||||
this.properties = { varname: "myname", global: false };
|
||||
this.properties = { varname: "myname", container: Variable.LITEGRAPH };
|
||||
this.value = null;
|
||||
}
|
||||
|
||||
Variable.title = "Variable";
|
||||
Variable.desc = "store/read variable value";
|
||||
|
||||
Variable.LITEGRAPH = 0; //between all graphs
|
||||
Variable.GRAPH = 1; //only inside this graph
|
||||
Variable.GLOBALSCOPE = 2; //attached to Window
|
||||
|
||||
Variable["@container"] = { type: "enum", values: {"litegraph":Variable.LITEGRAPH, "graph":Variable.GRAPH,"global": Variable.GLOBALSCOPE} };
|
||||
|
||||
Variable.prototype.onExecute = function() {
|
||||
this.value = this.getInputData(0);
|
||||
if(this.graph)
|
||||
this.graph.vars[ this.properties.varname ] = this.value;
|
||||
if(this.properties.global)
|
||||
global[this.properties.varname] = this.value;
|
||||
this.setOutputData(0, this.value );
|
||||
var container = this.getContainer();
|
||||
|
||||
if(this.isInputConnected(0))
|
||||
{
|
||||
this.value = this.getInputData(0);
|
||||
container[ this.properties.varname ] = this.value;
|
||||
this.setOutputData(0, this.value );
|
||||
return;
|
||||
}
|
||||
|
||||
this.setOutputData( 0, container[ this.properties.varname ] );
|
||||
};
|
||||
|
||||
Variable.prototype.getContainer = function()
|
||||
{
|
||||
switch(this.properties.container)
|
||||
{
|
||||
case Variable.GRAPH:
|
||||
if(this.graph)
|
||||
return this.graph.vars;
|
||||
return {};
|
||||
break;
|
||||
case Variable.GLOBALSCOPE:
|
||||
return global;
|
||||
break;
|
||||
case Variable.LITEGRAPH:
|
||||
default:
|
||||
return LiteGraph.Globals;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Variable.prototype.getTitle = function() {
|
||||
return this.properties.varname;
|
||||
};
|
||||
|
||||
LiteGraph.registerNodeType("basic/variable", Variable);
|
||||
|
||||
function length(v) {
|
||||
if(v && v.length != null)
|
||||
return Number(v.length);
|
||||
return 0;
|
||||
}
|
||||
|
||||
LiteGraph.wrapFunctionAsNode(
|
||||
"basic/length",
|
||||
length,
|
||||
[""],
|
||||
"number"
|
||||
);
|
||||
|
||||
function DownloadData() {
|
||||
this.size = [60, 30];
|
||||
|
||||
@@ -20,22 +20,30 @@
|
||||
//convert to Event if the value is true
|
||||
function TriggerEvent() {
|
||||
this.size = [60, 30];
|
||||
this.addInput("in", "");
|
||||
this.addInput("if", "");
|
||||
this.addOutput("true", LiteGraph.EVENT);
|
||||
this.addOutput("change", LiteGraph.EVENT);
|
||||
this.was_true = false;
|
||||
this.addOutput("false", LiteGraph.EVENT);
|
||||
this.properties = { only_on_change: true };
|
||||
this.prev = 0;
|
||||
}
|
||||
|
||||
TriggerEvent.title = "TriggerEvent";
|
||||
TriggerEvent.desc = "Triggers event if value is true";
|
||||
TriggerEvent.desc = "Triggers event if input evaluates to true";
|
||||
|
||||
TriggerEvent.prototype.onExecute = function(action, param) {
|
||||
var v = this.getInputData(0);
|
||||
if(v)
|
||||
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);
|
||||
if(v && !this.was_true)
|
||||
if(!v && must_resend)
|
||||
this.triggerSlot(2, param);
|
||||
if(changed)
|
||||
this.triggerSlot(1, param);
|
||||
this.was_true = v;
|
||||
this.prev = v;
|
||||
};
|
||||
|
||||
LiteGraph.registerNodeType("events/trigger", TriggerEvent);
|
||||
@@ -118,6 +126,29 @@
|
||||
|
||||
LiteGraph.registerNodeType("events/filter", FilterEvent);
|
||||
|
||||
|
||||
function EventBranch() {
|
||||
this.addInput("in", LiteGraph.ACTION);
|
||||
this.addInput("cond", "boolean");
|
||||
this.addOutput("true", LiteGraph.EVENT);
|
||||
this.addOutput("false", LiteGraph.EVENT);
|
||||
this.size = [120, 60];
|
||||
this._value = false;
|
||||
}
|
||||
|
||||
EventBranch.title = "Branch";
|
||||
EventBranch.desc = "If condition is true, outputs triggers true, otherwise false";
|
||||
|
||||
EventBranch.prototype.onExecute = function() {
|
||||
this._value = this.getInputData(1);
|
||||
}
|
||||
|
||||
EventBranch.prototype.onAction = function(action, param) {
|
||||
this.triggerSlot(this._value ? 0 : 1);
|
||||
}
|
||||
|
||||
LiteGraph.registerNodeType("events/branch", EventBranch);
|
||||
|
||||
//Show value inside the debug console
|
||||
function EventCounter() {
|
||||
this.addInput("inc", LiteGraph.ACTION);
|
||||
|
||||
@@ -65,8 +65,9 @@
|
||||
|
||||
LGraphPoints3D.OBJECT = 20;
|
||||
LGraphPoints3D.OBJECT_UNIFORMLY = 21;
|
||||
LGraphPoints3D.OBJECT_INSIDE = 22;
|
||||
|
||||
LGraphPoints3D.MODE_VALUES = { "rectangle":LGraphPoints3D.RECTANGLE, "circle":LGraphPoints3D.CIRCLE, "cube":LGraphPoints3D.CUBE, "sphere":LGraphPoints3D.SPHERE, "hemisphere":LGraphPoints3D.HEMISPHERE, "inside_sphere":LGraphPoints3D.INSIDE_SPHERE, "object":LGraphPoints3D.OBJECT, "object_uniformly":LGraphPoints3D.OBJECT_UNIFORMLY };
|
||||
LGraphPoints3D.MODE_VALUES = { "rectangle":LGraphPoints3D.RECTANGLE, "circle":LGraphPoints3D.CIRCLE, "cube":LGraphPoints3D.CUBE, "sphere":LGraphPoints3D.SPHERE, "hemisphere":LGraphPoints3D.HEMISPHERE, "inside_sphere":LGraphPoints3D.INSIDE_SPHERE, "object":LGraphPoints3D.OBJECT, "object_uniformly":LGraphPoints3D.OBJECT_UNIFORMLY, "object_inside":LGraphPoints3D.OBJECT_INSIDE };
|
||||
|
||||
LGraphPoints3D.widgets_info = {
|
||||
mode: { widget: "combo", values: LGraphPoints3D.MODE_VALUES }
|
||||
@@ -164,7 +165,7 @@
|
||||
if(normals)
|
||||
{
|
||||
for(var i = 0; i < normals.length; i+=3)
|
||||
normals.set(i, UP);
|
||||
normals.set(UP, i);
|
||||
}
|
||||
}
|
||||
else if( mode == LGraphPoints3D.SPHERE)
|
||||
@@ -195,7 +196,7 @@
|
||||
if(normals)
|
||||
{
|
||||
for(var i = 0; i < normals.length; i+=3)
|
||||
normals.set(i, UP);
|
||||
normals.set(UP, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -212,7 +213,7 @@
|
||||
if(normals)
|
||||
{
|
||||
for(var i = 0; i < normals.length; i+=3)
|
||||
normals.set(i, UP);
|
||||
normals.set(UP, i);
|
||||
}
|
||||
}
|
||||
else if( mode == LGraphPoints3D.CUBE)
|
||||
@@ -226,7 +227,7 @@
|
||||
if(normals)
|
||||
{
|
||||
for(var i = 0; i < normals.length; i+=3)
|
||||
normals.set(i, UP);
|
||||
normals.set(UP, i);
|
||||
}
|
||||
}
|
||||
else if( mode == LGraphPoints3D.SPHERE)
|
||||
@@ -261,6 +262,12 @@
|
||||
{
|
||||
LGraphPoints3D.generateFromObject( points, normals, size, obj, true );
|
||||
}
|
||||
else if( mode == LGraphPoints3D.OBJECT_INSIDE)
|
||||
{
|
||||
LGraphPoints3D.generateFromInsideObject( points, size, obj );
|
||||
//if(normals)
|
||||
// LGraphPoints3D.generateSphericalNormals( points, normals );
|
||||
}
|
||||
else
|
||||
console.warn("wrong mode in LGraphPoints3D");
|
||||
}
|
||||
@@ -466,6 +473,36 @@
|
||||
}
|
||||
}
|
||||
|
||||
LGraphPoints3D.generateFromInsideObject = function( points, size, mesh )
|
||||
{
|
||||
if(!mesh || mesh.constructor !== GL.Mesh)
|
||||
return;
|
||||
|
||||
var aabb = mesh.getBoundingBox();
|
||||
if(!mesh.octree)
|
||||
mesh.octree = new GL.Octree( mesh );
|
||||
var octree = mesh.octree;
|
||||
var origin = vec3.create();
|
||||
var direction = vec3.fromValues(1,0,0);
|
||||
var temp = vec3.create();
|
||||
var i = 0;
|
||||
var tries = 0;
|
||||
while(i < size && tries < points.length * 10) //limit to avoid problems
|
||||
{
|
||||
tries += 1
|
||||
var r = vec3.random(temp); //random point inside the aabb
|
||||
r[0] = (r[0] * 2 - 1) * aabb[3] + aabb[0];
|
||||
r[1] = (r[1] * 2 - 1) * aabb[4] + aabb[1];
|
||||
r[2] = (r[2] * 2 - 1) * aabb[5] + aabb[2];
|
||||
origin.set(r);
|
||||
var hit = octree.testRay( origin, direction, 0, 10000, true, GL.Octree.ALL );
|
||||
if(!hit || hit.length % 2 == 0) //not inside
|
||||
continue;
|
||||
points.set( r, i );
|
||||
i+=3;
|
||||
}
|
||||
}
|
||||
|
||||
LiteGraph.registerNodeType( "geometry/points3D", LGraphPoints3D );
|
||||
|
||||
|
||||
@@ -474,11 +511,13 @@
|
||||
this.addInput("points", "geometry");
|
||||
this.addOutput("instances", "[mat4]");
|
||||
this.properties = {
|
||||
mode: 1
|
||||
mode: 1,
|
||||
autoupdate: true
|
||||
};
|
||||
|
||||
this.must_update = true;
|
||||
this.matrices = [];
|
||||
this.first_time = true;
|
||||
}
|
||||
|
||||
LGraphPointsToInstances.NORMAL = 0;
|
||||
@@ -506,8 +545,13 @@
|
||||
if( !this.isOutputConnected(0) )
|
||||
return;
|
||||
|
||||
if( geo._version != this._version || geo._id != this._geometry_id )
|
||||
var has_changed = (geo._version != this._version || geo._id != this._geometry_id);
|
||||
|
||||
if( has_changed && this.properties.autoupdate || this.first_time )
|
||||
{
|
||||
this.first_time = false;
|
||||
this.updateInstances( geo );
|
||||
}
|
||||
|
||||
this.setOutputData( 0, this.matrices );
|
||||
}
|
||||
@@ -611,7 +655,8 @@
|
||||
this.geometry = {
|
||||
type: "triangles",
|
||||
vertices: null,
|
||||
_id: generateGeometryId()
|
||||
_id: generateGeometryId(),
|
||||
_version: 0
|
||||
};
|
||||
|
||||
this._last_geometry_id = -1;
|
||||
@@ -730,7 +775,7 @@
|
||||
this.addInput("sides", "number");
|
||||
this.addInput("radius", "number");
|
||||
this.addOutput("out", "geometry");
|
||||
this.properties = { sides: 6, radius: 1 }
|
||||
this.properties = { sides: 6, radius: 1, uvs: false }
|
||||
|
||||
this.geometry = {
|
||||
type: "line_loop",
|
||||
@@ -768,6 +813,13 @@
|
||||
if( !vertices || vertices.length != num )
|
||||
vertices = this.geometry.vertices = new Float32Array( 3*sides );
|
||||
var delta = (Math.PI * 2) / sides;
|
||||
var gen_uvs = this.properties.uvs;
|
||||
if(gen_uvs)
|
||||
{
|
||||
uvs = this.geometry.coords = new Float32Array( 3*sides );
|
||||
}
|
||||
|
||||
|
||||
for(var i = 0; i < sides; ++i)
|
||||
{
|
||||
var angle = delta * -i;
|
||||
@@ -777,6 +829,12 @@
|
||||
vertices[i*3] = x;
|
||||
vertices[i*3+1] = y;
|
||||
vertices[i*3+2] = z;
|
||||
|
||||
if(gen_uvs)
|
||||
{
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
this.geometry._id = ++this.geometry_id;
|
||||
this.geometry._version = ++this.version;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
(function(global) {
|
||||
var LiteGraph = global.LiteGraph;
|
||||
var LGraphTexture = global.LGraphTexture;
|
||||
|
||||
//Works with Litegl.js to create WebGL nodes
|
||||
if (typeof GL != "undefined") {
|
||||
|
||||
1588
src/nodes/glshaders.js
Normal file
@@ -1,5 +1,6 @@
|
||||
(function(global) {
|
||||
var LiteGraph = global.LiteGraph;
|
||||
var LGraphCanvas = global.LGraphCanvas;
|
||||
|
||||
//Works with Litegl.js to create WebGL nodes
|
||||
global.LGraphTexture = null;
|
||||
@@ -33,14 +34,16 @@
|
||||
LGraphTexture.image_preview_size = 256;
|
||||
|
||||
//flags to choose output texture type
|
||||
LGraphTexture.PASS_THROUGH = 1; //do not apply FX
|
||||
LGraphTexture.UNDEFINED = 0; //not specified
|
||||
LGraphTexture.PASS_THROUGH = 1; //do not apply FX (like disable but passing the in to the out)
|
||||
LGraphTexture.COPY = 2; //create new texture with the same properties as the origin texture
|
||||
LGraphTexture.LOW = 3; //create new texture with low precision (byte)
|
||||
LGraphTexture.HIGH = 4; //create new texture with high precision (half-float)
|
||||
LGraphTexture.REUSE = 5; //reuse input texture
|
||||
LGraphTexture.DEFAULT = 2;
|
||||
LGraphTexture.DEFAULT = 2; //use the default
|
||||
|
||||
LGraphTexture.MODE_VALUES = {
|
||||
"undefined": LGraphTexture.UNDEFINED,
|
||||
"pass through": LGraphTexture.PASS_THROUGH,
|
||||
copy: LGraphTexture.COPY,
|
||||
low: LGraphTexture.LOW,
|
||||
@@ -113,11 +116,12 @@
|
||||
!target ||
|
||||
target.width != origin.width ||
|
||||
target.height != origin.height ||
|
||||
target.type != tex_type
|
||||
target.type != tex_type ||
|
||||
target.format != origin.format
|
||||
) {
|
||||
target = new GL.Texture(origin.width, origin.height, {
|
||||
type: tex_type,
|
||||
format: gl.RGBA,
|
||||
format: origin.format,
|
||||
filter: gl.LINEAR
|
||||
});
|
||||
}
|
||||
@@ -659,7 +663,7 @@
|
||||
u_texture: 0,
|
||||
u_textureB: 1,
|
||||
value: value,
|
||||
texSize: [width, height],
|
||||
texSize: [width, height,1/width,1/height],
|
||||
time: time
|
||||
})
|
||||
.draw(mesh);
|
||||
@@ -674,7 +678,7 @@
|
||||
uniform sampler2D u_texture;\n\
|
||||
uniform sampler2D u_textureB;\n\
|
||||
varying vec2 v_coord;\n\
|
||||
uniform vec2 texSize;\n\
|
||||
uniform vec4 texSize;\n\
|
||||
uniform float time;\n\
|
||||
uniform float value;\n\
|
||||
\n\
|
||||
@@ -711,6 +715,20 @@
|
||||
LGraphTextureOperation.registerPreset("displace","texture2D(u_texture, uv + (colorB.xy - vec2(0.5)) * value).xyz");
|
||||
LGraphTextureOperation.registerPreset("grayscale","vec3(color.x + color.y + color.z) * value / 3.0");
|
||||
LGraphTextureOperation.registerPreset("saturation","mix( vec3(color.x + color.y + color.z) / 3.0, color, value )");
|
||||
LGraphTextureOperation.registerPreset("normalmap","\n\
|
||||
float z0 = texture2D(u_texture, uv + vec2(-texSize.z, -texSize.w) ).x;\n\
|
||||
float z1 = texture2D(u_texture, uv + vec2(0.0, -texSize.w) ).x;\n\
|
||||
float z2 = texture2D(u_texture, uv + vec2(texSize.z, -texSize.w) ).x;\n\
|
||||
float z3 = texture2D(u_texture, uv + vec2(-texSize.z, 0.0) ).x;\n\
|
||||
float z4 = color.x;\n\
|
||||
float z5 = texture2D(u_texture, uv + vec2(texSize.z, 0.0) ).x;\n\
|
||||
float z6 = texture2D(u_texture, uv + vec2(-texSize.z, texSize.w) ).x;\n\
|
||||
float z7 = texture2D(u_texture, uv + vec2(0.0, texSize.w) ).x;\n\
|
||||
float z8 = texture2D(u_texture, uv + vec2(texSize.z, texSize.w) ).x;\n\
|
||||
vec3 normal = vec3( z2 + 2.0*z4 + z7 - z0 - 2.0*z3 - z5, z5 + 2.0*z6 + z7 -z0 - 2.0*z1 - z2, 1.0 );\n\
|
||||
normal.xy *= value;\n\
|
||||
result.xyz = normalize(normal) * 0.5 + vec3(0.5);\n\
|
||||
");
|
||||
LGraphTextureOperation.registerPreset("threshold","vec3(color.x > colorB.x * value ? 1.0 : 0.0,color.y > colorB.y * value ? 1.0 : 0.0,color.z > colorB.z * value ? 1.0 : 0.0)");
|
||||
|
||||
//webglstudio stuff...
|
||||
@@ -742,15 +760,14 @@
|
||||
precision: LGraphTexture.DEFAULT
|
||||
};
|
||||
|
||||
this.properties.code =
|
||||
"//time: time in seconds\n//texSize: vec2 with res\nuniform float u_value;\nuniform vec4 u_color;\n\nvoid main() {\n vec2 uv = v_coord;\n vec3 color = vec3(0.0);\n //your code here\n color.xy=uv;\n\ngl_FragColor = vec4(color, 1.0);\n}\n";
|
||||
this._uniforms = { u_value: 1, u_color: vec4.create(), in_texture: 0, texSize: vec2.create(), time: 0 };
|
||||
this.properties.code = LGraphTextureShader.pixel_shader;
|
||||
this._uniforms = { u_value: 1, u_color: vec4.create(), in_texture: 0, texSize: vec4.create(), time: 0 };
|
||||
}
|
||||
|
||||
LGraphTextureShader.title = "Shader";
|
||||
LGraphTextureShader.desc = "Texture shader";
|
||||
LGraphTextureShader.widgets_info = {
|
||||
code: { type: "code" },
|
||||
code: { type: "code", lang: "glsl" },
|
||||
precision: { widget: "combo", values: LGraphTexture.MODE_VALUES }
|
||||
};
|
||||
|
||||
@@ -853,10 +870,7 @@
|
||||
}
|
||||
|
||||
this._shader_code = this.properties.code;
|
||||
this._shader = new GL.Shader(
|
||||
Shader.SCREEN_VERTEX_SHADER,
|
||||
LGraphTextureShader.pixel_shader + this.properties.code
|
||||
);
|
||||
this._shader = new GL.Shader( Shader.SCREEN_VERTEX_SHADER, this.properties.code );
|
||||
if (!this._shader) {
|
||||
this.boxcolor = "red";
|
||||
return null;
|
||||
@@ -913,6 +927,8 @@
|
||||
}
|
||||
uniforms.texSize[0] = w;
|
||||
uniforms.texSize[1] = h;
|
||||
uniforms.texSize[2] = 1/w;
|
||||
uniforms.texSize[3] = 1/h;
|
||||
uniforms.time = this.graph.getTime();
|
||||
uniforms.u_value = this.properties.u_value;
|
||||
uniforms.u_color.set( this.properties.u_color );
|
||||
@@ -929,10 +945,20 @@
|
||||
};
|
||||
|
||||
LGraphTextureShader.pixel_shader =
|
||||
"precision highp float;\n\
|
||||
\n\
|
||||
varying vec2 v_coord;\n\
|
||||
uniform float time;\n\
|
||||
"precision highp float;\n\
|
||||
\n\
|
||||
varying vec2 v_coord;\n\
|
||||
uniform float time; //time in seconds\n\
|
||||
uniform vec4 texSize; //tex resolution\n\
|
||||
uniform float u_value;\n\
|
||||
uniform vec4 u_color;\n\n\
|
||||
void main() {\n\
|
||||
vec2 uv = v_coord;\n\
|
||||
vec3 color = vec3(0.0);\n\
|
||||
//your code here\n\
|
||||
color.xy=uv;\n\n\
|
||||
gl_FragColor = vec4(color, 1.0);\n\
|
||||
}\n\
|
||||
";
|
||||
|
||||
LiteGraph.registerNodeType("texture/shader", LGraphTextureShader);
|
||||
@@ -1206,6 +1232,20 @@
|
||||
LGraphTextureToViewport.desc = "Texture to viewport";
|
||||
|
||||
LGraphTextureToViewport._prev_viewport = new Float32Array(4);
|
||||
|
||||
LGraphTextureToViewport.prototype.onDrawBackground = function( ctx )
|
||||
{
|
||||
if ( this.flags.collapsed || this.size[1] <= 40 )
|
||||
return;
|
||||
|
||||
var tex = this.getInputData(0);
|
||||
if (!tex) {
|
||||
return;
|
||||
}
|
||||
|
||||
ctx.drawImage( ctx == gl ? tex : gl.canvas, 10,30, this.size[0] -20, this.size[1] -40);
|
||||
}
|
||||
|
||||
LGraphTextureToViewport.prototype.onExecute = function() {
|
||||
var tex = this.getInputData(0);
|
||||
if (!tex) {
|
||||
@@ -1573,6 +1613,69 @@
|
||||
LGraphTextureDownsample
|
||||
);
|
||||
|
||||
|
||||
|
||||
function LGraphTextureResize() {
|
||||
this.addInput("Texture", "Texture");
|
||||
this.addOutput("", "Texture");
|
||||
this.properties = {
|
||||
size: [512,512],
|
||||
generate_mipmaps: false,
|
||||
precision: LGraphTexture.DEFAULT
|
||||
};
|
||||
}
|
||||
|
||||
LGraphTextureResize.title = "Resize";
|
||||
LGraphTextureResize.desc = "Resize Texture";
|
||||
LGraphTextureResize.widgets_info = {
|
||||
iterations: { type: "number", step: 1, precision: 0, min: 0 },
|
||||
precision: { widget: "combo", values: LGraphTexture.MODE_VALUES }
|
||||
};
|
||||
|
||||
LGraphTextureResize.prototype.onExecute = function() {
|
||||
var tex = this.getInputData(0);
|
||||
if (!tex && !this._temp_texture) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.isOutputConnected(0)) {
|
||||
return;
|
||||
} //saves work
|
||||
|
||||
//we do not allow any texture different than texture 2D
|
||||
if (!tex || tex.texture_type !== GL.TEXTURE_2D) {
|
||||
return;
|
||||
}
|
||||
|
||||
var width = this.properties.size[0] | 0;
|
||||
var height = this.properties.size[1] | 0;
|
||||
if(width == 0)
|
||||
width = tex.width;
|
||||
if(height == 0)
|
||||
height = tex.height;
|
||||
var type = tex.type;
|
||||
if (this.properties.precision === LGraphTexture.LOW) {
|
||||
type = gl.UNSIGNED_BYTE;
|
||||
} else if (this.properties.precision === LGraphTexture.HIGH) {
|
||||
type = gl.HIGH_PRECISION_FORMAT;
|
||||
}
|
||||
|
||||
if( !this._texture || this._texture.width != width || this._texture.height != height || this._texture.type != type )
|
||||
this._texture = new GL.Texture( width, height, { type: type } );
|
||||
|
||||
tex.copyTo( this._texture );
|
||||
|
||||
if (this.properties.generate_mipmaps) {
|
||||
this._texture.bind(0);
|
||||
gl.generateMipmap(this._texture.texture_type);
|
||||
this._texture.unbind(0);
|
||||
}
|
||||
|
||||
this.setOutputData(0, this._texture);
|
||||
};
|
||||
|
||||
LiteGraph.registerNodeType( "texture/resize", LGraphTextureResize );
|
||||
|
||||
// Texture Average *****************************************
|
||||
function LGraphTextureAverage() {
|
||||
this.addInput("Texture", "Texture");
|
||||
@@ -2234,6 +2337,130 @@
|
||||
|
||||
LiteGraph.registerNodeType("texture/LUT", LGraphTextureLUT);
|
||||
|
||||
|
||||
// Texture LUT *****************************************
|
||||
function LGraphTextureEncode() {
|
||||
this.addInput("Texture", "Texture");
|
||||
this.addInput("Atlas", "Texture");
|
||||
this.addOutput("", "Texture");
|
||||
this.properties = { enabled: true, num_row_symbols: 4, symbol_size: 16, brightness: 1, colorize: false, filter: false, invert: false, precision: LGraphTexture.DEFAULT, generate_mipmaps: false, texture: null };
|
||||
|
||||
if (!LGraphTextureEncode._shader) {
|
||||
LGraphTextureEncode._shader = new GL.Shader( Shader.SCREEN_VERTEX_SHADER, LGraphTextureEncode.pixel_shader );
|
||||
}
|
||||
|
||||
this._uniforms = {
|
||||
u_texture: 0,
|
||||
u_textureB: 1,
|
||||
u_row_simbols: 4,
|
||||
u_simbol_size: 16,
|
||||
u_res: vec2.create()
|
||||
};
|
||||
}
|
||||
|
||||
LGraphTextureEncode.widgets_info = {
|
||||
texture: { widget: "texture" },
|
||||
precision: { widget: "combo", values: LGraphTexture.MODE_VALUES }
|
||||
};
|
||||
|
||||
LGraphTextureEncode.title = "Encode";
|
||||
LGraphTextureEncode.desc = "Apply a texture atlas to encode a texture";
|
||||
|
||||
LGraphTextureEncode.prototype.onExecute = function() {
|
||||
if (!this.isOutputConnected(0)) {
|
||||
return;
|
||||
} //saves work
|
||||
|
||||
var tex = this.getInputData(0);
|
||||
|
||||
if (this.properties.precision === LGraphTexture.PASS_THROUGH || this.properties.enabled === false) {
|
||||
this.setOutputData(0, tex);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!tex) {
|
||||
return;
|
||||
}
|
||||
|
||||
var symbols_tex = this.getInputData(1);
|
||||
|
||||
if (!symbols_tex) {
|
||||
symbols_tex = LGraphTexture.getTexture(this.properties.texture);
|
||||
}
|
||||
|
||||
if (!symbols_tex) {
|
||||
this.setOutputData(0, tex);
|
||||
return;
|
||||
}
|
||||
|
||||
symbols_tex.bind(0);
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, this.properties.filter ? gl.LINEAR : gl.NEAREST );
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, this.properties.filter ? gl.LINEAR : gl.NEAREST );
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE );
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE );
|
||||
gl.bindTexture(gl.TEXTURE_2D, null);
|
||||
|
||||
var uniforms = this._uniforms;
|
||||
uniforms.u_row_simbols = Math.floor(this.properties.num_row_symbols);
|
||||
uniforms.u_symbol_size = this.properties.symbol_size;
|
||||
uniforms.u_brightness = this.properties.brightness;
|
||||
uniforms.u_invert = this.properties.invert ? 1 : 0;
|
||||
uniforms.u_colorize = this.properties.colorize ? 1 : 0;
|
||||
|
||||
this._tex = LGraphTexture.getTargetTexture( tex, this._tex, this.properties.precision );
|
||||
uniforms.u_res[0] = this._tex.width;
|
||||
uniforms.u_res[1] = this._tex.height;
|
||||
this._tex.bind(0);
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST );
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST );
|
||||
|
||||
this._tex.drawTo(function() {
|
||||
symbols_tex.bind(1);
|
||||
tex.toViewport(LGraphTextureEncode._shader, uniforms);
|
||||
});
|
||||
|
||||
if (this.properties.generate_mipmaps) {
|
||||
this._tex.bind(0);
|
||||
gl.generateMipmap(this._tex.texture_type);
|
||||
this._tex.unbind(0);
|
||||
}
|
||||
|
||||
this.setOutputData(0, this._tex);
|
||||
};
|
||||
|
||||
LGraphTextureEncode.pixel_shader =
|
||||
"precision highp float;\n\
|
||||
precision highp float;\n\
|
||||
varying vec2 v_coord;\n\
|
||||
uniform sampler2D u_texture;\n\
|
||||
uniform sampler2D u_textureB;\n\
|
||||
uniform float u_row_simbols;\n\
|
||||
uniform float u_symbol_size;\n\
|
||||
uniform float u_brightness;\n\
|
||||
uniform float u_invert;\n\
|
||||
uniform float u_colorize;\n\
|
||||
uniform vec2 u_res;\n\
|
||||
\n\
|
||||
void main() {\n\
|
||||
vec2 total_symbols = u_res / u_symbol_size;\n\
|
||||
vec2 uv = floor(v_coord * total_symbols) / total_symbols; //pixelate \n\
|
||||
vec2 local_uv = mod(v_coord * u_res, u_symbol_size) / u_symbol_size;\n\
|
||||
lowp vec4 textureColor = texture2D(u_texture, uv );\n\
|
||||
float lum = clamp(u_brightness * (textureColor.x + textureColor.y + textureColor.z)/3.0,0.0,1.0);\n\
|
||||
if( u_invert == 1.0 ) lum = 1.0 - lum;\n\
|
||||
float index = floor( lum * (u_row_simbols * u_row_simbols - 1.0));\n\
|
||||
float col = mod( index, u_row_simbols );\n\
|
||||
float row = u_row_simbols - floor( index / u_row_simbols ) - 1.0;\n\
|
||||
vec2 simbol_uv = ( vec2( col, row ) + local_uv ) / u_row_simbols;\n\
|
||||
vec4 color = texture2D( u_textureB, simbol_uv );\n\
|
||||
if(u_colorize == 1.0)\n\
|
||||
color *= textureColor;\n\
|
||||
gl_FragColor = color;\n\
|
||||
}\n\
|
||||
";
|
||||
|
||||
LiteGraph.registerNodeType("texture/encode", LGraphTextureEncode);
|
||||
|
||||
// Texture Channels *****************************************
|
||||
function LGraphTextureChannels() {
|
||||
this.addInput("Texture", "Texture");
|
||||
@@ -3044,8 +3271,8 @@
|
||||
};
|
||||
this._uniforms = {
|
||||
u_texture: 0,
|
||||
u_near: 0.1,
|
||||
u_far: 10000
|
||||
u_camera_planes: null, //filled later
|
||||
u_ires: vec2.create()
|
||||
};
|
||||
}
|
||||
|
||||
@@ -3077,9 +3304,6 @@
|
||||
}
|
||||
|
||||
var uniforms = this._uniforms;
|
||||
|
||||
uniforms.u_near = tex.near_far_planes[0];
|
||||
uniforms.u_far = tex.near_far_planes[1];
|
||||
uniforms.u_invert = this.properties.invert ? 1 : 0;
|
||||
|
||||
gl.disable(gl.BLEND);
|
||||
@@ -3099,6 +3323,8 @@
|
||||
planes = [0.1, 1000];
|
||||
} //hardcoded
|
||||
uniforms.u_camera_planes = planes;
|
||||
//uniforms.u_ires.set([1/tex.width, 1/tex.height]);
|
||||
uniforms.u_ires.set([0,0]);
|
||||
|
||||
this._temp_texture.drawTo(function() {
|
||||
tex.bind(0);
|
||||
@@ -3114,15 +3340,14 @@
|
||||
precision highp float;\n\
|
||||
varying vec2 v_coord;\n\
|
||||
uniform sampler2D u_texture;\n\
|
||||
uniform float u_near;\n\
|
||||
uniform float u_far;\n\
|
||||
uniform vec2 u_camera_planes;\n\
|
||||
uniform int u_invert;\n\
|
||||
uniform vec2 u_ires;\n\
|
||||
\n\
|
||||
void main() {\n\
|
||||
float zNear = u_near;\n\
|
||||
float zFar = u_far;\n\
|
||||
float depth = texture2D(u_texture, v_coord).x;\n\
|
||||
depth = depth * 2.0 - 1.0;\n\
|
||||
float zNear = u_camera_planes.x;\n\
|
||||
float zFar = u_camera_planes.y;\n\
|
||||
float depth = texture2D(u_texture, v_coord + u_ires*0.5).x * 2.0 - 1.0;\n\
|
||||
float f = zNear * (depth + 1.0) / (zFar + zNear - depth * (zFar - zNear));\n\
|
||||
if( u_invert == 1 )\n\
|
||||
f = 1.0 - f;\n\
|
||||
@@ -4256,6 +4481,54 @@ void main(void){\n\
|
||||
LiteGraph.registerNodeType("texture/lensfx", LGraphLensFX);
|
||||
|
||||
|
||||
function LGraphTextureFromData() {
|
||||
this.addInput("in", "");
|
||||
this.properties = { precision: LGraphTexture.LOW, width: 0, height: 0, channels: 1 };
|
||||
this.addOutput("out", "Texture");
|
||||
}
|
||||
|
||||
LGraphTextureFromData.title = "Data->Tex";
|
||||
LGraphTextureFromData.desc = "Generates or applies a curve to a texture";
|
||||
LGraphTextureFromData.widgets_info = {
|
||||
precision: { widget: "combo", values: LGraphTexture.MODE_VALUES }
|
||||
};
|
||||
|
||||
LGraphTextureFromData.prototype.onExecute = function() {
|
||||
if (!this.isOutputConnected(0)) {
|
||||
return;
|
||||
} //saves work
|
||||
|
||||
var data = this.getInputData(0);
|
||||
if(!data)
|
||||
return;
|
||||
|
||||
var channels = this.properties.channels;
|
||||
var w = this.properties.width;
|
||||
var h = this.properties.height;
|
||||
if(!w || !h)
|
||||
{
|
||||
w = Math.floor(data.length / channels);
|
||||
h = 1;
|
||||
}
|
||||
var format = gl.RGBA;
|
||||
if( channels == 3 )
|
||||
format = gl.RGB;
|
||||
else if( channels == 1 )
|
||||
format = gl.LUMINANCE;
|
||||
|
||||
var temp = this._temp_texture;
|
||||
var type = LGraphTexture.getTextureType( this.properties.precision );
|
||||
if ( !temp || temp.width != w || temp.height != h || temp.type != type ) {
|
||||
temp = this._temp_texture = new GL.Texture( w, h, { type: type, format: format, filter: gl.LINEAR } );
|
||||
}
|
||||
|
||||
temp.uploadData( data );
|
||||
this.setOutputData(0, temp);
|
||||
}
|
||||
|
||||
LiteGraph.registerNodeType("texture/fromdata", LGraphTextureFromData);
|
||||
|
||||
//applies a curve (or generates one)
|
||||
function LGraphTextureCurve() {
|
||||
this.addInput("in", "Texture");
|
||||
this.addOutput("out", "Texture");
|
||||
@@ -4279,22 +4552,33 @@ void main(void){\n\
|
||||
}
|
||||
|
||||
LGraphTextureCurve.title = "Curve";
|
||||
LGraphTextureCurve.desc = "Generates or applies a curve to a texture";
|
||||
LGraphTextureCurve.widgets_info = {
|
||||
precision: { widget: "combo", values: LGraphTexture.MODE_VALUES }
|
||||
};
|
||||
|
||||
LGraphTextureCurve.prototype.onExecute = function() {
|
||||
var tex = this.getInputData(0);
|
||||
if (!tex) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.isOutputConnected(0)) {
|
||||
return;
|
||||
} //saves work
|
||||
|
||||
var tex = this.getInputData(0);
|
||||
|
||||
var temp = this._temp_texture;
|
||||
if ( !temp || temp.width != tex.width || temp.height != tex.height || temp.type != tex.type ) {
|
||||
temp = this._temp_texture = new GL.Texture( tex.width, tex.height, { type: tex.type, format: gl.RGBA, filter: gl.LINEAR } );
|
||||
if(!tex) //generate one texture, nothing else
|
||||
{
|
||||
if(this._must_update || !this._curve_texture )
|
||||
this.updateCurve();
|
||||
this.setOutputData(0, this._curve_texture);
|
||||
return;
|
||||
}
|
||||
|
||||
var type = LGraphTexture.getTextureType( this.properties.precision, tex );
|
||||
|
||||
//apply curve to input texture
|
||||
if ( !temp || temp.type != type || temp.width != tex.width || temp.height != tex.height || temp.format != tex.format)
|
||||
temp = this._temp_texture = new GL.Texture( tex.width, tex.height, { type: type, format: tex.format, filter: gl.LINEAR } );
|
||||
|
||||
var shader = LGraphTextureCurve._shader;
|
||||
if (!shader) {
|
||||
shader = LGraphTextureCurve._shader = new GL.Shader( GL.Shader.SCREEN_VERTEX_SHADER, LGraphTextureCurve.pixel_shader );
|
||||
@@ -4315,7 +4599,7 @@ void main(void){\n\
|
||||
});
|
||||
|
||||
this.setOutputData(0, temp);
|
||||
};
|
||||
}
|
||||
|
||||
LGraphTextureCurve.prototype.sampleCurve = function(f,points)
|
||||
{
|
||||
@@ -4732,18 +5016,18 @@ void main(void){\n\
|
||||
|
||||
LGraphTexturePerlin.widgets_info = {
|
||||
precision: { widget: "combo", values: LGraphTexture.MODE_VALUES },
|
||||
width: { type: "Number", precision: 0, step: 1 },
|
||||
height: { type: "Number", precision: 0, step: 1 },
|
||||
octaves: { type: "Number", precision: 0, step: 1, min: 1, max: 50 }
|
||||
width: { type: "number", precision: 0, step: 1 },
|
||||
height: { type: "number", precision: 0, step: 1 },
|
||||
octaves: { type: "number", precision: 0, step: 1, min: 1, max: 50 }
|
||||
};
|
||||
|
||||
LGraphTexturePerlin.prototype.onGetInputs = function() {
|
||||
return [
|
||||
["seed", "Number"],
|
||||
["persistence", "Number"],
|
||||
["octaves", "Number"],
|
||||
["scale", "Number"],
|
||||
["amplitude", "Number"],
|
||||
["seed", "number"],
|
||||
["persistence", "number"],
|
||||
["octaves", "number"],
|
||||
["scale", "number"],
|
||||
["amplitude", "number"],
|
||||
["offset", "vec2"]
|
||||
];
|
||||
};
|
||||
@@ -4893,7 +5177,7 @@ void main(void){\n\
|
||||
this.addInput("v");
|
||||
this.addOutput("out", "Texture");
|
||||
this.properties = {
|
||||
code: "",
|
||||
code: LGraphTextureCanvas2D.default_code,
|
||||
width: 512,
|
||||
height: 512,
|
||||
clear: true,
|
||||
@@ -4902,17 +5186,20 @@ void main(void){\n\
|
||||
};
|
||||
this._func = null;
|
||||
this._temp_texture = null;
|
||||
this.compileCode();
|
||||
}
|
||||
|
||||
LGraphTextureCanvas2D.title = "Canvas2D";
|
||||
LGraphTextureCanvas2D.desc = "Executes Canvas2D code inside a texture or the viewport.";
|
||||
LGraphTextureCanvas2D.help = "Set width and height to 0 to match viewport size.";
|
||||
|
||||
LGraphTextureCanvas2D.default_code = "//vars: canvas,ctx,time\nctx.fillStyle='red';\nctx.fillRect(0,0,50,50);\n";
|
||||
|
||||
LGraphTextureCanvas2D.widgets_info = {
|
||||
precision: { widget: "combo", values: LGraphTexture.MODE_VALUES },
|
||||
code: { type: "code" },
|
||||
width: { type: "Number", precision: 0, step: 1 },
|
||||
height: { type: "Number", precision: 0, step: 1 }
|
||||
width: { type: "number", precision: 0, step: 1 },
|
||||
height: { type: "number", precision: 0, step: 1 }
|
||||
};
|
||||
|
||||
LGraphTextureCanvas2D.prototype.onPropertyChanged = function( name, value ) {
|
||||
|
||||
@@ -143,9 +143,7 @@
|
||||
if (callback) {
|
||||
callback(this);
|
||||
}
|
||||
that.trace(
|
||||
"Image loaded, size: " + that.img.width + "x" + that.img.height
|
||||
);
|
||||
console.log( "Image loaded, size: " + that.img.width + "x" + that.img.height );
|
||||
this.dirty = true;
|
||||
that.boxcolor = "#9F9";
|
||||
that.setDirtyCanvas(true);
|
||||
@@ -427,7 +425,7 @@
|
||||
if (name == "scale") {
|
||||
this.properties[name] = parseFloat(value);
|
||||
if (this.properties[name] == 0) {
|
||||
this.trace("Error in scale");
|
||||
console.error("Error in scale");
|
||||
this.properties[name] = 1.0;
|
||||
}
|
||||
} else {
|
||||
@@ -597,10 +595,23 @@
|
||||
ImageVideo.prototype.loadVideo = function(url) {
|
||||
this._video_url = url;
|
||||
|
||||
var pos = url.substr(0,10).indexOf(":");
|
||||
var protocol = "";
|
||||
if(pos != -1)
|
||||
protocol = url.substr(0,pos);
|
||||
|
||||
var host = "";
|
||||
if(protocol)
|
||||
{
|
||||
host = url.substr(0,url.indexOf("/",protocol.length + 3));
|
||||
host = host.substr(protocol.length+3);
|
||||
}
|
||||
|
||||
if (
|
||||
this.properties.use_proxy &&
|
||||
url.substr(0, 4) == "http" &&
|
||||
LiteGraph.proxy
|
||||
protocol &&
|
||||
LiteGraph.proxy &&
|
||||
host != location.host
|
||||
) {
|
||||
url = LiteGraph.proxy + url.substr(url.indexOf(":") + 3);
|
||||
}
|
||||
@@ -615,41 +626,38 @@
|
||||
var that = this;
|
||||
this._video.addEventListener("loadedmetadata", function(e) {
|
||||
//onload
|
||||
that.trace("Duration: " + this.duration + " seconds");
|
||||
that.trace("Size: " + this.videoWidth + "," + this.videoHeight);
|
||||
console.log("Duration: " + this.duration + " seconds");
|
||||
console.log("Size: " + this.videoWidth + "," + this.videoHeight);
|
||||
that.setDirtyCanvas(true);
|
||||
this.width = this.videoWidth;
|
||||
this.height = this.videoHeight;
|
||||
});
|
||||
this._video.addEventListener("progress", function(e) {
|
||||
//onload
|
||||
//that.trace("loading...");
|
||||
console.log("video loading...");
|
||||
});
|
||||
this._video.addEventListener("error", function(e) {
|
||||
console.log("Error loading video: " + this.src);
|
||||
that.trace("Error loading video: " + this.src);
|
||||
console.error("Error loading video: " + this.src);
|
||||
if (this.error) {
|
||||
switch (this.error.code) {
|
||||
case this.error.MEDIA_ERR_ABORTED:
|
||||
that.trace("You stopped the video.");
|
||||
console.error("You stopped the video.");
|
||||
break;
|
||||
case this.error.MEDIA_ERR_NETWORK:
|
||||
that.trace("Network error - please try again later.");
|
||||
console.error("Network error - please try again later.");
|
||||
break;
|
||||
case this.error.MEDIA_ERR_DECODE:
|
||||
that.trace("Video is broken..");
|
||||
console.error("Video is broken..");
|
||||
break;
|
||||
case this.error.MEDIA_ERR_SRC_NOT_SUPPORTED:
|
||||
that.trace(
|
||||
"Sorry, your browser can't play this video."
|
||||
);
|
||||
console.error("Sorry, your browser can't play this video.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
this._video.addEventListener("ended", function(e) {
|
||||
that.trace("Ended.");
|
||||
console.log("Video Ended.");
|
||||
this.play(); //loop
|
||||
});
|
||||
|
||||
@@ -666,7 +674,7 @@
|
||||
};
|
||||
|
||||
ImageVideo.prototype.play = function() {
|
||||
if (this._video) {
|
||||
if (this._video && this._video.videoWidth ) { //is loaded
|
||||
this._video.play();
|
||||
}
|
||||
};
|
||||
@@ -694,7 +702,7 @@
|
||||
if (!this._video) {
|
||||
return;
|
||||
}
|
||||
this.trace("Video paused");
|
||||
console.log("Video paused");
|
||||
this._video.pause();
|
||||
};
|
||||
|
||||
|
||||
@@ -110,6 +110,7 @@
|
||||
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);
|
||||
@@ -117,7 +118,7 @@
|
||||
this.addProperty("out_min", 0);
|
||||
this.addProperty("out_max", 1);
|
||||
|
||||
this.size = [80, 30];
|
||||
this.size = [120, 50];
|
||||
}
|
||||
|
||||
MathRange.title = "Range";
|
||||
@@ -151,10 +152,22 @@
|
||||
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._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) {
|
||||
@@ -223,6 +236,10 @@
|
||||
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];
|
||||
}
|
||||
|
||||
@@ -253,7 +270,22 @@
|
||||
|
||||
MathNoise.prototype.onExecute = function() {
|
||||
var f = this.getInputData(0) || 0;
|
||||
var r = MathNoise.getValue(f, this.properties.smooth);
|
||||
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;
|
||||
@@ -595,12 +627,14 @@
|
||||
|
||||
//Math operation
|
||||
function MathOperation() {
|
||||
this.addInput("A", "number");
|
||||
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"];
|
||||
@@ -627,11 +661,34 @@
|
||||
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) {
|
||||
this.properties["A"] = A;
|
||||
if ( A != null ) {
|
||||
if( A.constructor === Number )
|
||||
this.properties["A"] = A;
|
||||
} else {
|
||||
A = this.properties["A"];
|
||||
}
|
||||
@@ -642,38 +699,26 @@
|
||||
B = this.properties["B"];
|
||||
}
|
||||
|
||||
var result = 0;
|
||||
switch (this.properties.OP) {
|
||||
case "+":
|
||||
result = A + B;
|
||||
break;
|
||||
case "-":
|
||||
result = A - B;
|
||||
break;
|
||||
case "x":
|
||||
case "X":
|
||||
case "*":
|
||||
result = A * B;
|
||||
break;
|
||||
case "/":
|
||||
result = A / B;
|
||||
break;
|
||||
case "%":
|
||||
result = A % B;
|
||||
break;
|
||||
case "^":
|
||||
result = Math.pow(A, B);
|
||||
break;
|
||||
case "max":
|
||||
result = Math.max(A, B);
|
||||
break;
|
||||
case "min":
|
||||
result = Math.min(A, B);
|
||||
break;
|
||||
default:
|
||||
console.warn("Unknown operation: " + this.properties.OP);
|
||||
}
|
||||
this.setOutputData(0, result);
|
||||
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) {
|
||||
@@ -704,6 +749,7 @@
|
||||
title: "MIN()"
|
||||
});
|
||||
|
||||
|
||||
//Math compare
|
||||
function MathCompare() {
|
||||
this.addInput("A", "number");
|
||||
@@ -808,6 +854,7 @@
|
||||
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];
|
||||
}
|
||||
@@ -875,6 +922,37 @@
|
||||
|
||||
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");
|
||||
@@ -1247,141 +1325,4 @@
|
||||
|
||||
LiteGraph.registerNodeType("math3d/xyzw-to-vec4", Math3DXYZWToVec4);
|
||||
|
||||
//if glMatrix is installed...
|
||||
if (global.glMatrix) {
|
||||
function Math3DQuaternion() {
|
||||
this.addOutput("quat", "quat");
|
||||
this.properties = { x: 0, y: 0, z: 0, w: 1 };
|
||||
this._value = quat.create();
|
||||
}
|
||||
|
||||
Math3DQuaternion.title = "Quaternion";
|
||||
Math3DQuaternion.desc = "quaternion";
|
||||
|
||||
Math3DQuaternion.prototype.onExecute = function() {
|
||||
this._value[0] = this.properties.x;
|
||||
this._value[1] = this.properties.y;
|
||||
this._value[2] = this.properties.z;
|
||||
this._value[3] = this.properties.w;
|
||||
this.setOutputData(0, this._value);
|
||||
};
|
||||
|
||||
LiteGraph.registerNodeType("math3d/quaternion", Math3DQuaternion);
|
||||
|
||||
function Math3DRotation() {
|
||||
this.addInputs([["degrees", "number"], ["axis", "vec3"]]);
|
||||
this.addOutput("quat", "quat");
|
||||
this.properties = { angle: 90.0, axis: vec3.fromValues(0, 1, 0) };
|
||||
|
||||
this._value = quat.create();
|
||||
}
|
||||
|
||||
Math3DRotation.title = "Rotation";
|
||||
Math3DRotation.desc = "quaternion rotation";
|
||||
|
||||
Math3DRotation.prototype.onExecute = function() {
|
||||
var angle = this.getInputData(0);
|
||||
if (angle == null) {
|
||||
angle = this.properties.angle;
|
||||
}
|
||||
var axis = this.getInputData(1);
|
||||
if (axis == null) {
|
||||
axis = this.properties.axis;
|
||||
}
|
||||
|
||||
var R = quat.setAxisAngle(this._value, axis, angle * 0.0174532925);
|
||||
this.setOutputData(0, R);
|
||||
};
|
||||
|
||||
LiteGraph.registerNodeType("math3d/rotation", Math3DRotation);
|
||||
|
||||
//Math3D rotate vec3
|
||||
function Math3DRotateVec3() {
|
||||
this.addInputs([["vec3", "vec3"], ["quat", "quat"]]);
|
||||
this.addOutput("result", "vec3");
|
||||
this.properties = { vec: [0, 0, 1] };
|
||||
}
|
||||
|
||||
Math3DRotateVec3.title = "Rot. Vec3";
|
||||
Math3DRotateVec3.desc = "rotate a point";
|
||||
|
||||
Math3DRotateVec3.prototype.onExecute = function() {
|
||||
var vec = this.getInputData(0);
|
||||
if (vec == null) {
|
||||
vec = this.properties.vec;
|
||||
}
|
||||
var quat = this.getInputData(1);
|
||||
if (quat == null) {
|
||||
this.setOutputData(vec);
|
||||
} else {
|
||||
this.setOutputData(
|
||||
0,
|
||||
vec3.transformQuat(vec3.create(), vec, quat)
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
LiteGraph.registerNodeType("math3d/rotate_vec3", Math3DRotateVec3);
|
||||
|
||||
function Math3DMultQuat() {
|
||||
this.addInputs([["A", "quat"], ["B", "quat"]]);
|
||||
this.addOutput("A*B", "quat");
|
||||
|
||||
this._value = quat.create();
|
||||
}
|
||||
|
||||
Math3DMultQuat.title = "Mult. Quat";
|
||||
Math3DMultQuat.desc = "rotate quaternion";
|
||||
|
||||
Math3DMultQuat.prototype.onExecute = function() {
|
||||
var A = this.getInputData(0);
|
||||
if (A == null) {
|
||||
return;
|
||||
}
|
||||
var B = this.getInputData(1);
|
||||
if (B == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
var R = quat.multiply(this._value, A, B);
|
||||
this.setOutputData(0, R);
|
||||
};
|
||||
|
||||
LiteGraph.registerNodeType("math3d/mult-quat", Math3DMultQuat);
|
||||
|
||||
function Math3DQuatSlerp() {
|
||||
this.addInputs([
|
||||
["A", "quat"],
|
||||
["B", "quat"],
|
||||
["factor", "number"]
|
||||
]);
|
||||
this.addOutput("slerp", "quat");
|
||||
this.addProperty("factor", 0.5);
|
||||
|
||||
this._value = quat.create();
|
||||
}
|
||||
|
||||
Math3DQuatSlerp.title = "Quat Slerp";
|
||||
Math3DQuatSlerp.desc = "quaternion spherical interpolation";
|
||||
|
||||
Math3DQuatSlerp.prototype.onExecute = function() {
|
||||
var A = this.getInputData(0);
|
||||
if (A == null) {
|
||||
return;
|
||||
}
|
||||
var B = this.getInputData(1);
|
||||
if (B == null) {
|
||||
return;
|
||||
}
|
||||
var factor = this.properties.factor;
|
||||
if (this.getInputData(2) != null) {
|
||||
factor = this.getInputData(2);
|
||||
}
|
||||
|
||||
var R = quat.slerp(this._value, A, B, factor);
|
||||
this.setOutputData(0, R);
|
||||
};
|
||||
|
||||
LiteGraph.registerNodeType("math3d/quat-slerp", Math3DQuatSlerp);
|
||||
} //glMatrix
|
||||
})(this);
|
||||
|
||||
@@ -68,12 +68,22 @@
|
||||
function Math3DOperation() {
|
||||
this.addInput("A", "number,vec3");
|
||||
this.addInput("B", "number,vec3");
|
||||
this.addOutput("=", "vec3");
|
||||
this.addOutput("=", "number,vec3");
|
||||
this.addProperty("OP", "+", "enum", { values: Math3DOperation.values });
|
||||
this._result = vec3.create();
|
||||
}
|
||||
|
||||
Math3DOperation.values = ["+", "-", "*", "/", "%", "^", "max", "min"];
|
||||
Math3DOperation.values = ["+", "-", "*", "/", "%", "^", "max", "min","dot","cross"];
|
||||
|
||||
LiteGraph.registerSearchboxExtra("math3d/operation", "CROSS()", {
|
||||
properties: {"OP":"cross"},
|
||||
title: "CROSS()"
|
||||
});
|
||||
|
||||
LiteGraph.registerSearchboxExtra("math3d/operation", "DOT()", {
|
||||
properties: {"OP":"dot"},
|
||||
title: "DOT()"
|
||||
});
|
||||
|
||||
Math3DOperation.title = "Operation";
|
||||
Math3DOperation.desc = "Easy math 3D operators";
|
||||
@@ -135,6 +145,11 @@
|
||||
result[0] = Math.min(A[0],B[0]);
|
||||
result[1] = Math.min(A[1],B[1]);
|
||||
result[2] = Math.min(A[2],B[2]);
|
||||
case "dot":
|
||||
result = vec3.dot(A,B);
|
||||
break;
|
||||
case "cross":
|
||||
vec3.cross(result,A,B);
|
||||
break;
|
||||
default:
|
||||
console.warn("Unknown operation: " + this.properties.OP);
|
||||
@@ -352,6 +367,53 @@
|
||||
|
||||
LiteGraph.registerNodeType("math3d/rotation", Math3DRotation);
|
||||
|
||||
|
||||
function MathEulerToQuat() {
|
||||
this.addInput("euler", "vec3");
|
||||
this.addOutput("quat", "quat");
|
||||
this.properties = { euler:[0,0,0], use_yaw_pitch_roll: false };
|
||||
this._degs = vec3.create();
|
||||
this._value = quat.create();
|
||||
}
|
||||
|
||||
MathEulerToQuat.title = "Euler->Quat";
|
||||
MathEulerToQuat.desc = "Converts euler angles (in degrees) to quaternion";
|
||||
|
||||
MathEulerToQuat.prototype.onExecute = function() {
|
||||
var euler = this.getInputData(0);
|
||||
if (euler == null) {
|
||||
euler = this.properties.euler;
|
||||
}
|
||||
vec3.scale( this._degs, euler, DEG2RAD );
|
||||
if(this.properties.use_yaw_pitch_roll)
|
||||
this._degs = [this._degs[2],this._degs[0],this._degs[1]];
|
||||
var R = quat.fromEuler(this._value, this._degs);
|
||||
this.setOutputData(0, R);
|
||||
};
|
||||
|
||||
LiteGraph.registerNodeType("math3d/euler_to_quat", MathEulerToQuat);
|
||||
|
||||
function MathQuatToEuler() {
|
||||
this.addInput(["quat", "quat"]);
|
||||
this.addOutput("euler", "vec3");
|
||||
this._value = vec3.create();
|
||||
}
|
||||
|
||||
MathQuatToEuler.title = "Euler->Quat";
|
||||
MathQuatToEuler.desc = "Converts rotX,rotY,rotZ in degrees to quat";
|
||||
|
||||
MathQuatToEuler.prototype.onExecute = function() {
|
||||
var q = this.getInputData(0);
|
||||
if(!q)
|
||||
return;
|
||||
var R = quat.toEuler(this._value, q);
|
||||
vec3.scale( this._value, this._value, DEG2RAD );
|
||||
this.setOutputData(0, this._value);
|
||||
};
|
||||
|
||||
LiteGraph.registerNodeType("math3d/quat_to_euler", MathQuatToEuler);
|
||||
|
||||
|
||||
//Math3D rotate vec3
|
||||
function Math3DRotateVec3() {
|
||||
this.addInputs([["vec3", "vec3"], ["quat", "quat"]]);
|
||||
@@ -464,6 +526,21 @@
|
||||
var target_min = this.properties.target_min;
|
||||
var target_max = this.properties.target_max;
|
||||
|
||||
//swap to avoid errors
|
||||
/*
|
||||
if(range_min > range_max)
|
||||
{
|
||||
range_min = range_max;
|
||||
range_max = this.properties.range_min;
|
||||
}
|
||||
|
||||
if(target_min > target_max)
|
||||
{
|
||||
target_min = target_max;
|
||||
target_max = this.properties.target_min;
|
||||
}
|
||||
*/
|
||||
|
||||
for(var i = 0; i < 3; ++i)
|
||||
{
|
||||
var r = range_max[i] - range_min[i];
|
||||
|
||||
@@ -328,7 +328,7 @@
|
||||
MIDIEvent.commands_reversed[MIDIEvent.commands[i]] = i;
|
||||
}
|
||||
|
||||
//MIDI wrapper
|
||||
//MIDI wrapper, instantiate by MIDIIn and MIDIOut
|
||||
function MIDIInterface(on_ready, on_error) {
|
||||
if (!navigator.requestMIDIAccess) {
|
||||
this.error = "not suppoorted";
|
||||
@@ -347,9 +347,12 @@
|
||||
cc: []
|
||||
};
|
||||
|
||||
navigator
|
||||
.requestMIDIAccess()
|
||||
.then(this.onMIDISuccess.bind(this), this.onMIDIFailure.bind(this));
|
||||
this.input_ports = null;
|
||||
this.input_ports_info = [];
|
||||
this.output_ports = null;
|
||||
this.output_ports_info = [];
|
||||
|
||||
navigator.requestMIDIAccess().then(this.onMIDISuccess.bind(this), this.onMIDIFailure.bind(this));
|
||||
}
|
||||
|
||||
MIDIInterface.input = null;
|
||||
@@ -370,80 +373,34 @@
|
||||
MIDIInterface.prototype.updatePorts = function() {
|
||||
var midi = this.midi;
|
||||
this.input_ports = midi.inputs;
|
||||
this.input_ports_info = [];
|
||||
this.output_ports = midi.outputs;
|
||||
this.output_ports_info = [];
|
||||
|
||||
var num = 0;
|
||||
|
||||
var it = this.input_ports.values();
|
||||
var it_value = it.next();
|
||||
while (it_value && it_value.done === false) {
|
||||
var port_info = it_value.value;
|
||||
console.log(
|
||||
"Input port [type:'" +
|
||||
port_info.type +
|
||||
"'] id:'" +
|
||||
port_info.id +
|
||||
"' manufacturer:'" +
|
||||
port_info.manufacturer +
|
||||
"' name:'" +
|
||||
port_info.name +
|
||||
"' version:'" +
|
||||
port_info.version +
|
||||
"'"
|
||||
);
|
||||
this.input_ports_info.push(port_info);
|
||||
console.log( "Input port [type:'" + port_info.type + "'] id:'" + port_info.id + "' manufacturer:'" + port_info.manufacturer + "' name:'" + port_info.name + "' version:'" + port_info.version + "'" );
|
||||
num++;
|
||||
it_value = it.next();
|
||||
}
|
||||
this.num_input_ports = num;
|
||||
|
||||
num = 0;
|
||||
this.output_ports = midi.outputs;
|
||||
var it = this.output_ports.values();
|
||||
var it_value = it.next();
|
||||
while (it_value && it_value.done === false) {
|
||||
var port_info = it_value.value;
|
||||
console.log(
|
||||
"Output port [type:'" +
|
||||
port_info.type +
|
||||
"'] id:'" +
|
||||
port_info.id +
|
||||
"' manufacturer:'" +
|
||||
port_info.manufacturer +
|
||||
"' name:'" +
|
||||
port_info.name +
|
||||
"' version:'" +
|
||||
port_info.version +
|
||||
"'"
|
||||
);
|
||||
this.output_ports_info.push(port_info);
|
||||
console.log( "Output port [type:'" + port_info.type + "'] id:'" + port_info.id + "' manufacturer:'" + port_info.manufacturer + "' name:'" + port_info.name + "' version:'" + port_info.version + "'" );
|
||||
num++;
|
||||
it_value = it.next();
|
||||
}
|
||||
this.num_output_ports = num;
|
||||
|
||||
/* OLD WAY
|
||||
for (var i = 0; i < this.input_ports.size; ++i) {
|
||||
var input = this.input_ports.get(i);
|
||||
if(!input)
|
||||
continue; //sometimes it is null?!
|
||||
console.log( "Input port [type:'" + input.type + "'] id:'" + input.id +
|
||||
"' manufacturer:'" + input.manufacturer + "' name:'" + input.name +
|
||||
"' version:'" + input.version + "'" );
|
||||
num++;
|
||||
}
|
||||
this.num_input_ports = num;
|
||||
|
||||
|
||||
num = 0;
|
||||
this.output_ports = midi.outputs;
|
||||
for (var i = 0; i < this.output_ports.size; ++i) {
|
||||
var output = this.output_ports.get(i);
|
||||
if(!output)
|
||||
continue;
|
||||
console.log( "Output port [type:'" + output.type + "'] id:'" + output.id +
|
||||
"' manufacturer:'" + output.manufacturer + "' name:'" + output.name +
|
||||
"' version:'" + output.version + "'" );
|
||||
num++;
|
||||
}
|
||||
this.num_output_ports = num;
|
||||
*/
|
||||
};
|
||||
|
||||
MIDIInterface.prototype.onMIDIFailure = function(msg) {
|
||||
@@ -493,7 +450,7 @@
|
||||
return;
|
||||
}
|
||||
|
||||
var output_port = this.output_ports.get("output-" + port);
|
||||
var output_port = this.output_ports_info[port];//this.output_ports.get("output-" + port);
|
||||
if (!output_port) {
|
||||
return;
|
||||
}
|
||||
@@ -540,10 +497,9 @@
|
||||
|
||||
if (name == "port") {
|
||||
var values = {};
|
||||
for (var i = 0; i < this._midi.input_ports.size; ++i) {
|
||||
var input = this._midi.input_ports.get("input-" + i);
|
||||
values[i] =
|
||||
i + ".- " + input.name + " version:" + input.version;
|
||||
for (var i = 0; i < this._midi.input_ports_info.length; ++i) {
|
||||
var input = this._midi.input_ports_info[i];
|
||||
values[i] = i + ".- " + input.name + " version:" + input.version;
|
||||
}
|
||||
return { type: "enum", values: values };
|
||||
}
|
||||
@@ -641,7 +597,10 @@
|
||||
var that = this;
|
||||
new MIDIInterface(function(midi) {
|
||||
that._midi = midi;
|
||||
that.widget.options.values = that.getMIDIOutputs();
|
||||
});
|
||||
this.widget = this.addWidget("combo","Device",this.properties.port,{ property: "port", values: this.getMIDIOutputs.bind(this) });
|
||||
this.size = [340,60];
|
||||
}
|
||||
|
||||
LGMIDIOut.MIDIInterface = MIDIInterface;
|
||||
@@ -650,21 +609,33 @@
|
||||
LGMIDIOut.desc = "Sends MIDI to output channel";
|
||||
LGMIDIOut.color = MIDI_COLOR;
|
||||
|
||||
LGMIDIOut.prototype.getPropertyInfo = function(name) {
|
||||
LGMIDIOut.prototype.onGetPropertyInfo = function(name) {
|
||||
if (!this._midi) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (name == "port") {
|
||||
var values = {};
|
||||
for (var i = 0; i < this._midi.output_ports.size; ++i) {
|
||||
var output = this._midi.output_ports.get(i);
|
||||
values[i] =
|
||||
i + ".- " + output.name + " version:" + output.version;
|
||||
}
|
||||
var values = this.getMIDIOutputs();
|
||||
return { type: "enum", values: values };
|
||||
}
|
||||
};
|
||||
LGMIDIOut.default_ports = {0:"unknown"};
|
||||
|
||||
LGMIDIOut.prototype.getMIDIOutputs = function()
|
||||
{
|
||||
var values = {};
|
||||
if(!this._midi)
|
||||
return LGMIDIOut.default_ports;
|
||||
if(this._midi.output_ports_info)
|
||||
for (var i = 0; i < this._midi.output_ports_info.length; ++i) {
|
||||
var output = this._midi.output_ports_info[i];
|
||||
if(!output)
|
||||
continue;
|
||||
var name = i + ".- " + output.name + " version:" + output.version;
|
||||
values[i] = name;
|
||||
}
|
||||
return values;
|
||||
}
|
||||
|
||||
LGMIDIOut.prototype.onAction = function(event, midi_event) {
|
||||
//console.log(midi_event);
|
||||
@@ -672,7 +643,7 @@
|
||||
return;
|
||||
}
|
||||
if (event == "send") {
|
||||
this._midi.sendMIDI(this.port, midi_event);
|
||||
this._midi.sendMIDI(this.properties.port, midi_event);
|
||||
}
|
||||
this.trigger("midi", midi_event);
|
||||
};
|
||||
@@ -687,6 +658,7 @@
|
||||
|
||||
LiteGraph.registerNodeType("midi/output", LGMIDIOut);
|
||||
|
||||
|
||||
function LGMIDIShow() {
|
||||
this.addInput("on_midi", LiteGraph.EVENT);
|
||||
this._str = "";
|
||||
@@ -910,6 +882,24 @@
|
||||
this.properties.value1 = (v | 0) % 255;
|
||||
}
|
||||
break;
|
||||
case "cmd":
|
||||
var v = this.getInputData(i);
|
||||
if (v != null) {
|
||||
this.properties.cmd = v;
|
||||
}
|
||||
break;
|
||||
case "value1":
|
||||
var v = this.getInputData(i);
|
||||
if (v != null) {
|
||||
this.properties.value1 = Math.clamp(v|0,0,127);
|
||||
}
|
||||
break;
|
||||
case "value2":
|
||||
var v = this.getInputData(i);
|
||||
if (v != null) {
|
||||
this.properties.value2 = Math.clamp(v|0,0,127);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -978,7 +968,7 @@
|
||||
};
|
||||
|
||||
LGMIDIEvent.prototype.onGetInputs = function() {
|
||||
return [["note", "number"]];
|
||||
return [["cmd", "number"],["note", "number"],["value1", "number"],["value2", "number"]];
|
||||
};
|
||||
|
||||
LGMIDIEvent.prototype.onGetOutputs = function() {
|
||||
@@ -1235,6 +1225,122 @@
|
||||
|
||||
LiteGraph.registerNodeType("midi/quantize", LGMIDIQuantize);
|
||||
|
||||
function LGMIDIFromFile() {
|
||||
this.properties = {
|
||||
url: "",
|
||||
autoplay: true
|
||||
};
|
||||
|
||||
this.addInput("play", LiteGraph.ACTION);
|
||||
this.addInput("pause", LiteGraph.ACTION);
|
||||
this.addOutput("note", LiteGraph.EVENT);
|
||||
this._midi = null;
|
||||
this._current_time = 0;
|
||||
this._playing = false;
|
||||
|
||||
if (typeof MidiParser == "undefined") {
|
||||
console.error(
|
||||
"midi-parser.js not included, LGMidiPlay requires that library: https://raw.githubusercontent.com/colxi/midi-parser-js/master/src/main.js"
|
||||
);
|
||||
this.boxcolor = "red";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
LGMIDIFromFile.title = "MIDI fromFile";
|
||||
LGMIDIFromFile.desc = "Plays a MIDI file";
|
||||
LGMIDIFromFile.color = MIDI_COLOR;
|
||||
|
||||
LGMIDIFromFile.prototype.onAction = function( name )
|
||||
{
|
||||
if(name == "play")
|
||||
this.play();
|
||||
else if(name == "pause")
|
||||
this._playing = !this._playing;
|
||||
}
|
||||
|
||||
LGMIDIFromFile.prototype.onPropertyChanged = function(name,value)
|
||||
{
|
||||
if(name == "url")
|
||||
this.loadMIDIFile(value);
|
||||
}
|
||||
|
||||
LGMIDIFromFile.prototype.onExecute = function() {
|
||||
if(!this._midi)
|
||||
return;
|
||||
|
||||
if(!this._playing)
|
||||
return;
|
||||
|
||||
this._current_time += this.graph.elapsed_time;
|
||||
var current_time = this._current_time * 100;
|
||||
|
||||
for(var i = 0; i < this._midi.tracks; ++i)
|
||||
{
|
||||
var track = this._midi.track[i];
|
||||
if(!track._last_pos)
|
||||
{
|
||||
track._last_pos = 0;
|
||||
track._time = 0;
|
||||
}
|
||||
|
||||
var elem = track.event[ track._last_pos ];
|
||||
if(elem && (track._time + elem.deltaTime) <= current_time )
|
||||
{
|
||||
track._last_pos++;
|
||||
track._time += elem.deltaTime;
|
||||
|
||||
if(elem.data)
|
||||
{
|
||||
var midi_cmd = elem.type << 4 + elem.channel;
|
||||
var midi_event = new MIDIEvent();
|
||||
midi_event.setup([midi_cmd, elem.data[0], elem.data[1]]);
|
||||
this.trigger("note", midi_event);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
LGMIDIFromFile.prototype.play = function()
|
||||
{
|
||||
this._playing = true;
|
||||
this._current_time = 0;
|
||||
if(!this._midi)
|
||||
return;
|
||||
|
||||
for(var i = 0; i < this._midi.tracks; ++i)
|
||||
{
|
||||
var track = this._midi.track[i];
|
||||
track._last_pos = 0;
|
||||
track._time = 0;
|
||||
}
|
||||
}
|
||||
|
||||
LGMIDIFromFile.prototype.loadMIDIFile = function(url)
|
||||
{
|
||||
var that = this;
|
||||
LiteGraph.fetchFile( url, "arraybuffer", function(data)
|
||||
{
|
||||
that.boxcolor = "#AFA";
|
||||
that._midi = MidiParser.parse( new Uint8Array(data) );
|
||||
if(that.properties.autoplay)
|
||||
that.play();
|
||||
}, function(err){
|
||||
that.boxcolor = "#FAA";
|
||||
that._midi = null;
|
||||
});
|
||||
}
|
||||
|
||||
LGMIDIFromFile.prototype.onDropFile = function(file)
|
||||
{
|
||||
this.properties.url = "";
|
||||
this.loadMIDIFile( file );
|
||||
}
|
||||
|
||||
LiteGraph.registerNodeType("midi/fromFile", LGMIDIFromFile);
|
||||
|
||||
|
||||
function LGMIDIPlay() {
|
||||
this.properties = {
|
||||
volume: 0.5,
|
||||
|
||||
@@ -86,10 +86,10 @@
|
||||
this._ws.onmessage = function(e) {
|
||||
that.boxcolor = "#AFA";
|
||||
var data = JSON.parse(e.data);
|
||||
if (data.room && data.room != this.properties.room) {
|
||||
if (data.room && data.room != that.properties.room) {
|
||||
return;
|
||||
}
|
||||
if (e.data.type == 1) {
|
||||
if (data.type == 1) {
|
||||
if (
|
||||
data.data.object_class &&
|
||||
LiteGraph[data.data.object_class]
|
||||
@@ -105,7 +105,7 @@
|
||||
that.triggerSlot(0, data.data);
|
||||
}
|
||||
} else {
|
||||
that._last_received_data[e.data.channel || 0] = data.data;
|
||||
that._last_received_data[data.channel || 0] = data.data;
|
||||
}
|
||||
};
|
||||
this._ws.onerror = function(e) {
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
return String(a);
|
||||
}
|
||||
|
||||
LiteGraph.wrapFunctionAsNode("string/toString", compare, ["*"], "String");
|
||||
LiteGraph.wrapFunctionAsNode("string/toString", compare, [""], "String");
|
||||
|
||||
function compare(a, b) {
|
||||
return a == b;
|
||||
@@ -15,8 +15,8 @@
|
||||
LiteGraph.wrapFunctionAsNode(
|
||||
"string/compare",
|
||||
compare,
|
||||
["String", "String"],
|
||||
"Boolean"
|
||||
["string", "string"],
|
||||
"boolean"
|
||||
);
|
||||
|
||||
function concatenate(a, b) {
|
||||
@@ -32,8 +32,8 @@
|
||||
LiteGraph.wrapFunctionAsNode(
|
||||
"string/concatenate",
|
||||
concatenate,
|
||||
["String", "String"],
|
||||
"String"
|
||||
["string", "string"],
|
||||
"string"
|
||||
);
|
||||
|
||||
function contains(a, b) {
|
||||
@@ -46,8 +46,8 @@
|
||||
LiteGraph.wrapFunctionAsNode(
|
||||
"string/contains",
|
||||
contains,
|
||||
["String", "String"],
|
||||
"Boolean"
|
||||
["string", "string"],
|
||||
"boolean"
|
||||
);
|
||||
|
||||
function toUpperCase(a) {
|
||||
@@ -60,22 +60,33 @@
|
||||
LiteGraph.wrapFunctionAsNode(
|
||||
"string/toUpperCase",
|
||||
toUpperCase,
|
||||
["String"],
|
||||
"String"
|
||||
["string"],
|
||||
"string"
|
||||
);
|
||||
|
||||
function split(a, b) {
|
||||
if (a != null && a.constructor === String) {
|
||||
return a.split(b || " ");
|
||||
}
|
||||
return [a];
|
||||
function split(str, separator) {
|
||||
if(separator == null)
|
||||
separator = this.properties.separator;
|
||||
if (str == null )
|
||||
return [];
|
||||
if( str.constructor === String )
|
||||
return str.split(separator || " ");
|
||||
else if( str.constructor === Array )
|
||||
{
|
||||
var r = [];
|
||||
for(var i = 0; i < str.length; ++i)
|
||||
r[i] = str[i].split(separator || " ");
|
||||
return r;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
LiteGraph.wrapFunctionAsNode(
|
||||
"string/split",
|
||||
toUpperCase,
|
||||
["String", "String"],
|
||||
"Array"
|
||||
split,
|
||||
["string,array", "string"],
|
||||
"array",
|
||||
{ separator: "," }
|
||||
);
|
||||
|
||||
function toFixed(a) {
|
||||
@@ -88,8 +99,39 @@
|
||||
LiteGraph.wrapFunctionAsNode(
|
||||
"string/toFixed",
|
||||
toFixed,
|
||||
["Number"],
|
||||
"String",
|
||||
["number"],
|
||||
"string",
|
||||
{ precision: 0 }
|
||||
);
|
||||
|
||||
|
||||
function StringToTable() {
|
||||
this.addInput("", "string");
|
||||
this.addOutput("table", "table");
|
||||
this.addOutput("rows", "number");
|
||||
this.addProperty("value", "");
|
||||
this.addProperty("separator", ",");
|
||||
this._table = null;
|
||||
}
|
||||
|
||||
StringToTable.title = "toTable";
|
||||
StringToTable.desc = "Splits a string to table";
|
||||
|
||||
StringToTable.prototype.onExecute = function() {
|
||||
var input = this.getInputData(0);
|
||||
if(!input)
|
||||
return;
|
||||
var separator = this.properties.separator || ",";
|
||||
if(input != this._str || separator != this._last_separator )
|
||||
{
|
||||
this._last_separator = separator;
|
||||
this._str = input;
|
||||
this._table = input.split("\n").map(function(a){ return a.trim().split(separator)});
|
||||
}
|
||||
this.setOutputData(0, this._table );
|
||||
this.setOutputData(1, this._table ? this._table.length : 0 );
|
||||
};
|
||||
|
||||
LiteGraph.registerNodeType("string/toTable", StringToTable);
|
||||
|
||||
})(this);
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
../src/nodes/logic.js
|
||||
../src/nodes/graphics.js
|
||||
../src/nodes/gltextures.js
|
||||
../src/nodes/glshaders.js
|
||||
../src/nodes/geometry.js
|
||||
../src/nodes/glfx.js
|
||||
../src/nodes/midi.js
|
||||
|
||||
@@ -7,4 +7,4 @@ app.use('/external', express.static('external'))
|
||||
app.use('/demo', express.static('demo'))
|
||||
app.use('/', express.static('demo'))
|
||||
|
||||
app.listen(80, () => console.log('Example app listening on port 80!'))
|
||||
app.listen(8000, () => console.log('Example app listening on port 8000!'))
|
||||
|
||||