diff --git a/.npmrc b/.npmrc deleted file mode 100755 index 9cf949503..000000000 --- a/.npmrc +++ /dev/null @@ -1 +0,0 @@ -package-lock=false \ No newline at end of file diff --git a/.vscode/extensions.json b/.vscode/extensions.json deleted file mode 100755 index 62271a5d0..000000000 --- a/.vscode/extensions.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - // See http://go.microsoft.com/fwlink/?LinkId=827846 to learn about workspace recommendations. - // Extension identifier format: ${publisher}.${name}. Example: vscode.csharp - - // List of extensions which should be recommended for users of this workspace. - "recommendations": ["esbenp.prettier-vscode", "dbaeumer.vscode-eslint"], - // List of extensions recommended by VS Code that should not be recommended for users of this workspace. - "unwantedRecommendations": [] -} diff --git a/css/litegraph-editor.css b/css/litegraph-editor.css deleted file mode 100755 index 130ac0b97..000000000 --- a/css/litegraph-editor.css +++ /dev/null @@ -1,214 +0,0 @@ -.litegraph-editor { - width: 100%; - height: 100%; - margin: 0; - padding: 0; - - background-color: #333; - color: #eee; - font: 14px Tahoma; - - position: relative; -} - -.litegraph-editor h1 { - font-family: "Metro Light", Tahoma; - color: #ddd; - font-size: 28px; - padding-left: 10px; - /*text-shadow: 0 1px 1px #333, 0 -1px 1px #777;*/ - margin: 0; - font-weight: normal; -} - -.litegraph-editor h1 span { - font-family: "Arial"; - font-size: 14px; - font-weight: normal; - color: #aaa; -} - -.litegraph-editor h2 { - font-family: "Metro Light"; - padding: 5px; - margin-left: 10px; -} - -.litegraph-editor * { - box-sizing: border-box; - -moz-box-sizing: border-box; -} - -.litegraph-editor .content { - position: relative; - width: 100%; - height: calc(100% - 80px); - background-color: #1a1a1a; -} - -.litegraph-editor .header, -.litegraph-editor .footer { - position: relative; - height: 40px; - background-color: #333; - /*border-radius: 10px 10px 0 0;*/ -} - -.litegraph-editor .tools, -.litegraph-editor .tools-left, -.litegraph-editor .tools-right { - position: absolute; - top: 2px; - right: 0px; - vertical-align: top; - - margin: 2px 5px 0 0px; -} - -.litegraph-editor .tools-left { - right: auto; - left: 4px; -} - -.litegraph-editor .footer { - height: 40px; - position: relative; - /*border-radius: 0 0 10px 10px;*/ -} - -.litegraph-editor .miniwindow { - background-color: #333; - border: 1px solid #111; -} - -.litegraph-editor .miniwindow .corner-button { - position: absolute; - top: 2px; - right: 2px; - font-family: "Tahoma"; - font-size: 14px; - color: #aaa; - cursor: pointer; -} - -/* BUTTONS **********************/ - -.litegraph-editor .btn { - /*font-family: "Metro Light";*/ - color: #ccc; - font-size: 20px; - min-width: 30px; - /*border-radius: 0.3em;*/ - border: 0 solid #666; - background-color: #3f3f3f; - /*box-shadow: 0 0 3px black;*/ - padding: 4px 10px; - cursor: pointer; - transition: all 1s; - -moz-transition: all 1s; - -webkit-transition: all 0.4s; -} - -.litegraph-editor button:hover { - background-color: #999; - color: #fff; - transition: all 1s; - -moz-transition: all 1s; - -webkit-transition: all 0.4s; -} - -.litegraph-editor button:active { - background-color: white; -} - -.litegraph-editor button.fixed { - position: absolute; - top: 5px; - right: 5px; - font-size: 1.2em; -} - -.litegraph-editor button img { - margin: -4px; - vertical-align: top; - opacity: 0.8; - transition: all 1s; -} - -.litegraph-editor button:hover img { - opacity: 1; -} - -.litegraph-editor .header button { - height: 32px; - vertical-align: top; -} - -.litegraph-editor .footer button { - /*font-size: 16px;*/ -} - -.litegraph-editor .toolbar-widget { - display: inline-block; -} - -.litegraph-editor .editor-area { - width: 100%; - height: 100%; -} - -/* METER *********************/ - -.litegraph-editor .loadmeter { - font-family: "Tahoma"; - color: #aaa; - font-size: 12px; - border-radius: 2px; - width: 130px; - vertical-align: top; -} - -.litegraph-editor .strong { - vertical-align: top; - padding: 3px; - width: 30px; - display: inline-block; - line-height: 8px; -} - -.litegraph-editor .cpuload .bgload, -.litegraph-editor .gpuload .bgload { - display: inline-block; - width: 90px; - height: 15px; - background-image: url("../editor/imgs/load-progress-empty.png"); -} - -.litegraph-editor .cpuload .fgload, -.litegraph-editor .gpuload .fgload { - display: inline-block; - width: 4px; - height: 15px; - max-width: 90px; - background-image: url("../editor/imgs/load-progress-full.png"); -} - -.litegraph-editor textarea.code, .litegraph-editor div.code { - height: 100%; - width: 100%; - background-color: black; - padding: 4px; - font: 16px monospace; - overflow: auto; - resize: none; - outline: none; - color: #DDD; -} - -.litegraph-editor .codeflask { - background-color: #2a2a2a; -} - -.litegraph-editor .codeflask textarea { - opacity: 0; -} \ No newline at end of file diff --git a/doc/api.js b/doc/api.js deleted file mode 100755 index e7accb06a..000000000 --- a/doc/api.js +++ /dev/null @@ -1,14 +0,0 @@ -YUI.add("yuidoc-meta", function(Y) { - Y.YUIDoc = { meta: { - "classes": [ - "ContextMenu", - "LGraph", - "LGraphCanvas", - "LGraphNode", - "LiteGraph" - ], - "modules": [], - "allModules": [], - "elements": [] -} }; -}); \ No newline at end of file diff --git a/doc/assets/css/external-small.png b/doc/assets/css/external-small.png deleted file mode 100755 index 759a1cdcb..000000000 Binary files a/doc/assets/css/external-small.png and /dev/null differ diff --git a/doc/assets/css/logo.png b/doc/assets/css/logo.png deleted file mode 100755 index c82444af9..000000000 Binary files a/doc/assets/css/logo.png and /dev/null differ diff --git a/doc/assets/css/main.css b/doc/assets/css/main.css deleted file mode 100755 index cdfe209e8..000000000 --- a/doc/assets/css/main.css +++ /dev/null @@ -1,783 +0,0 @@ -/* -Font sizes for all selectors other than the body are given in percentages, -with 100% equal to 13px. To calculate a font size percentage, multiply the -desired size in pixels by 7.6923076923. - -Here's a quick lookup table: - -10px - 76.923% -11px - 84.615% -12px - 92.308% -13px - 100% -14px - 107.692% -15px - 115.385% -16px - 123.077% -17px - 130.769% -18px - 138.462% -19px - 146.154% -20px - 153.846% -*/ - -html { - background: #fff; - color: #333; - overflow-y: scroll; -} - -body { - /*font: 13px/1.4 'Lucida Grande', 'Lucida Sans Unicode', 'DejaVu Sans', 'Bitstream Vera Sans', 'Helvetica', 'Arial', sans-serif;*/ - font: 13px/1.4 'Helvetica', 'Arial', sans-serif; - margin: 0; - padding: 0; -} - -/* -- Links ----------------------------------------------------------------- */ -a { - color: #356de4; - text-decoration: none; -} - -.hidden { - display: none; -} - -a:hover { text-decoration: underline; } - -/* "Jump to Table of Contents" link is shown to assistive tools, but hidden from - sight until it's focused. */ -.jump { - position: absolute; - padding: 3px 6px; - left: -99999px; - top: 0; -} - -.jump:focus { left: 40%; } - -/* -- Paragraphs ------------------------------------------------------------ */ -p { margin: 1.3em 0; } -dd p, td p { margin-bottom: 0; } -dd p:first-child, td p:first-child { margin-top: 0; } - -/* -- Headings -------------------------------------------------------------- */ -h1, h2, h3, h4, h5, h6 { - color: #D98527;/*was #f80*/ - font-family: 'Trebuchet MS', sans-serif; - font-weight: bold; - line-height: 1.1; - margin: 1.1em 0 0.5em; -} - -h1 { - font-size: 184.6%; - color: #30418C; - margin: 0.75em 0 0.5em; -} - -h2 { - font-size: 153.846%; - color: #E48A2B; -} - -h3 { font-size: 138.462%; } - -h4 { - border-bottom: 1px solid #DBDFEA; - color: #E48A2B; - font-size: 115.385%; - font-weight: normal; - padding-bottom: 2px; -} - -h5, h6 { font-size: 107.692%; } - -/* -- Code and examples ----------------------------------------------------- */ -code, kbd, pre, samp { - font-family: Menlo, Monaco, 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace; - font-size: 92.308%; - line-height: 1.35; -} - -p code, p kbd, p samp, li code { - background: #FCFBFA; - border: 1px solid #EFEEED; - padding: 0 3px; -} - -a code, a kbd, a samp, -pre code, pre kbd, pre samp, -table code, table kbd, table samp, -.intro code, .intro kbd, .intro samp, -.toc code, .toc kbd, .toc samp { - background: none; - border: none; - padding: 0; -} - -pre.code, pre.terminal, pre.cmd { - overflow-x: auto; - *overflow-x: scroll; - padding: 0.3em 0.6em; -} - -pre.code { - background: #FCFBFA; - border: 1px solid #EFEEED; - border-left-width: 5px; -} - -pre.terminal, pre.cmd { - background: #F0EFFC; - border: 1px solid #D0CBFB; - border-left: 5px solid #D0CBFB; -} - -/* Don't reduce the font size of // elements inside
-   blocks. */
-pre code, pre kbd, pre samp { font-size: 100%; }
-
-/* Used to denote text that shouldn't be selectable, such as line numbers or
-   shell prompts. Guess which browser this doesn't work in. */
-.noselect {
-    -moz-user-select: -moz-none;
-    -khtml-user-select: none;
-    -webkit-user-select: none;
-    -o-user-select: none;
-    user-select: none;
-}
-
-/* -- Lists ----------------------------------------------------------------- */
-dd { margin: 0.2em 0 0.7em 1em; }
-dl { margin: 1em 0; }
-dt { font-weight: bold; }
-
-/* -- Tables ---------------------------------------------------------------- */
-caption, th { text-align: left; }
-
-table {
-    border-collapse: collapse;
-    width: 100%;
-}
-
-td, th {
-    border: 1px solid #fff;
-    padding: 5px 12px;
-    vertical-align: top;
-}
-
-td { background: #E6E9F5; }
-td dl { margin: 0; }
-td dl dl { margin: 1em 0; }
-td pre:first-child { margin-top: 0; }
-
-th {
-    background: #D2D7E6;/*#97A0BF*/
-    border-bottom: none;
-    border-top: none;
-    color: #000;/*#FFF1D5*/
-    font-family: 'Trebuchet MS', sans-serif;
-    font-weight: bold;
-    line-height: 1.3;
-    white-space: nowrap;
-}
-
-
-/* -- Layout and Content ---------------------------------------------------- */
-#doc {
-    margin: auto;
-    min-width: 1024px;
-}
-
-.content { padding: 0 20px 0 25px; }
-
-.sidebar {
-    padding: 0 15px 0 10px;
-}
-#bd {
-    padding: 7px 0 130px;
-    position: relative;
-    width: 99%;
-}
-
-/* -- Table of Contents ----------------------------------------------------- */
-
-/* The #toc id refers to the single global table of contents, while the .toc
-   class refers to generic TOC lists that could be used throughout the page. */
-
-.toc code, .toc kbd, .toc samp { font-size: 100%; }
-.toc li { font-weight: bold; }
-.toc li li { font-weight: normal; }
-
-/* -- Intro and Example Boxes ----------------------------------------------- */
-/*
-.intro, .example { margin-bottom: 2em; }
-.example {
-    -moz-border-radius: 4px;
-    -webkit-border-radius: 4px;
-    border-radius: 4px;
-    -moz-box-shadow: 0 0 5px #bfbfbf;
-    -webkit-box-shadow: 0 0 5px #bfbfbf;
-    box-shadow: 0 0 5px #bfbfbf;
-    padding: 1em;
-}
-.intro {
-    background: none repeat scroll 0 0 #F0F1F8; border: 1px solid #D4D8EB; padding: 0 1em;
-}
-*/
-
-/* -- Other Styles ---------------------------------------------------------- */
-
-/* These are probably YUI-specific, and should be moved out of Selleck's default
-   theme. */
-
-.button {
-    border: 1px solid #dadada;
-    -moz-border-radius: 3px;
-    -webkit-border-radius: 3px;
-    border-radius: 3px;
-    color: #444;
-    display: inline-block;
-    font-family: Helvetica, Arial, sans-serif;
-    font-size: 92.308%;
-    font-weight: bold;
-    padding: 4px 13px 3px;
-    -moz-text-shadow: 1px 1px 0 #fff;
-    -webkit-text-shadow: 1px 1px 0 #fff;
-    text-shadow: 1px 1px 0 #fff;
-    white-space: nowrap;
-
-    background: #EFEFEF; /* old browsers */
-    background: -moz-linear-gradient(top, #f5f5f5 0%, #efefef 50%, #e5e5e5 51%, #dfdfdf 100%); /* firefox */
-    background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#f5f5f5), color-stop(50%,#efefef), color-stop(51%,#e5e5e5), color-stop(100%,#dfdfdf)); /* webkit */
-    filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#f5f5f5', endColorstr='#dfdfdf',GradientType=0 ); /* ie */
-}
-
-.button:hover {
-    border-color: #466899;
-    color: #fff;
-    text-decoration: none;
-    -moz-text-shadow: 1px 1px 0 #222;
-    -webkit-text-shadow: 1px 1px 0 #222;
-    text-shadow: 1px 1px 0 #222;
-
-    background: #6396D8; /* old browsers */
-    background: -moz-linear-gradient(top, #6396D8 0%, #5A83BC 50%, #547AB7 51%, #466899 100%); /* firefox */
-    background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#6396D8), color-stop(50%,#5A83BC), color-stop(51%,#547AB7), color-stop(100%,#466899)); /* webkit */
-    filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#6396D8', endColorstr='#466899',GradientType=0 ); /* ie */
-}
-
-.newwindow { text-align: center; }
-
-.header .version em {
-    display: block;
-    text-align: right;
-}
-
-
-#classdocs .item {
-    border-bottom: 1px solid #466899;
-    margin: 1em 0;
-    padding: 1.5em;
-}
-
-#classdocs .item .params p,
-    #classdocs .item .returns p,{
-    display: inline;
-}
-
-#classdocs .item em code, #classdocs .item em.comment {
-    color: green;
-}
-
-#classdocs .item em.comment a {
-    color: green;
-    text-decoration: underline;
-}
-
-#classdocs .foundat {
-    font-size: 11px;
-    font-style: normal;
-}
-
-.attrs .emits {
-    margin-left: 2em;
-    padding: .5em;
-    border-left: 1px dashed #ccc;
-}
-
-abbr {
-    border-bottom: 1px dashed #ccc;
-    font-size: 80%;
-    cursor: help;
-}
-
-.prettyprint li.L0, 
-.prettyprint li.L1, 
-.prettyprint li.L2, 
-.prettyprint li.L3, 
-.prettyprint li.L5, 
-.prettyprint li.L6, 
-.prettyprint li.L7, 
-.prettyprint li.L8 {
-    list-style: decimal;
-}
-
-ul li p {
-    margin-top: 0;
-}
-
-.method .name {
-    font-size: 110%;
-}
-
-.apidocs .methods .extends .method,
-.apidocs .properties .extends .property,
-.apidocs .attrs .extends .attr,
-.apidocs .events .extends .event {
-    font-weight: bold;
-}
-
-.apidocs .methods .extends .inherited,
-.apidocs .properties .extends .inherited,
-.apidocs .attrs .extends .inherited,
-.apidocs .events .extends .inherited {
-    font-weight: normal;
-}
-
-#hd {
-    background: whiteSmoke;
-    background: -moz-linear-gradient(top,#DCDBD9 0,#F6F5F3 100%);
-    background: -webkit-gradient(linear,left top,left bottom,color-stop(0%,#DCDBD9),color-stop(100%,#F6F5F3));
-    filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#dcdbd9',endColorstr='#F6F5F3',GradientType=0);
-    border-bottom: 1px solid #DFDFDF;
-    padding: 0 15px 1px 20px;
-    margin-bottom: 15px;
-}
-
-#hd img {
-    margin-right: 10px;
-    vertical-align: middle;
-}
-
-
-/* -- API Docs CSS ---------------------------------------------------------- */
-
-/*
-This file is organized so that more generic styles are nearer the top, and more
-specific styles are nearer the bottom of the file. This allows us to take full
-advantage of the cascade to avoid redundant style rules. Please respect this
-convention when making changes.
-*/
-
-/* -- Generic TabView styles ------------------------------------------------ */
-
-/*
-These styles apply to all API doc tabviews. To change styles only for a
-specific tabview, see the other sections below.
-*/
-
-.yui3-js-enabled .apidocs .tabview {
-    visibility: hidden; /* Hide until the TabView finishes rendering. */
-    _visibility: visible;
-}
-
-.apidocs .tabview.yui3-tabview-content { visibility: visible; }
-.apidocs .tabview .yui3-tabview-panel { background: #fff; }
-
-/* -- Generic Content Styles ------------------------------------------------ */
-
-/* Headings */
-h2, h3, h4, h5, h6 {
-    border: none;
-    color: #30418C;
-    font-weight: bold;
-    text-decoration: none;
-}
-
-.link-docs {
-    float: right;
-    font-size: 15px;
-    margin: 4px 4px 6px;
-    padding: 6px 30px 5px;
-}
-
-.apidocs { zoom: 1; }
-
-/* Generic box styles. */
-.apidocs .box {
-    border: 1px solid;
-    border-radius: 3px;
-    margin: 1em 0;
-    padding: 0 1em;
-}
-
-/* A flag is a compact, capsule-like indicator of some kind. It's used to
-   indicate private and protected items, item return types, etc. in an
-   attractive and unobtrusive way. */
-.apidocs .flag {
-    background: #bababa;
-    border-radius: 3px;
-    color: #fff;
-    font-size: 11px;
-    margin: 0 0.5em;
-    padding: 2px 4px 1px;
-}
-
-/* Class/module metadata such as "Uses", "Extends", "Defined in", etc. */
-.apidocs .meta {
-    background: #f9f9f9;
-    border-color: #efefef;
-    color: #555;
-    font-size: 11px;
-    padding: 3px 6px;
-}
-
-.apidocs .meta p { margin: 0; }
-
-/* Deprecation warning. */
-.apidocs .box.deprecated,
-.apidocs .flag.deprecated {
-    background: #fdac9f;
-    border: 1px solid #fd7775;
-}
-
-.apidocs .box.deprecated p { margin: 0.5em 0; }
-.apidocs .flag.deprecated { color: #333; }
-
-/* Module/Class intro description. */
-.apidocs .intro {
-    background: #f0f1f8;
-    border-color: #d4d8eb;
-}
-
-/* Loading spinners. */
-#bd.loading .apidocs,
-#api-list.loading .yui3-tabview-panel {
-    background: #fff url(../img/spinner.gif) no-repeat center 70px;
-    min-height: 150px;
-}
-
-#bd.loading .apidocs .content,
-#api-list.loading .yui3-tabview-panel .apis {
-    display: none;
-}
-
-.apidocs .no-visible-items { color: #666; }
-
-/* Generic inline list. */
-.apidocs ul.inline {
-    display: inline;
-    list-style: none;
-    margin: 0;
-    padding: 0;
-}
-
-.apidocs ul.inline li { display: inline; }
-
-/* Comma-separated list. */
-.apidocs ul.commas li:after { content: ','; }
-.apidocs ul.commas li:last-child:after { content: ''; }
-
-/* Keyboard shortcuts. */
-kbd .cmd { font-family: Monaco, Helvetica; }
-
-/* -- Generic Access Level styles ------------------------------------------- */
-.apidocs .item.protected,
-.apidocs .item.private,
-.apidocs .index-item.protected,
-.apidocs .index-item.deprecated,
-.apidocs .index-item.private {
-    display: none;
-}
-
-.show-deprecated .item.deprecated,
-.show-deprecated .index-item.deprecated,
-.show-protected .item.protected,
-.show-protected .index-item.protected,
-.show-private .item.private,
-.show-private .index-item.private {
-    display: block;
-}
-
-.hide-inherited .item.inherited,
-.hide-inherited .index-item.inherited {
-    display: none;
-}
-
-/* -- Generic Item Index styles --------------------------------------------- */
-.apidocs .index { margin: 1.5em 0 3em; }
-
-.apidocs .index h3 {
-    border-bottom: 1px solid #efefef;
-    color: #333;
-    font-size: 13px;
-    margin: 2em 0 0.6em;
-    padding-bottom: 2px;
-}
-
-.apidocs .index .no-visible-items { margin-top: 2em; }
-
-.apidocs .index-list {
-    border-color: #efefef;
-    font-size: 12px;
-    list-style: none;
-    margin: 0;
-    padding: 0;
-    -moz-column-count: 4;
-    -moz-column-gap: 10px;
-    -moz-column-width: 170px;
-    -ms-column-count: 4;
-    -ms-column-gap: 10px;
-    -ms-column-width: 170px;
-    -o-column-count: 4;
-    -o-column-gap: 10px;
-    -o-column-width: 170px;
-    -webkit-column-count: 4;
-    -webkit-column-gap: 10px;
-    -webkit-column-width: 170px;
-    column-count: 4;
-    column-gap: 10px;
-    column-width: 170px;
-}
-
-.apidocs .no-columns .index-list {
-    -moz-column-count: 1;
-    -ms-column-count: 1;
-    -o-column-count: 1;
-    -webkit-column-count: 1;
-    column-count: 1;
-}
-
-.apidocs .index-item { white-space: nowrap; }
-
-.apidocs .index-item .flag {
-    background: none;
-    border: none;
-    color: #afafaf;
-    display: inline;
-    margin: 0 0 0 0.2em;
-    padding: 0;
-}
-
-/* -- Generic API item styles ----------------------------------------------- */
-.apidocs .args {
-    display: inline;
-    margin: 0 0.5em;
-}
-
-.apidocs .flag.chainable { background: #46ca3b; }
-.apidocs .flag.protected { background: #9b86fc; }
-.apidocs .flag.private { background: #fd6b1b; }
-.apidocs .flag.async { background: #356de4; }
-.apidocs .flag.required { background: #e60923; }
-
-.apidocs .item {
-    border-bottom: 1px solid #efefef;
-    margin: 1.5em 0 2em;
-    padding-bottom: 2em;
-}
-
-.apidocs .item h4,
-.apidocs .item h5,
-.apidocs .item h6 {
-    color: #333;
-    font-family: inherit;
-    font-size: 100%;
-}
-
-.apidocs .item .description p,
-.apidocs .item pre.code {
-    margin: 1em 0 0;
-}
-
-.apidocs .item .meta {
-    background: none;
-    border: none;
-    padding: 0;
-}
-
-.apidocs .item .name {
-    display: inline;
-    font-size: 14px;
-}
-
-.apidocs .item .type,
-.apidocs .item .type a,
-.apidocs .returns-inline {
-    color: #555;
-}
-
-.apidocs .item .type,
-.apidocs .returns-inline {
-    font-size: 11px;
-    margin: 0 0 0 0;
-}
-
-.apidocs .item .type a { border-bottom: 1px dotted #afafaf; }
-.apidocs .item .type a:hover { border: none; }
-
-/* -- Item Parameter List --------------------------------------------------- */
-.apidocs .params-list {
-    list-style: square;
-    margin: 1em 0 0 2em;
-    padding: 0;
-}
-
-.apidocs .param { margin-bottom: 1em; }
-
-.apidocs .param .type,
-.apidocs .param .type a {
-    color: #666;
-}
-
-.apidocs .param .type {
-    margin: 0 0 0 0.5em;
-    *margin-left: 0.5em;
-}
-
-.apidocs .param-name { font-weight: bold; }
-
-/* -- Item "Emits" block ---------------------------------------------------- */
-.apidocs .item .emits {
-    background: #f9f9f9;
-    border-color: #eaeaea;
-}
-
-/* -- Item "Returns" block -------------------------------------------------- */
-.apidocs .item .returns .type,
-.apidocs .item .returns .type a {
-    font-size: 100%;
-    margin: 0;
-}
-
-/* -- Class Constructor block ----------------------------------------------- */
-.apidocs .constructor .item {
-    border: none;
-    padding-bottom: 0;
-}
-
-/* -- File Source View ------------------------------------------------------ */
-.apidocs .file pre.code,
-#doc .apidocs .file pre.prettyprint {
-    background: inherit;
-    border: none;
-    overflow: visible;
-    padding: 0;
-}
-
-.apidocs .L0,
-.apidocs .L1,
-.apidocs .L2,
-.apidocs .L3,
-.apidocs .L4,
-.apidocs .L5,
-.apidocs .L6,
-.apidocs .L7,
-.apidocs .L8,
-.apidocs .L9 {
-    background: inherit;
-}
-
-/* -- Submodule List -------------------------------------------------------- */
-.apidocs .module-submodule-description {
-    font-size: 12px;
-    margin: 0.3em 0 1em;
-}
-
-.apidocs .module-submodule-description p:first-child { margin-top: 0; }
-
-/* -- Sidebar TabView ------------------------------------------------------- */
-#api-tabview { margin-top: 0.6em; }
-
-#api-tabview-filter,
-#api-tabview-panel {
-    border: 1px solid #dfdfdf;
-}
-
-#api-tabview-filter {
-    border-bottom: none;
-    border-top: none;
-    padding: 0.6em 10px 0 10px;
-}
-
-#api-tabview-panel { border-top: none; }
-#api-filter { width: 97%; }
-
-/* -- Content TabView ------------------------------------------------------- */
-#classdocs .yui3-tabview-panel { border: none; }
-
-/* -- Source File Contents -------------------------------------------------- */
-.prettyprint li.L0,
-.prettyprint li.L1,
-.prettyprint li.L2,
-.prettyprint li.L3,
-.prettyprint li.L5,
-.prettyprint li.L6,
-.prettyprint li.L7,
-.prettyprint li.L8 {
-    list-style: decimal;
-}
-
-/* -- API options ----------------------------------------------------------- */
-#api-options {
-    font-size: 11px;
-    margin-top: 2.2em;
-    position: absolute;
-    right: 1.5em;
-}
-
-/*#api-options label { margin-right: 0.6em; }*/
-
-/* -- API list -------------------------------------------------------------- */
-#api-list {
-    margin-top: 1.5em;
-    *zoom: 1;
-}
-
-.apis {
-    font-size: 12px;
-    line-height: 1.4;
-    list-style: none;
-    margin: 0;
-    padding: 0.5em 0 0.5em 0.4em;
-}
-
-.apis a {
-    border: 1px solid transparent;
-    display: block;
-    margin: 0 0 0 -4px;
-    padding: 1px 4px 0;
-    text-decoration: none;
-    _border: none;
-    _display: inline;
-}
-
-.apis a:hover,
-.apis a:focus {
-    background: #E8EDFC;
-    background: -moz-linear-gradient(top, #e8edfc 0%, #becef7 100%);
-    background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#E8EDFC), color-stop(100%,#BECEF7));
-    border-color: #AAC0FA;
-    border-radius: 3px;
-    color: #333;
-    outline: none;
-}
-
-.api-list-item a:hover,
-.api-list-item a:focus {
-    font-weight: bold;
-    text-shadow: 1px 1px 1px #fff;
-}
-
-.apis .message { color: #888; }
-.apis .result a { padding: 3px 5px 2px; }
-
-.apis .result .type {
-    right: 4px;
-    top: 7px;
-}
-
-.api-list-item .yui3-highlight {
-    font-weight: bold;
-}
-
diff --git a/doc/assets/favicon.ico b/doc/assets/favicon.ico
deleted file mode 100755
index 414ac4fb9..000000000
Binary files a/doc/assets/favicon.ico and /dev/null differ
diff --git a/doc/assets/img/spinner.gif b/doc/assets/img/spinner.gif
deleted file mode 100755
index 44f96ba68..000000000
Binary files a/doc/assets/img/spinner.gif and /dev/null differ
diff --git a/doc/assets/index.html b/doc/assets/index.html
deleted file mode 100755
index 487fe15b2..000000000
--- a/doc/assets/index.html
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
-    
-        Redirector
-        
-    
-    
-        Click here to redirect
-    
-
diff --git a/doc/assets/js/api-filter.js b/doc/assets/js/api-filter.js
deleted file mode 100755
index d442e5430..000000000
--- a/doc/assets/js/api-filter.js
+++ /dev/null
@@ -1,56 +0,0 @@
-YUI.add('api-filter', function (Y) {
-
-Y.APIFilter = Y.Base.create('apiFilter', Y.Base, [Y.AutoCompleteBase], {
-    // -- Initializer ----------------------------------------------------------
-    initializer: function () {
-        this._bindUIACBase();
-        this._syncUIACBase();
-    },
-    getDisplayName: function(name) {
-
-        Y.each(Y.YUIDoc.meta.allModules, function(i) {
-            if (i.name === name && i.displayName) {
-                name = i.displayName;
-            }
-        });
-
-        if (this.get('queryType') === 'elements') {
-            name = '<' + name + '>';
-        }
-
-        return name;
-    }
-
-}, {
-    // -- Attributes -----------------------------------------------------------
-    ATTRS: {
-        resultHighlighter: {
-            value: 'phraseMatch'
-        },
-
-        // May be set to "classes", "elements" or "modules".
-        queryType: {
-            value: 'classes'
-        },
-
-        source: {
-            valueFn: function() {
-                var self = this;
-                return function(q) {
-                    var data = Y.YUIDoc.meta[self.get('queryType')],
-                        out = [];
-                    Y.each(data, function(v) {
-                        if (v.toLowerCase().indexOf(q.toLowerCase()) > -1) {
-                            out.push(v);
-                        }
-                    });
-                    return out;
-                };
-            }
-        }
-    }
-});
-
-}, '3.4.0', {requires: [
-    'autocomplete-base', 'autocomplete-highlighters', 'autocomplete-sources'
-]});
diff --git a/doc/assets/js/api-list.js b/doc/assets/js/api-list.js
deleted file mode 100755
index e8f650d5d..000000000
--- a/doc/assets/js/api-list.js
+++ /dev/null
@@ -1,255 +0,0 @@
-YUI.add('api-list', function (Y) {
-
-var Lang   = Y.Lang,
-    YArray = Y.Array,
-
-    APIList = Y.namespace('APIList'),
-
-    classesNode    = Y.one('#api-classes'),
-    elementsNode   = Y.one('#api-elements'),
-    inputNode      = Y.one('#api-filter'),
-    modulesNode    = Y.one('#api-modules'),
-    tabviewNode    = Y.one('#api-tabview'),
-
-    tabs = APIList.tabs = {},
-
-    filter = APIList.filter = new Y.APIFilter({
-        inputNode : inputNode,
-        maxResults: 1000,
-
-        on: {
-            results: onFilterResults
-        }
-    }),
-
-    search = APIList.search = new Y.APISearch({
-        inputNode : inputNode,
-        maxResults: 100,
-
-        on: {
-            clear  : onSearchClear,
-            results: onSearchResults
-        }
-    }),
-
-    tabview = APIList.tabview = new Y.TabView({
-        srcNode  : tabviewNode,
-        panelNode: '#api-tabview-panel',
-        render   : true,
-
-        on: {
-            selectionChange: onTabSelectionChange
-        }
-    }),
-
-    focusManager = APIList.focusManager = tabviewNode.plug(Y.Plugin.NodeFocusManager, {
-        circular   : true,
-        descendants: '#api-filter, .yui3-tab-panel-selected .api-list-item a, .yui3-tab-panel-selected .result a',
-        keys       : {next: 'down:40', previous: 'down:38'}
-    }).focusManager,
-
-    LIST_ITEM_TEMPLATE =
-        '
  • ' + - '{displayName}' + - '
  • '; - -// -- Init --------------------------------------------------------------------- - -// Duckpunch FocusManager's key event handling to prevent it from handling key -// events when a modifier is pressed. -Y.before(function (e, activeDescendant) { - if (e.altKey || e.ctrlKey || e.metaKey || e.shiftKey) { - return new Y.Do.Prevent(); - } -}, focusManager, '_focusPrevious', focusManager); - -Y.before(function (e, activeDescendant) { - if (e.altKey || e.ctrlKey || e.metaKey || e.shiftKey) { - return new Y.Do.Prevent(); - } -}, focusManager, '_focusNext', focusManager); - -// Create a mapping of tabs in the tabview so we can refer to them easily later. -tabview.each(function (tab, index) { - var name = tab.get('label').toLowerCase(); - - tabs[name] = { - index: index, - name : name, - tab : tab - }; -}); - -// Switch tabs on Ctrl/Cmd-Left/Right arrows. -tabviewNode.on('key', onTabSwitchKey, 'down:37,39'); - -// Focus the filter input when the `/` key is pressed. -Y.one(Y.config.doc).on('key', onSearchKey, 'down:83'); - -// Keep the Focus Manager up to date. -inputNode.on('focus', function () { - focusManager.set('activeDescendant', inputNode); -}); - -// Update all tabview links to resolved URLs. -tabview.get('panelNode').all('a').each(function (link) { - link.setAttribute('href', link.get('href')); -}); - -// -- Private Functions -------------------------------------------------------- -function getFilterResultNode() { - var queryType = filter.get('queryType'); - return queryType === 'classes' ? classesNode - : queryType === 'elements' ? elementsNode : modulesNode; -} - -// -- Event Handlers ----------------------------------------------------------- -function onFilterResults(e) { - var frag = Y.one(Y.config.doc.createDocumentFragment()), - resultNode = getFilterResultNode(), - typePlural = filter.get('queryType'), - typeSingular = typePlural === 'classes' ? 'class' : typePlural === 'elements' ? 'element' : 'module'; - - if (e.results.length) { - YArray.each(e.results, function (result) { - frag.append(Lang.sub(LIST_ITEM_TEMPLATE, { - rootPath : APIList.rootPath, - displayName : filter.getDisplayName(result.highlighted), - name : result.text, - typePlural : typePlural, - typeSingular: typeSingular - })); - }); - } else { - frag.append( - '
  • ' + - 'No ' + typePlural + ' found.' + - '
  • ' - ); - } - - resultNode.empty(true); - resultNode.append(frag); - - focusManager.refresh(); -} - -function onSearchClear(e) { - - focusManager.refresh(); -} - -function onSearchKey(e) { - var target = e.target; - - if (target.test('input,select,textarea') - || target.get('isContentEditable')) { - return; - } - - e.preventDefault(); - - inputNode.focus(); - focusManager.refresh(); -} - -function onSearchResults(e) { - var frag = Y.one(Y.config.doc.createDocumentFragment()); - - if (e.results.length) { - YArray.each(e.results, function (result) { - frag.append(result.display); - }); - } else { - frag.append( - '
  • ' + - 'No results found. Maybe you\'ll have better luck with a ' + - 'different query?' + - '
  • ' - ); - } - - - focusManager.refresh(); -} - -function onTabSelectionChange(e) { - var tab = e.newVal, - name = tab.get('label').toLowerCase(); - - tabs.selected = { - index: tab.get('index'), - name : name, - tab : tab - }; - - switch (name) { - case 'elements':// fallthru - case 'classes': // fallthru - case 'modules': - filter.setAttrs({ - minQueryLength: 0, - queryType : name - }); - - search.set('minQueryLength', -1); - - // Only send a request if this isn't the initially-selected tab. - if (e.prevVal) { - filter.sendRequest(filter.get('value')); - } - break; - - case 'everything': - filter.set('minQueryLength', -1); - search.set('minQueryLength', 1); - - if (search.get('value')) { - search.sendRequest(search.get('value')); - } else { - inputNode.focus(); - } - break; - - default: - // WTF? We shouldn't be here! - filter.set('minQueryLength', -1); - search.set('minQueryLength', -1); - } - - if (focusManager) { - setTimeout(function () { - focusManager.refresh(); - }, 1); - } -} - -function onTabSwitchKey(e) { - var currentTabIndex = tabs.selected.index; - - if (!(e.ctrlKey || e.metaKey)) { - return; - } - - e.preventDefault(); - - switch (e.keyCode) { - case 37: // left arrow - if (currentTabIndex > 0) { - tabview.selectChild(currentTabIndex - 1); - inputNode.focus(); - } - break; - - case 39: // right arrow - if (currentTabIndex < (Y.Object.size(tabs) - 2)) { - tabview.selectChild(currentTabIndex + 1); - inputNode.focus(); - } - break; - } -} - -}, '3.4.0', {requires: [ - 'api-filter', 'api-search', 'event-key', 'node-focusmanager', 'tabview' -]}); diff --git a/doc/assets/js/api-search.js b/doc/assets/js/api-search.js deleted file mode 100755 index 175f6a617..000000000 --- a/doc/assets/js/api-search.js +++ /dev/null @@ -1,98 +0,0 @@ -YUI.add('api-search', function (Y) { - -var Lang = Y.Lang, - Node = Y.Node, - YArray = Y.Array; - -Y.APISearch = Y.Base.create('apiSearch', Y.Base, [Y.AutoCompleteBase], { - // -- Public Properties ---------------------------------------------------- - RESULT_TEMPLATE: - '
  • ' + - '' + - '

    {name}

    ' + - '{resultType}' + - '
    {description}
    ' + - '{class}' + - '
    ' + - '
  • ', - - // -- Initializer ---------------------------------------------------------- - initializer: function () { - this._bindUIACBase(); - this._syncUIACBase(); - }, - - // -- Protected Methods ---------------------------------------------------- - _apiResultFilter: function (query, results) { - // Filter components out of the results. - return YArray.filter(results, function (result) { - return result.raw.resultType === 'component' ? false : result; - }); - }, - - _apiResultFormatter: function (query, results) { - return YArray.map(results, function (result) { - var raw = Y.merge(result.raw), // create a copy - desc = raw.description || ''; - - // Convert description to text and truncate it if necessary. - desc = Node.create('
    ' + desc + '
    ').get('text'); - - if (desc.length > 65) { - desc = Y.Escape.html(desc.substr(0, 65)) + ' …'; - } else { - desc = Y.Escape.html(desc); - } - - raw['class'] || (raw['class'] = ''); - raw.description = desc; - - // Use the highlighted result name. - raw.name = result.highlighted; - - return Lang.sub(this.RESULT_TEMPLATE, raw); - }, this); - }, - - _apiTextLocator: function (result) { - return result.displayName || result.name; - } -}, { - // -- Attributes ----------------------------------------------------------- - ATTRS: { - resultFormatter: { - valueFn: function () { - return this._apiResultFormatter; - } - }, - - resultFilters: { - valueFn: function () { - return this._apiResultFilter; - } - }, - - resultHighlighter: { - value: 'phraseMatch' - }, - - resultListLocator: { - value: 'data.results' - }, - - resultTextLocator: { - valueFn: function () { - return this._apiTextLocator; - } - }, - - source: { - value: '/api/v1/search?q={query}&count={maxResults}' - } - } -}); - -}, '3.4.0', {requires: [ - 'autocomplete-base', 'autocomplete-highlighters', 'autocomplete-sources', - 'escape' -]}); diff --git a/doc/assets/js/apidocs.js b/doc/assets/js/apidocs.js deleted file mode 100755 index af9ac3220..000000000 --- a/doc/assets/js/apidocs.js +++ /dev/null @@ -1,376 +0,0 @@ -YUI().use( - 'yuidoc-meta', - 'api-list', 'history-hash', 'node-screen', 'node-style', 'pjax', -function (Y) { - -var win = Y.config.win, - localStorage = win.localStorage, - - bdNode = Y.one('#bd'), - - pjax, - defaultRoute, - - classTabView, - selectedTab; - -// Kill pjax functionality unless serving over HTTP. -if (!Y.getLocation().protocol.match(/^https?\:/)) { - Y.Router.html5 = false; -} - -// Create the default route with middleware which enables syntax highlighting -// on the loaded content. -defaultRoute = Y.Pjax.defaultRoute.concat(function (req, res, next) { - prettyPrint(); - bdNode.removeClass('loading'); - - next(); -}); - -pjax = new Y.Pjax({ - container : '#docs-main', - contentSelector: '#docs-main > .content', - linkSelector : '#bd a', - titleSelector : '#xhr-title', - - navigateOnHash: true, - root : '/', - routes : [ - // -- / ---------------------------------------------------------------- - { - path : '/(index.html)?', - callbacks: defaultRoute - }, - - // -- /elements/* ------------------------------------------------------- - { - path : '/elements/:element.html*', - callbacks: defaultRoute - }, - - // -- /classes/* ------------------------------------------------------- - { - path : '/classes/:class.html*', - callbacks: [defaultRoute, 'handleClasses'] - }, - - // -- /files/* --------------------------------------------------------- - { - path : '/files/*file', - callbacks: [defaultRoute, 'handleFiles'] - }, - - // -- /modules/* ------------------------------------------------------- - { - path : '/modules/:module.html*', - callbacks: defaultRoute - } - ] -}); - -// -- Utility Functions -------------------------------------------------------- - -pjax.checkVisibility = function (tab) { - tab || (tab = selectedTab); - - if (!tab) { return; } - - var panelNode = tab.get('panelNode'), - visibleItems; - - // If no items are visible in the tab panel due to the current visibility - // settings, display a message to that effect. - visibleItems = panelNode.all('.item,.index-item').some(function (itemNode) { - if (itemNode.getComputedStyle('display') !== 'none') { - return true; - } - }); - - panelNode.all('.no-visible-items').remove(); - - if (!visibleItems) { - if (Y.one('#index .index-item')) { - panelNode.append( - '
    ' + - '

    ' + - 'Some items are not shown due to the current visibility ' + - 'settings. Use the checkboxes at the upper right of this ' + - 'page to change the visibility settings.' + - '

    ' + - '
    ' - ); - } else { - panelNode.append( - '
    ' + - '

    ' + - 'This class doesn\'t provide any methods, properties, ' + - 'attributes, or events.' + - '

    ' + - '
    ' - ); - } - } - - // Hide index sections without any visible items. - Y.all('.index-section').each(function (section) { - var items = 0, - visibleItems = 0; - - section.all('.index-item').each(function (itemNode) { - items += 1; - - if (itemNode.getComputedStyle('display') !== 'none') { - visibleItems += 1; - } - }); - - section.toggleClass('hidden', !visibleItems); - section.toggleClass('no-columns', visibleItems < 4); - }); -}; - -pjax.initClassTabView = function () { - if (!Y.all('#classdocs .api-class-tab').size()) { - return; - } - - if (classTabView) { - classTabView.destroy(); - selectedTab = null; - } - - classTabView = new Y.TabView({ - srcNode: '#classdocs', - - on: { - selectionChange: pjax.onTabSelectionChange - } - }); - - pjax.updateTabState(); - classTabView.render(); -}; - -pjax.initLineNumbers = function () { - var hash = win.location.hash.substring(1), - container = pjax.get('container'), - hasLines, node; - - // Add ids for each line number in the file source view. - container.all('.linenums>li').each(function (lineNode, index) { - lineNode.set('id', 'l' + (index + 1)); - lineNode.addClass('file-line'); - hasLines = true; - }); - - // Scroll to the desired line. - if (hasLines && /^l\d+$/.test(hash)) { - if ((node = container.getById(hash))) { - win.scroll(0, node.getY()); - } - } -}; - -pjax.initRoot = function () { - var terminators = /^(?:classes|files|elements|modules)$/, - parts = pjax._getPathRoot().split('/'), - root = [], - i, len, part; - - for (i = 0, len = parts.length; i < len; i += 1) { - part = parts[i]; - - if (part.match(terminators)) { - // Makes sure the path will end with a "/". - root.push(''); - break; - } - - root.push(part); - } - - pjax.set('root', root.join('/')); -}; - -pjax.updateTabState = function (src) { - var hash = win.location.hash.substring(1), - defaultTab, node, tab, tabPanel; - - function scrollToNode() { - if (node.hasClass('protected')) { - Y.one('#api-show-protected').set('checked', true); - pjax.updateVisibility(); - } - - if (node.hasClass('private')) { - Y.one('#api-show-private').set('checked', true); - pjax.updateVisibility(); - } - - setTimeout(function () { - // For some reason, unless we re-get the node instance here, - // getY() always returns 0. - var node = Y.one('#classdocs').getById(hash); - win.scrollTo(0, node.getY() - 70); - }, 1); - } - - if (!classTabView) { - return; - } - - if (src === 'hashchange' && !hash) { - defaultTab = 'index'; - } else { - if (localStorage) { - defaultTab = localStorage.getItem('tab_' + pjax.getPath()) || - 'index'; - } else { - defaultTab = 'index'; - } - } - - if (hash && (node = Y.one('#classdocs').getById(hash))) { - if ((tabPanel = node.ancestor('.api-class-tabpanel', true))) { - if ((tab = Y.one('#classdocs .api-class-tab.' + tabPanel.get('id')))) { - if (classTabView.get('rendered')) { - Y.Widget.getByNode(tab).set('selected', 1); - } else { - tab.addClass('yui3-tab-selected'); - } - } - } - - // Scroll to the desired element if this is a hash URL. - if (node) { - if (classTabView.get('rendered')) { - scrollToNode(); - } else { - classTabView.once('renderedChange', scrollToNode); - } - } - } else { - tab = Y.one('#classdocs .api-class-tab.' + defaultTab); - - // When the `defaultTab` node isn't found, `localStorage` is stale. - if (!tab && defaultTab !== 'index') { - tab = Y.one('#classdocs .api-class-tab.index'); - } - - if (classTabView.get('rendered')) { - Y.Widget.getByNode(tab).set('selected', 1); - } else { - tab.addClass('yui3-tab-selected'); - } - } -}; - -pjax.updateVisibility = function () { - var container = pjax.get('container'); - - container.toggleClass('hide-inherited', - !Y.one('#api-show-inherited').get('checked')); - - container.toggleClass('show-deprecated', - Y.one('#api-show-deprecated').get('checked')); - - container.toggleClass('show-protected', - Y.one('#api-show-protected').get('checked')); - - container.toggleClass('show-private', - Y.one('#api-show-private').get('checked')); - - pjax.checkVisibility(); -}; - -// -- Route Handlers ----------------------------------------------------------- - -pjax.handleClasses = function (req, res, next) { - var status = res.ioResponse.status; - - // Handles success and local filesystem XHRs. - if (res.ioResponse.readyState === 4 && (!status || (status >= 200 && status < 300))) { - pjax.initClassTabView(); - } - - next(); -}; - -pjax.handleFiles = function (req, res, next) { - var status = res.ioResponse.status; - - // Handles success and local filesystem XHRs. - if (res.ioResponse.readyState === 4 && (!status || (status >= 200 && status < 300))) { - pjax.initLineNumbers(); - } - - next(); -}; - -// -- Event Handlers ----------------------------------------------------------- - -pjax.onNavigate = function (e) { - var hash = e.hash, - originTarget = e.originEvent && e.originEvent.target, - tab; - - if (hash) { - tab = originTarget && originTarget.ancestor('.yui3-tab', true); - - if (hash === win.location.hash) { - pjax.updateTabState('hashchange'); - } else if (!tab) { - win.location.hash = hash; - } - - e.preventDefault(); - return; - } - - // Only scroll to the top of the page when the URL doesn't have a hash. - this.set('scrollToTop', !e.url.match(/#.+$/)); - - bdNode.addClass('loading'); -}; - -pjax.onOptionClick = function (e) { - pjax.updateVisibility(); -}; - -pjax.onTabSelectionChange = function (e) { - var tab = e.newVal, - tabId = tab.get('contentBox').getAttribute('href').substring(1); - - selectedTab = tab; - - // If switching from a previous tab (i.e., this is not the default tab), - // replace the history entry with a hash URL that will cause this tab to - // be selected if the user navigates away and then returns using the back - // or forward buttons. - if (e.prevVal && localStorage) { - localStorage.setItem('tab_' + pjax.getPath(), tabId); - } - - pjax.checkVisibility(tab); -}; - -// -- Init --------------------------------------------------------------------- - -pjax.on('navigate', pjax.onNavigate); - -pjax.initRoot(); -pjax.upgrade(); -pjax.initClassTabView(); -pjax.initLineNumbers(); -pjax.updateVisibility(); - -Y.APIList.rootPath = pjax.get('root'); - -Y.one('#api-options').delegate('click', pjax.onOptionClick, 'input'); - -Y.on('hashchange', function (e) { - pjax.updateTabState('hashchange'); -}, win); - -}); diff --git a/doc/assets/js/yui-prettify.js b/doc/assets/js/yui-prettify.js deleted file mode 100755 index 18de86495..000000000 --- a/doc/assets/js/yui-prettify.js +++ /dev/null @@ -1,17 +0,0 @@ -YUI().use('node', function(Y) { - var code = Y.all('.prettyprint.linenums'); - if (code.size()) { - code.each(function(c) { - var lis = c.all('ol li'), - l = 1; - lis.each(function(n) { - n.prepend(''); - l++; - }); - }); - var h = location.hash; - location.hash = ''; - h = h.replace('LINE_', 'LINENUM_'); - location.hash = h; - } -}); diff --git a/doc/assets/vendor/prettify/CHANGES.html b/doc/assets/vendor/prettify/CHANGES.html deleted file mode 100755 index b50b84149..000000000 --- a/doc/assets/vendor/prettify/CHANGES.html +++ /dev/null @@ -1,130 +0,0 @@ - - - - Change Log - - - README - -

    Known Issues

    - - -

    Change Log

    -

    29 March 2007

    - -

    4 Jul 2008

    - -

    5 Jul 2008

    -
    -

    14 Jul 2008

    - -

    6 Jan 2009

    - -

    21 May 2009

    - -

    14 August 2009

    - -

    3 October 2009

    - -

    19 July 2010

    - - - diff --git a/doc/assets/vendor/prettify/COPYING b/doc/assets/vendor/prettify/COPYING deleted file mode 100755 index d64569567..000000000 --- a/doc/assets/vendor/prettify/COPYING +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/doc/assets/vendor/prettify/README.html b/doc/assets/vendor/prettify/README.html deleted file mode 100755 index c6fe1a32c..000000000 --- a/doc/assets/vendor/prettify/README.html +++ /dev/null @@ -1,203 +0,0 @@ - - - - - Javascript code prettifier - - - - - - - - - - Languages : CH -

    Javascript code prettifier

    - -

    Setup

    -
      -
    1. Download a distribution -
    2. Include the script and stylesheets in your document - (you will need to make sure the css and js file are on your server, and - adjust the paths in the script and link tag) -
      -<link href="prettify.css" type="text/css" rel="stylesheet" />
      -<script type="text/javascript" src="prettify.js"></script>
      -
    3. Add onload="prettyPrint()" to your - document's body tag. -
    4. Modify the stylesheet to get the coloring you prefer
    5. -
    - -

    Usage

    -

    Put code snippets in - <pre class="prettyprint">...</pre> - or <code class="prettyprint">...</code> - and it will automatically be pretty printed. - - - - -
    The original - Prettier -
    class Voila {
    -public:
    -  // Voila
    -  static const string VOILA = "Voila";
    -
    -  // will not interfere with embedded tags.
    -}
    - -
    class Voila {
    -public:
    -  // Voila
    -  static const string VOILA = "Voila";
    -
    -  // will not interfere with embedded tags.
    -}
    -
    - -

    FAQ

    -

    Which languages does it work for?

    -

    The comments in prettify.js are authoritative but the lexer - should work on a number of languages including C and friends, - Java, Python, Bash, SQL, HTML, XML, CSS, Javascript, and Makefiles. - It works passably on Ruby, PHP, VB, and Awk and a decent subset of Perl - and Ruby, but, because of commenting conventions, doesn't work on - Smalltalk, or CAML-like languages.

    - -

    LISPy languages are supported via an extension: - lang-lisp.js.

    -

    And similarly for - CSS, - Haskell, - Lua, - OCAML, SML, F#, - Visual Basic, - SQL, - Protocol Buffers, and - WikiText.. - -

    If you'd like to add an extension for your favorite language, please - look at src/lang-lisp.js and file an - issue including your language extension, and a testcase.

    - -

    How do I specify which language my code is in?

    -

    You don't need to specify the language since prettyprint() - will guess. You can specify a language by specifying the language extension - along with the prettyprint class like so:

    -
    <pre class="prettyprint lang-html">
    -  The lang-* class specifies the language file extensions.
    -  File extensions supported by default include
    -    "bsh", "c", "cc", "cpp", "cs", "csh", "cyc", "cv", "htm", "html",
    -    "java", "js", "m", "mxml", "perl", "pl", "pm", "py", "rb", "sh",
    -    "xhtml", "xml", "xsl".
    -</pre>
    - -

    It doesn't work on <obfuscated code sample>?

    -

    Yes. Prettifying obfuscated code is like putting lipstick on a pig - — i.e. outside the scope of this tool.

    - -

    Which browsers does it work with?

    -

    It's been tested with IE 6, Firefox 1.5 & 2, and Safari 2.0.4. - Look at the test page to see if it - works in your browser.

    - -

    What's changed?

    -

    See the change log

    - -

    Why doesn't Prettyprinting of strings work on WordPress?

    -

    Apparently wordpress does "smart quoting" which changes close quotes. - This causes end quotes to not match up with open quotes. -

    This breaks prettifying as well as copying and pasting of code samples. - See - WordPress's help center for info on how to stop smart quoting of code - snippets.

    - -

    How do I put line numbers in my code?

    -

    You can use the linenums class to turn on line - numbering. If your code doesn't start at line number 1, you can - add a colon and a line number to the end of that class as in - linenums:52. - -

    For example -

    <pre class="prettyprint linenums:4"
    ->// This is line 4.
    -foo();
    -bar();
    -baz();
    -boo();
    -far();
    -faz();
    -<pre>
    - produces -
    // This is line 4.
    -foo();
    -bar();
    -baz();
    -boo();
    -far();
    -faz();
    -
    - -

    How do I prevent a portion of markup from being marked as code?

    -

    You can use the nocode class to identify a span of markup - that is not code. -

    <pre class=prettyprint>
    -int x = foo();  /* This is a comment  <span class="nocode">This is not code</span>
    -  Continuation of comment */
    -int y = bar();
    -</pre>
    -produces -
    -int x = foo();  /* This is a comment  This is not code
    -  Continuation of comment */
    -int y = bar();
    -
    - -

    For a more complete example see the issue22 - testcase.

    - -

    I get an error message "a is not a function" or "opt_whenDone is not a function"

    -

    If you are calling prettyPrint via an event handler, wrap it in a function. - Instead of doing -

    - addEventListener('load', prettyPrint, false); -
    - wrap it in a closure like -
    - addEventListener('load', function (event) { prettyPrint() }, false); -
    - so that the browser does not pass an event object to prettyPrint which - will confuse it. - -


    - - - - diff --git a/doc/assets/vendor/prettify/prettify-min.css b/doc/assets/vendor/prettify/prettify-min.css deleted file mode 100755 index d44b3a228..000000000 --- a/doc/assets/vendor/prettify/prettify-min.css +++ /dev/null @@ -1 +0,0 @@ -.pln{color:#000}@media screen{.str{color:#080}.kwd{color:#008}.com{color:#800}.typ{color:#606}.lit{color:#066}.pun,.opn,.clo{color:#660}.tag{color:#008}.atn{color:#606}.atv{color:#080}.dec,.var{color:#606}.fun{color:red}}@media print,projection{.str{color:#060}.kwd{color:#006;font-weight:bold}.com{color:#600;font-style:italic}.typ{color:#404;font-weight:bold}.lit{color:#044}.pun,.opn,.clo{color:#440}.tag{color:#006;font-weight:bold}.atn{color:#404}.atv{color:#060}}pre.prettyprint{padding:2px;border:1px solid #888}ol.linenums{margin-top:0;margin-bottom:0}li.L0,li.L1,li.L2,li.L3,li.L5,li.L6,li.L7,li.L8{list-style-type:none}li.L1,li.L3,li.L5,li.L7,li.L9{background:#eee} \ No newline at end of file diff --git a/doc/assets/vendor/prettify/prettify-min.js b/doc/assets/vendor/prettify/prettify-min.js deleted file mode 100755 index 4845d05d4..000000000 --- a/doc/assets/vendor/prettify/prettify-min.js +++ /dev/null @@ -1 +0,0 @@ -window.PR_SHOULD_USE_CONTINUATION=true;var prettyPrintOne;var prettyPrint;(function(){var O=window;var j=["break,continue,do,else,for,if,return,while"];var v=[j,"auto,case,char,const,default,double,enum,extern,float,goto,int,long,register,short,signed,sizeof,static,struct,switch,typedef,union,unsigned,void,volatile"];var q=[v,"catch,class,delete,false,import,new,operator,private,protected,public,this,throw,true,try,typeof"];var m=[q,"alignof,align_union,asm,axiom,bool,concept,concept_map,const_cast,constexpr,decltype,dynamic_cast,explicit,export,friend,inline,late_check,mutable,namespace,nullptr,reinterpret_cast,static_assert,static_cast,template,typeid,typename,using,virtual,where"];var y=[q,"abstract,boolean,byte,extends,final,finally,implements,import,instanceof,null,native,package,strictfp,super,synchronized,throws,transient"];var T=[y,"as,base,by,checked,decimal,delegate,descending,dynamic,event,fixed,foreach,from,group,implicit,in,interface,internal,into,is,let,lock,object,out,override,orderby,params,partial,readonly,ref,sbyte,sealed,stackalloc,string,select,uint,ulong,unchecked,unsafe,ushort,var,virtual,where"];var s="all,and,by,catch,class,else,extends,false,finally,for,if,in,is,isnt,loop,new,no,not,null,of,off,on,or,return,super,then,throw,true,try,unless,until,when,while,yes";var x=[q,"debugger,eval,export,function,get,null,set,undefined,var,with,Infinity,NaN"];var t="caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END";var J=[j,"and,as,assert,class,def,del,elif,except,exec,finally,from,global,import,in,is,lambda,nonlocal,not,or,pass,print,raise,try,with,yield,False,True,None"];var g=[j,"alias,and,begin,case,class,def,defined,elsif,end,ensure,false,in,module,next,nil,not,or,redo,rescue,retry,self,super,then,true,undef,unless,until,when,yield,BEGIN,END"];var I=[j,"case,done,elif,esac,eval,fi,function,in,local,set,then,until"];var B=[m,T,x,t+J,g,I];var f=/^(DIR|FILE|vector|(de|priority_)?queue|list|stack|(const_)?iterator|(multi)?(set|map)|bitset|u?(int|float)\d*)\b/;var D="str";var A="kwd";var k="com";var Q="typ";var H="lit";var M="pun";var G="pln";var n="tag";var F="dec";var K="src";var R="atn";var o="atv";var P="nocode";var N="(?:^^\\.?|[+-]|[!=]=?=?|\\#|%=?|&&?=?|\\(|\\*=?|[+\\-]=|->|\\/=?|::?|<>?>?=?|,|;|\\?|@|\\[|~|{|\\^\\^?=?|\\|\\|?=?|break|case|continue|delete|do|else|finally|instanceof|return|throw|try|typeof)\\s*";function l(ab){var af=0;var U=false;var ae=false;for(var X=0,W=ab.length;X122)){if(!(am<65||ai>90)){ah.push([Math.max(65,ai)|32,Math.min(am,90)|32])}if(!(am<97||ai>122)){ah.push([Math.max(97,ai)&~32,Math.min(am,122)&~32])}}}}ah.sort(function(aw,av){return(aw[0]-av[0])||(av[1]-aw[1])});var ak=[];var aq=[];for(var at=0;atau[0]){if(au[1]+1>au[0]){ao.push("-")}ao.push(V(au[1]))}}ao.push("]");return ao.join("")}function Y(an){var al=an.source.match(new RegExp("(?:\\[(?:[^\\x5C\\x5D]|\\\\[\\s\\S])*\\]|\\\\u[A-Fa-f0-9]{4}|\\\\x[A-Fa-f0-9]{2}|\\\\[0-9]+|\\\\[^ux0-9]|\\(\\?[:!=]|[\\(\\)\\^]|[^\\x5B\\x5C\\(\\)\\^]+)","g"));var aj=al.length;var ap=[];for(var am=0,ao=0;am=2&&ak==="["){al[am]=Z(ai)}else{if(ak!=="\\"){al[am]=ai.replace(/[a-zA-Z]/g,function(aq){var ar=aq.charCodeAt(0);return"["+String.fromCharCode(ar&~32,ar|32)+"]"})}}}}return al.join("")}var ac=[];for(var X=0,W=ab.length;X=0;){U[ae.charAt(ag)]=aa}}var ah=aa[1];var ac=""+ah;if(!ai.hasOwnProperty(ac)){aj.push(ah);ai[ac]=null}}aj.push(/[\0-\uffff]/);X=l(aj)})();var Z=V.length;var Y=function(aj){var ab=aj.sourceCode,aa=aj.basePos;var af=[aa,G];var ah=0;var ap=ab.match(X)||[];var al={};for(var ag=0,at=ap.length;ag=5&&"lang-"===ar.substring(0,5);if(ao&&!(ak&&typeof ak[1]==="string")){ao=false;ar=K}if(!ao){al[ai]=ar}}var ad=ah;ah+=ai.length;if(!ao){af.push(aa+ad,ar)}else{var an=ak[1];var am=ai.indexOf(an);var ae=am+an.length;if(ak[2]){ae=ai.length-ak[2].length;am=ae-an.length}var au=ar.substring(5);C(aa+ad,ai.substring(0,am),Y,af);C(aa+ad+am,an,r(au,an),af);C(aa+ad+ae,ai.substring(ae),Y,af)}}aj.decorations=af};return Y}function i(V){var Y=[],U=[];if(V.tripleQuotedStrings){Y.push([D,/^(?:\'\'\'(?:[^\'\\]|\\[\s\S]|\'{1,2}(?=[^\']))*(?:\'\'\'|$)|\"\"\"(?:[^\"\\]|\\[\s\S]|\"{1,2}(?=[^\"]))*(?:\"\"\"|$)|\'(?:[^\\\']|\\[\s\S])*(?:\'|$)|\"(?:[^\\\"]|\\[\s\S])*(?:\"|$))/,null,"'\""])}else{if(V.multiLineStrings){Y.push([D,/^(?:\'(?:[^\\\']|\\[\s\S])*(?:\'|$)|\"(?:[^\\\"]|\\[\s\S])*(?:\"|$)|\`(?:[^\\\`]|\\[\s\S])*(?:\`|$))/,null,"'\"`"])}else{Y.push([D,/^(?:\'(?:[^\\\'\r\n]|\\.)*(?:\'|$)|\"(?:[^\\\"\r\n]|\\.)*(?:\"|$))/,null,"\"'"])}}if(V.verbatimStrings){U.push([D,/^@\"(?:[^\"]|\"\")*(?:\"|$)/,null])}var ab=V.hashComments;if(ab){if(V.cStyleComments){if(ab>1){Y.push([k,/^#(?:##(?:[^#]|#(?!##))*(?:###|$)|.*)/,null,"#"])}else{Y.push([k,/^#(?:(?:define|e(?:l|nd)if|else|error|ifn?def|include|line|pragma|undef|warning)\b|[^\r\n]*)/,null,"#"])}U.push([D,/^<(?:(?:(?:\.\.\/)*|\/?)(?:[\w-]+(?:\/[\w-]+)+)?[\w-]+\.h(?:h|pp|\+\+)?|[a-z]\w*)>/,null])}else{Y.push([k,/^#[^\r\n]*/,null,"#"])}}if(V.cStyleComments){U.push([k,/^\/\/[^\r\n]*/,null]);U.push([k,/^\/\*[\s\S]*?(?:\*\/|$)/,null])}if(V.regexLiterals){var aa=("/(?=[^/*])(?:[^/\\x5B\\x5C]|\\x5C[\\s\\S]|\\x5B(?:[^\\x5C\\x5D]|\\x5C[\\s\\S])*(?:\\x5D|$))+/");U.push(["lang-regex",new RegExp("^"+N+"("+aa+")")])}var X=V.types;if(X){U.push([Q,X])}var W=(""+V.keywords).replace(/^ | $/g,"");if(W.length){U.push([A,new RegExp("^(?:"+W.replace(/[\s,]+/g,"|")+")\\b"),null])}Y.push([G,/^\s+/,null," \r\n\t\xA0"]);var Z=/^.[^\s\w\.$@\'\"\`\/\\]*/;U.push([H,/^@[a-z_$][a-z_$@0-9]*/i,null],[Q,/^(?:[@_]?[A-Z]+[a-z][A-Za-z_$@0-9]*|\w+_t\b)/,null],[G,/^[a-z_$][a-z_$@0-9]*/i,null],[H,new RegExp("^(?:0x[a-f0-9]+|(?:\\d(?:_\\d+)*\\d*(?:\\.\\d*)?|\\.\\d\\+)(?:e[+\\-]?\\d+)?)[a-z]*","i"),null,"0123456789"],[G,/^\\[\s\S]?/,null],[M,Z,null]);return h(Y,U)}var L=i({keywords:B,hashComments:true,cStyleComments:true,multiLineStrings:true,regexLiterals:true});function S(W,ah,aa){var V=/(?:^|\s)nocode(?:\s|$)/;var ac=/\r\n?|\n/;var ad=W.ownerDocument;var ag=ad.createElement("li");while(W.firstChild){ag.appendChild(W.firstChild)}var X=[ag];function af(am){switch(am.nodeType){case 1:if(V.test(am.className)){break}if("br"===am.nodeName){ae(am);if(am.parentNode){am.parentNode.removeChild(am)}}else{for(var ao=am.firstChild;ao;ao=ao.nextSibling){af(ao)}}break;case 3:case 4:if(aa){var an=am.nodeValue;var ak=an.match(ac);if(ak){var aj=an.substring(0,ak.index);am.nodeValue=aj;var ai=an.substring(ak.index+ak[0].length);if(ai){var al=am.parentNode;al.insertBefore(ad.createTextNode(ai),am.nextSibling)}ae(am);if(!aj){am.parentNode.removeChild(am)}}}break}}function ae(al){while(!al.nextSibling){al=al.parentNode;if(!al){return}}function aj(am,at){var ar=at?am.cloneNode(false):am;var ap=am.parentNode;if(ap){var aq=aj(ap,1);var ao=am.nextSibling;aq.appendChild(ar);for(var an=ao;an;an=ao){ao=an.nextSibling;aq.appendChild(an)}}return ar}var ai=aj(al.nextSibling,0);for(var ak;(ak=ai.parentNode)&&ak.nodeType===1;){ai=ak}X.push(ai)}for(var Z=0;Z=U){aj+=2}if(Y>=ar){ac+=2}}}finally{if(au){au.style.display=ak}}}var u={};function d(W,X){for(var U=X.length;--U>=0;){var V=X[U];if(!u.hasOwnProperty(V)){u[V]=W}else{if(O.console){console.warn("cannot override language handler %s",V)}}}}function r(V,U){if(!(V&&u.hasOwnProperty(V))){V=/^\s*]*(?:>|$)/],[k,/^<\!--[\s\S]*?(?:-\->|$)/],["lang-",/^<\?([\s\S]+?)(?:\?>|$)/],["lang-",/^<%([\s\S]+?)(?:%>|$)/],[M,/^(?:<[%?]|[%?]>)/],["lang-",/^]*>([\s\S]+?)<\/xmp\b[^>]*>/i],["lang-js",/^]*>([\s\S]*?)(<\/script\b[^>]*>)/i],["lang-css",/^]*>([\s\S]*?)(<\/style\b[^>]*>)/i],["lang-in.tag",/^(<\/?[a-z][^<>]*>)/i]]),["default-markup","htm","html","mxml","xhtml","xml","xsl"]);d(h([[G,/^[\s]+/,null," \t\r\n"],[o,/^(?:\"[^\"]*\"?|\'[^\']*\'?)/,null,"\"'"]],[[n,/^^<\/?[a-z](?:[\w.:-]*\w)?|\/?>$/i],[R,/^(?!style[\s=]|on)[a-z](?:[\w:-]*\w)?/i],["lang-uq.val",/^=\s*([^>\'\"\s]*(?:[^>\'\"\s\/]|\/(?=\s)))/],[M,/^[=<>\/]+/],["lang-js",/^on\w+\s*=\s*\"([^\"]+)\"/i],["lang-js",/^on\w+\s*=\s*\'([^\']+)\'/i],["lang-js",/^on\w+\s*=\s*([^\"\'>\s]+)/i],["lang-css",/^style\s*=\s*\"([^\"]+)\"/i],["lang-css",/^style\s*=\s*\'([^\']+)\'/i],["lang-css",/^style\s*=\s*([^\"\'>\s]+)/i]]),["in.tag"]);d(h([],[[o,/^[\s\S]+/]]),["uq.val"]);d(i({keywords:m,hashComments:true,cStyleComments:true,types:f}),["c","cc","cpp","cxx","cyc","m"]);d(i({keywords:"null,true,false"}),["json"]);d(i({keywords:T,hashComments:true,cStyleComments:true,verbatimStrings:true,types:f}),["cs"]);d(i({keywords:y,cStyleComments:true}),["java"]);d(i({keywords:I,hashComments:true,multiLineStrings:true}),["bsh","csh","sh"]);d(i({keywords:J,hashComments:true,multiLineStrings:true,tripleQuotedStrings:true}),["cv","py"]);d(i({keywords:t,hashComments:true,multiLineStrings:true,regexLiterals:true}),["perl","pl","pm"]);d(i({keywords:g,hashComments:true,multiLineStrings:true,regexLiterals:true}),["rb"]);d(i({keywords:x,cStyleComments:true,regexLiterals:true}),["js"]);d(i({keywords:s,hashComments:3,cStyleComments:true,multilineStrings:true,tripleQuotedStrings:true,regexLiterals:true}),["coffee"]);d(h([],[[D,/^[\s\S]+/]]),["regex"]);function e(X){var W=X.langExtension;try{var U=b(X.sourceNode,X.pre);var V=U.sourceCode;X.sourceCode=V;X.spans=U.spans;X.basePos=0;r(W,V)(X);E(X)}catch(Y){if(O.console){console.log(Y&&Y.stack?Y.stack:Y)}}}function z(Y,X,W){var U=document.createElement("pre");U.innerHTML=Y;if(W){S(U,W,true)}var V={langExtension:X,numberLines:W,sourceNode:U,pre:1};e(V);return U.innerHTML}function c(aj){function ab(al){return document.getElementsByTagName(al)}var ah=[ab("pre"),ab("code"),ab("xmp")];var V=[];for(var ae=0;ae]*(?:>|$)/],[PR.PR_COMMENT,/^<\!--[\s\S]*?(?:-\->|$)/],[PR.PR_PUNCTUATION,/^(?:<[%?]|[%?]>)/],["lang-",/^<\?([\s\S]+?)(?:\?>|$)/],["lang-",/^<%([\s\S]+?)(?:%>|$)/],["lang-",/^]*>([\s\S]+?)<\/xmp\b[^>]*>/i],["lang-handlebars",/^]*type\s*=\s*['"]?text\/x-handlebars-template['"]?\b[^>]*>([\s\S]*?)(<\/script\b[^>]*>)/i],["lang-js",/^]*>([\s\S]*?)(<\/script\b[^>]*>)/i],["lang-css",/^]*>([\s\S]*?)(<\/style\b[^>]*>)/i],["lang-in.tag",/^(<\/?[a-z][^<>]*>)/i],[PR.PR_DECLARATION,/^{{[#^>/]?\s*[\w.][^}]*}}/],[PR.PR_DECLARATION,/^{{&?\s*[\w.][^}]*}}/],[PR.PR_DECLARATION,/^{{{>?\s*[\w.][^}]*}}}/],[PR.PR_COMMENT,/^{{![^}]*}}/]]),["handlebars","hbs"]);PR.registerLangHandler(PR.createSimpleLexer([[PR.PR_PLAIN,/^[ \t\r\n\f]+/,null," \t\r\n\f"]],[[PR.PR_STRING,/^\"(?:[^\n\r\f\\\"]|\\(?:\r\n?|\n|\f)|\\[\s\S])*\"/,null],[PR.PR_STRING,/^\'(?:[^\n\r\f\\\']|\\(?:\r\n?|\n|\f)|\\[\s\S])*\'/,null],["lang-css-str",/^url\(([^\)\"\']*)\)/i],[PR.PR_KEYWORD,/^(?:url|rgb|\!important|@import|@page|@media|@charset|inherit)(?=[^\-\w]|$)/i,null],["lang-css-kw",/^(-?(?:[_a-z]|(?:\\[0-9a-f]+ ?))(?:[_a-z0-9\-]|\\(?:\\[0-9a-f]+ ?))*)\s*:/i],[PR.PR_COMMENT,/^\/\*[^*]*\*+(?:[^\/*][^*]*\*+)*\//],[PR.PR_COMMENT,/^(?:)/],[PR.PR_LITERAL,/^(?:\d+|\d*\.\d+)(?:%|[a-z]+)?/i],[PR.PR_LITERAL,/^#(?:[0-9a-f]{3}){1,2}/i],[PR.PR_PLAIN,/^-?(?:[_a-z]|(?:\\[\da-f]+ ?))(?:[_a-z\d\-]|\\(?:\\[\da-f]+ ?))*/i],[PR.PR_PUNCTUATION,/^[^\s\w\'\"]+/]]),["css"]);PR.registerLangHandler(PR.createSimpleLexer([],[[PR.PR_KEYWORD,/^-?(?:[_a-z]|(?:\\[\da-f]+ ?))(?:[_a-z\d\-]|\\(?:\\[\da-f]+ ?))*/i]]),["css-kw"]);PR.registerLangHandler(PR.createSimpleLexer([],[[PR.PR_STRING,/^[^\)\"\']+/]]),["css-str"]); \ No newline at end of file diff --git a/doc/classes/ContextMenu.html b/doc/classes/ContextMenu.html deleted file mode 100755 index 74dc0b085..000000000 --- a/doc/classes/ContextMenu.html +++ /dev/null @@ -1,212 +0,0 @@ - - - - - ContextMenu - - - - - - - - -
    -
    -
    -

    -
    -
    - API Docs for: -
    -
    -
    - -
    - -
    -
    -
    - Show: - - - - - - - -
    - -
    -
    -
    -

    ContextMenu Class

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

    ContextMenu from LiteGUI

    - -
    - -
    -

    Constructor

    -
    -

    ContextMenu

    - -
    - (
      -
    • - values -
    • -
    • - options -
    • -
    ) -
    - - - - - - - - -
    -

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

    - - - -
    - -
    - -
    - -
    -

    Parameters:

    - -
      -
    • - values - Array - - -
      -

      (allows object { title: "Nice text", callback: function ... })

      - -
      - -
    • -
    • - options - Object - - -
      -

      [optional] Some options:\

      -
        -
      • title: title to show on top of the menu
      • -
      • callback: function to call when an option is clicked, it receives the item information
      • -
      • ignore_item_callbacks: ignores the callback inside the item, it just calls the options.callback
      • -
      • event: you can pass a MouseEvent, this way the ContextMenu appears in that position
      • -
      - -
      - -
    • -
    -
    - - - -
    -
    - -
    - - -
    -
    -

    Item Index

    - - - - -
    - - - - -
    -
    -
    -
    -
    -
    -
    -
    - - - - - - - - - - diff --git a/doc/classes/LGraph.html b/doc/classes/LGraph.html deleted file mode 100755 index d3c5c3a46..000000000 --- a/doc/classes/LGraph.html +++ /dev/null @@ -1,2499 +0,0 @@ - - - - - LGraph - - - - - - - - -
    -
    -
    -

    -
    -
    - API Docs for: -
    -
    -
    - -
    - -
    -
    -
    - Show: - - - - - - - -
    - -
    -
    -
    -

    LGraph Class

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

    LGraph is the class that contain a full graph. We instantiate one and add nodes to it, and then we can run the execution loop.

    - -
    - -
    -

    Constructor

    -
    -

    LGraph

    - -
    - (
      -
    • - o -
    • -
    ) -
    - - - - - - - - -
    -

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

    - - - -
    - -
    - -
    - -
    -

    Parameters:

    - -
      -
    • - o - Object - - -
      -

      data from previous serialization [optional]

      - -
      - -
    • -
    -
    - - - -
    -
    - -
    - - -
    -
    -

    Item Index

    - -
    -

    Methods

    - - -
    - - - -
    - -
    -

    Methods

    - -
    -

    add

    - -
    - (
      -
    • - node -
    • -
    ) -
    - - - - - - - - -
    -

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

    - - - -
    - -
    -

    Adds a new node instasnce to this graph

    - -
    - -
    -

    Parameters:

    - -
      -
    • - node - LGraphNode - - -
      -

      the instance of the node

      - -
      - -
    • -
    -
    - - - -
    -
    -

    addGlobalInput

    - -
    - (
      -
    • - name -
    • -
    • - type -
    • -
    • - value -
    • -
    ) -
    - - - - - - - - -
    -

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

    - - - -
    - -
    -

    Tell this graph it has a global graph input of this type

    - -
    - -
    -

    Parameters:

    - -
      -
    • - name - String - - -
      - -
      - -
    • -
    • - type - String - - -
      - -
      - -
    • -
    • - value - - - -
      -

      [optional]

      - -
      - -
    • -
    -
    - - - -
    -
    -

    addOutput

    - -
    - (
      -
    • - name -
    • -
    • - type -
    • -
    • - value -
    • -
    ) -
    - - - - - - - - -
    -

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

    - - - -
    - -
    -

    Creates a global graph output

    - -
    - -
    -

    Parameters:

    - -
      -
    • - name - String - - -
      - -
      - -
    • -
    • - type - String - - -
      - -
      - -
    • -
    • - value - - - -
      - -
      - -
    • -
    -
    - - - -
    -
    -

    arrange

    - - () - - - - - - - - -
    -

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

    - - - -
    - -
    -

    Positions every node in a more readable manner

    - -
    - - - - -
    -
    -

    attachCanvas

    - -
    - (
      -
    • - graph_canvas -
    • -
    ) -
    - - - - - - - - -
    -

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

    - - - -
    - -
    -

    Attach Canvas to this graph

    - -
    - -
    -

    Parameters:

    - -
      -
    • - graph_canvas - GraphCanvas - - -
      - -
      - -
    • -
    -
    - - - -
    -
    -

    changeInputType

    - -
    - (
      -
    • - name -
    • -
    • - type -
    • -
    ) -
    - - - - - - - - -
    -

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

    - - - -
    - -
    -

    Changes the type of a global graph input

    - -
    - -
    -

    Parameters:

    - -
      -
    • - name - String - - -
      - -
      - -
    • -
    • - type - String - - -
      - -
      - -
    • -
    -
    - - - -
    -
    -

    changeOutputType

    - -
    - (
      -
    • - name -
    • -
    • - type -
    • -
    ) -
    - - - - - - - - -
    -

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

    - - - -
    - -
    -

    Changes the type of a global graph output

    - -
    - -
    -

    Parameters:

    - -
      -
    • - name - String - - -
      - -
      - -
    • -
    • - type - String - - -
      - -
      - -
    • -
    -
    - - - -
    -
    -

    clear

    - - () - - - - - - - - -
    -

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

    - - - -
    - -
    -

    Removes all nodes from this graph

    - -
    - - - - -
    -
    -

    clearTriggeredSlots

    - - () - - - - - - - - -
    -

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

    - - - -
    - -
    -

    clears the triggered slot animation in all links (stop visual animation)

    - -
    - - - - -
    -
    -

    configure

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

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

    - - - -
    - -
    -

    Configure a graph from a JSON string

    - -
    - -
    -

    Parameters:

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

      configure a graph from a JSON string

      - -
      - -
    • -
    • - returns - Boolean - - -
      -

      if there was any error parsing

      - -
      - -
    • -
    -
    - - - -
    -
    -

    detachCanvas

    - -
    - (
      -
    • - graph_canvas -
    • -
    ) -
    - - - - - - - - -
    -

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

    - - - -
    - -
    -

    Detach Canvas from this graph

    - -
    - -
    -

    Parameters:

    - -
      -
    • - graph_canvas - GraphCanvas - - -
      - -
      - -
    • -
    -
    - - - -
    -
    -

    findNodesByClass

    - -
    - (
      -
    • - classObject -
    • -
    ) -
    - - - Array - - - - - - - - -
    -

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

    - - - -
    - -
    -

    Returns a list of nodes that matches a class

    - -
    - -
    -

    Parameters:

    - -
      -
    • - classObject - Class - - -
      -

      the class itself (not an string)

      - -
      - -
    • -
    -
    - -
    -

    Returns:

    - -
    - Array: -

    a list with all the nodes of this type

    - -
    -
    - - -
    -
    -

    findNodesByTitle

    - -
    - (
      -
    • - name -
    • -
    ) -
    - - - Array - - - - - - - - -
    -

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

    - - - -
    - -
    -

    Returns a list of nodes that matches a name

    - -
    - -
    -

    Parameters:

    - -
      -
    • - name - String - - -
      -

      the name of the node to search

      - -
      - -
    • -
    -
    - -
    -

    Returns:

    - -
    - Array: -

    a list with all the nodes with this name

    - -
    -
    - - -
    -
    -

    findNodesByType

    - -
    - (
      -
    • - type -
    • -
    ) -
    - - - Array - - - - - - - - -
    -

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

    - - - -
    - -
    -

    Returns a list of nodes that matches a type

    - -
    - -
    -

    Parameters:

    - -
      -
    • - type - String - - -
      -

      the name of the node type

      - -
      - -
    • -
    -
    - -
    -

    Returns:

    - -
    - Array: -

    a list with all the nodes of this type

    - -
    -
    - - -
    -
    -

    getAncestors

    - - () - - - Array - - - - - - - - -
    -

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

    - - - -
    - -
    -

    Returns all the nodes that could affect this one (ancestors) by crawling all the inputs recursively. -It doesnt include the node itself

    - -
    - - -
    -

    Returns:

    - -
    - Array: -

    an array with all the LGraphNodes that affect this node, in order of execution

    - -
    -
    - - -
    -
    -

    getElapsedTime

    - - () - - - Number - - - - - - - - -
    -

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

    - - - -
    - -
    -

    Returns the amount of time it took to compute the latest iteration. Take into account that this number could be not correct -if the nodes are using graphical actions

    - -
    - - -
    -

    Returns:

    - -
    - Number: -

    number of milliseconds it took the last cycle

    - -
    -
    - - -
    -
    -

    getFixedTime

    - - () - - - Number - - - - - - - - -
    -

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

    - - - -
    - -
    -

    Returns the amount of time accumulated using the fixedtime_lapse var. This is used in context where the time increments should be constant

    - -
    - - -
    -

    Returns:

    - -
    - Number: -

    number of milliseconds the graph has been running

    - -
    -
    - - -
    -
    -

    getGroupOnPos

    - -
    - (
      -
    • - x -
    • -
    • - y -
    • -
    ) -
    - - - LGraphGroup - - - - - - - - -
    -

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

    - - - -
    - -
    -

    Returns the top-most group in that position

    - -
    - -
    -

    Parameters:

    - -
      -
    • - x - Number - - -
      -

      the x coordinate in canvas space

      - -
      - -
    • -
    • - y - Number - - -
      -

      the y coordinate in canvas space

      - -
      - -
    • -
    -
    - -
    -

    Returns:

    - -
    - LGraphGroup: -

    the group or null

    - -
    -
    - - -
    -
    -

    getInputData

    - -
    - (
      -
    • - name -
    • -
    ) -
    - - - - - - - - - - - -
    -

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

    - - - -
    - -
    -

    Returns the current value of a global graph input

    - -
    - -
    -

    Parameters:

    - -
      -
    • - name - String - - -
      - -
      - -
    • -
    -
    - -
    -

    Returns:

    - -
    - : -

    the data

    - -
    -
    - - -
    -
    -

    getNodeById

    - -
    - (
      -
    • - id -
    • -
    ) -
    - - - - - - - - -
    -

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

    - - - -
    - -
    -

    Returns a node by its id.

    - -
    - -
    -

    Parameters:

    - -
      -
    • - id - Number - - -
      - -
      - -
    • -
    -
    - - - -
    -
    -

    getNodeOnPos

    - -
    - (
      -
    • - x -
    • -
    • - y -
    • -
    • - nodes_list -
    • -
    ) -
    - - - LGraphNode - - - - - - - - -
    -

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

    - - - -
    - -
    -

    Returns the top-most node in this position of the canvas

    - -
    - -
    -

    Parameters:

    - -
      -
    • - x - Number - - -
      -

      the x coordinate in canvas space

      - -
      - -
    • -
    • - y - Number - - -
      -

      the y coordinate in canvas space

      - -
      - -
    • -
    • - nodes_list - Array - - -
      -

      a list with all the nodes to search from, by default is all the nodes in the graph

      - -
      - -
    • -
    -
    - -
    -

    Returns:

    - -
    - LGraphNode: -

    the node at this position or null

    - -
    -
    - - -
    -
    -

    getOutputData

    - -
    - (
      -
    • - name -
    • -
    ) -
    - - - - - - - - - - - -
    -

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

    - - - -
    - -
    -

    Returns the current value of a global graph output

    - -
    - -
    -

    Parameters:

    - -
      -
    • - name - String - - -
      - -
      - -
    • -
    -
    - -
    -

    Returns:

    - -
    - : -

    the data

    - -
    -
    - - -
    -
    -

    getTime

    - - () - - - Number - - - - - - - - -
    -

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

    - - - -
    - -
    -

    Returns the amount of time the graph has been running in milliseconds

    - -
    - - -
    -

    Returns:

    - -
    - Number: -

    number of milliseconds the graph has been running

    - -
    -
    - - -
    -
    -

    isLive

    - - () - - - - - - - - -
    -

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

    - - - -
    - -
    -

    returns if the graph is in live mode

    - -
    - - - - -
    -
    -

    remove

    - -
    - (
      -
    • - node -
    • -
    ) -
    - - - - - - - - -
    -

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

    - - - -
    - -
    -

    Removes a node from the graph

    - -
    - -
    -

    Parameters:

    - -
      -
    • - node - LGraphNode - - -
      -

      the instance of the node

      - -
      - -
    • -
    -
    - - - -
    -
    -

    removeInput

    - -
    - (
      -
    • - name -
    • -
    • - type -
    • -
    ) -
    - - - - - - - - -
    -

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

    - - - -
    - -
    -

    Removes a global graph input

    - -
    - -
    -

    Parameters:

    - -
      -
    • - name - String - - -
      - -
      - -
    • -
    • - type - String - - -
      - -
      - -
    • -
    -
    - - - -
    - -
    -

    removeOutput

    - -
    - (
      -
    • - name -
    • -
    ) -
    - - - - - - - - -
    -

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

    - - - -
    - -
    -

    Removes a global graph output

    - -
    - -
    -

    Parameters:

    - -
      -
    • - name - String - - -
      - -
      - -
    • -
    -
    - - - -
    -
    -

    renameInput

    - -
    - (
      -
    • - old_name -
    • -
    • - new_name -
    • -
    ) -
    - - - - - - - - -
    -

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

    - - - -
    - -
    -

    Changes the name of a global graph input

    - -
    - -
    -

    Parameters:

    - -
      -
    • - old_name - String - - -
      - -
      - -
    • -
    • - new_name - String - - -
      - -
      - -
    • -
    -
    - - - -
    -
    -

    renameOutput

    - -
    - (
      -
    • - old_name -
    • -
    • - new_name -
    • -
    ) -
    - - - - - - - - -
    -

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

    - - - -
    - -
    -

    Renames a global graph output

    - -
    - -
    -

    Parameters:

    - -
      -
    • - old_name - String - - -
      - -
      - -
    • -
    • - new_name - String - - -
      - -
      - -
    • -
    -
    - - - -
    -
    -

    runStep

    - -
    - (
      -
    • - num -
    • -
    ) -
    - - - - - - - - -
    -

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

    - - - -
    - -
    -

    Run N steps (cycles) of the graph

    - -
    - -
    -

    Parameters:

    - -
      -
    • - num - Number - - -
      -

      number of steps to run, default is 1

      - -
      - -
    • -
    -
    - - - -
    -
    -

    sendEventToAllNodes

    - -
    - (
      -
    • - eventname -
    • -
    • - params -
    • -
    ) -
    - - - - - - - - -
    -

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

    - - - -
    - -
    -

    Sends an event to all the nodes, useful to trigger stuff

    - -
    - -
    -

    Parameters:

    - -
      -
    • - eventname - String - - -
      -

      the name of the event (function to be called)

      - -
      - -
    • -
    • - params - Array - - -
      -

      parameters in array format

      - -
      - -
    • -
    -
    - - - -
    -
    -

    serialize

    - - () - - - Object - - - - - - - - -
    -

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

    - - - -
    - -
    -

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

    - -
    - - -
    -

    Returns:

    - -
    - Object: -

    value of the node

    - -
    -
    - - -
    -
    -

    setGlobalInputData

    - -
    - (
      -
    • - name -
    • -
    • - data -
    • -
    ) -
    - - - - - - - - -
    -

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

    - - - -
    - -
    -

    Assign a data to the global graph input

    - -
    - -
    -

    Parameters:

    - -
      -
    • - name - String - - -
      - -
      - -
    • -
    • - data - - - -
      - -
      - -
    • -
    -
    - - - -
    -
    -

    setOutputData

    - -
    - (
      -
    • - name -
    • -
    • - value -
    • -
    ) -
    - - - - - - - - -
    -

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

    - - - -
    - -
    -

    Assign a data to the global output

    - -
    - -
    -

    Parameters:

    - -
      -
    • - name - String - - -
      - -
      - -
    • -
    • - value - String - - -
      - -
      - -
    • -
    -
    - - - -
    -
    -

    start

    - -
    - (
      -
    • - interval -
    • -
    ) -
    - - - - - - - - -
    -

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

    - - - -
    - -
    -

    Starts running this graph every interval milliseconds.

    - -
    - -
    -

    Parameters:

    - -
      -
    • - interval - Number - - -
      -

      amount of milliseconds between executions, if 0 then it renders to the monitor refresh rate

      - -
      - -
    • -
    -
    - - - -
    -
    -

    stop execution

    - - () - - - - - - - - -
    -

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

    - - - -
    - -
    -

    Stops the execution loop of the graph

    - -
    - - - - -
    -
    -

    updateExecutionOrder

    - - () - - - - - - - - -
    -

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

    - - - -
    - -
    -

    Updates the graph execution order according to relevance of the nodes (nodes with only outputs have more relevance than -nodes with only inputs.

    - -
    - - - - -
    -
    - - - -
    -
    -
    -
    -
    -
    -
    -
    - - - - - - - - - - diff --git a/doc/classes/LGraphCanvas.html b/doc/classes/LGraphCanvas.html deleted file mode 100755 index 36d94b021..000000000 --- a/doc/classes/LGraphCanvas.html +++ /dev/null @@ -1,2011 +0,0 @@ - - - - - LGraphCanvas - - - - - - - - -
    -
    -
    -

    -
    -
    - API Docs for: -
    -
    -
    - -
    - -
    -
    -
    - Show: - - - - - - - -
    - -
    -
    -
    -

    LGraphCanvas Class

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

    marks as dirty the canvas, this way it will be rendered again

    - -
    - -
    -

    Constructor

    -
    -

    LGraphCanvas

    - -
    - (
      -
    • - canvas -
    • -
    • - graph -
    • -
    • - options -
    • -
    ) -
    - - - - - - - - -
    -

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

    - - - -
    - -
    - -
    - -
    -

    Parameters:

    - -
      -
    • - canvas - HTMLCanvas - - -
      -

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

      - -
      - -
    • -
    • - graph - LGraph - - -
      -

      [optional]

      - -
      - -
    • -
    • - options - Object - - -
      -

      [optional] { skip_rendering, autoresize }

      - -
      - -
    • -
    -
    - - - -
    -
    - -
    - - -
    -
    -

    Item Index

    - -
    -

    Methods

    - - -
    - - - -
    - -
    -

    Methods

    - -
    -

    adjustMouseEvent

    - - () - - - - - - - - -
    -

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

    - - - -
    - -
    -

    adds some useful properties to a mouse event, like the position in graph coordinates

    - -
    - - - - -
    -
    -

    bindEvents

    - - () - - - - - - - - -
    -

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

    - - - -
    - -
    -

    binds mouse, keyboard, touch and drag events to the canvas

    - -
    - - - - -
    -
    -

    bringToFront

    - - () - - - - - - - - -
    -

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

    - - - -
    - -
    -

    brings a node to front (above all other nodes)

    - -
    - - - - -
    -
    -

    centerOnNode

    - - () - - - - - - - - -
    -

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

    - - - -
    - -
    -

    centers the camera on a given node

    - -
    - - - - -
    -
    -

    clear

    - - () - - - - - - - - -
    -

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

    - - - -
    - -
    -

    clears all the data inside

    - -
    - - - - -
    -
    -

    closeSubgraph

    - -
    - (
      -
    • - assigns -
    • -
    ) -
    - - - - - - - - -
    -

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

    - - - -
    - -
    -

    closes a subgraph contained inside a node

    - -
    - -
    -

    Parameters:

    - -
      -
    • - assigns - LGraph - - -
      -

      a graph

      - -
      - -
    • -
    -
    - - - -
    -
    -

    computeVisibleNodes

    - - () - - - - - - - - -
    -

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

    - - - -
    - -
    -

    checks which nodes are visible (inside the camera area)

    - -
    - - - - -
    -
    -

    convertCanvasToOffset

    - - () - - - - - - - - -
    -

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

    - - - -
    - -
    -

    converts a coordinate from Canvas2D coordinates to graph space

    - -
    - - - - -
    -
    -

    convertOffsetToCanvas

    - - () - - - - - - - - -
    -

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

    - - - -
    - -
    -

    converts a coordinate from graph coordinates to canvas2D coordinates

    - -
    - - - - -
    -
    -

    deleteSelectedNodes

    - - () - - - - - - - - -
    -

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

    - - - -
    - -
    -

    deletes all nodes in the current selection from the graph

    - -
    - - - - -
    -
    -

    deselectAllNodes

    - - () - - - - - - - - -
    -

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

    - - - -
    - -
    -

    removes all nodes from the current selection

    - -
    - - - - -
    -
    -

    deselectNode

    - - () - - - - - - - - -
    -

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

    - - - -
    - -
    -

    removes a node from the current selection

    - -
    - - - - -
    -
    -

    draw

    - - () - - - - - - - - -
    -

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

    - - - -
    - -
    -

    renders the whole canvas content, by rendering in two separated canvas, one containing the background grid and the connections, and one containing the nodes)

    - -
    - - - - -
    -
    -

    drawBackCanvas

    - - () - - - - - - - - -
    -

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

    - - - -
    - -
    -

    draws the back canvas (the one containing the background and the connections)

    - -
    - - - - -
    -
    -

    drawConnections

    - - () - - - - - - - - -
    -

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

    - - - -
    - -
    -

    draws every connection visible in the canvas -OPTIMIZE THIS: precatch connections position instead of recomputing them every time

    - -
    - - - - -
    -
    -

    drawFrontCanvas

    - - () - - - - - - - - -
    -

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

    - - - -
    - -
    -

    draws the front canvas (the one containing all the nodes)

    - -
    - - - - -
    -
    -

    drawGroups

    - - () - - - - - - - - -
    -

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

    - - - -
    - -
    -

    draws every group area in the background

    - -
    - - - - -
    -
    -

    drawNode

    - - () - - - - - - - - -
    -

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

    - - - -
    - -
    -

    draws the given node inside the canvas

    - -
    - - - - -
    -
    -

    drawNodeShape

    - - () - - - - - - - - -
    -

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

    - - - -
    - -
    -

    draws the shape of the given node in the canvas

    - -
    - - - - -
    -
    -

    drawNodeWidgets

    - - () - - - - - - - - -
    -

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

    - - - -
    - -
    -

    draws the widgets stored inside a node

    - -
    - - - - -
    -
    -

    enableWebGL

    - - () - - - - - - - - -
    -

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

    - - - -
    - -
    -

    this function allows to render the canvas using WebGL instead of Canvas2D -this is useful if you plant to render 3D objects inside your nodes, it uses litegl.js for webgl and canvas2DtoWebGL to emulate the Canvas2D calls in webGL

    - -
    - - - - -
    -
    -

    getCanvasWindow

    - - () - - - Window - - - - - - - - -
    -

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

    - - - -
    - -
    -

    Used to attach the canvas in a popup

    - -
    - - -
    -

    Returns:

    - -
    - Window: -

    returns the window where the canvas is attached (the DOM root node)

    - -
    -
    - - -
    -
    -

    isOverNodeBox

    - - () - - - - - - - - -
    -

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

    - - - -
    - -
    -

    retuns true if a position (in graph space) is on top of a node little corner box

    - -
    - - - - -
    -
    -

    isOverNodeInput

    - - () - - - - - - - - -
    -

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

    - - - -
    - -
    -

    retuns true if a position (in graph space) is on top of a node input slot

    - -
    - - - - -
    -
    -

    openSubgraph

    - -
    - (
      -
    • - graph -
    • -
    ) -
    - - - - - - - - -
    -

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

    - - - -
    - -
    -

    opens a graph contained inside a node in the current graph

    - -
    - -
    -

    Parameters:

    - -
      -
    • - graph - LGraph - - -
      - -
      - -
    • -
    -
    - - - -
    -
    -

    processDrop

    - - () - - - - - - - - -
    -

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

    - - - -
    - -
    -

    process a item drop event on top the canvas

    - -
    - - - - -
    -
    -

    processKey

    - - () - - - - - - - - -
    -

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

    - - - -
    - -
    -

    process a key event

    - -
    - - - - -
    -
    -

    processMouseMove

    - - () - - - - - - - - -
    -

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

    - - - -
    - -
    -

    Called when a mouse move event has to be processed

    - -
    - - - - -
    -
    -

    processMouseUp

    - - () - - - - - - - - -
    -

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

    - - - -
    - -
    -

    Called when a mouse up event has to be processed

    - -
    - - - - -
    -
    -

    processMouseWheel

    - - () - - - - - - - - -
    -

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

    - - - -
    - -
    -

    Called when a mouse wheel event has to be processed

    - -
    - - - - -
    -
    -

    processNodeWidgets

    - - () - - - - - - - - -
    -

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

    - - - -
    - -
    -

    process an event on widgets

    - -
    - - - - -
    -
    -

    renderInfo

    - - () - - - - - - - - -
    -

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

    - - - -
    - -
    -

    draws some useful stats in the corner of the canvas

    - -
    - - - - -
    - -
    -

    resize

    - - () - - - - - - - - -
    -

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

    - - - -
    - -
    -

    resizes the canvas to a given size, if no size is passed, then it tries to fill the parentNode

    - -
    - - - - -
    -
    -

    selectNode

    - - () - - - - - - - - -
    -

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

    - - - -
    - -
    -

    selects a given node (or adds it to the current selection)

    - -
    - - - - -
    -
    -

    selectNodes

    - - () - - - - - - - - -
    -

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

    - - - -
    - -
    -

    selects several nodes (or adds them to the current selection)

    - -
    - - - - -
    -
    -

    sendToBack

    - - () - - - - - - - - -
    -

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

    - - - -
    - -
    -

    sends a node to the back (below all other nodes)

    - -
    - - - - -
    -
    -

    setCanvas

    - -
    - (
      -
    • - assigns -
    • -
    ) -
    - - - - - - - - -
    -

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

    - - - -
    - -
    -

    assigns a canvas

    - -
    - -
    -

    Parameters:

    - -
      -
    • - assigns - Canvas - - -
      -

      a canvas (also accepts the ID of the element (not a selector)

      - -
      - -
    • -
    -
    - - - -
    -
    -

    setGraph

    - -
    - (
      -
    • - graph -
    • -
    ) -
    - - - - - - - - -
    -

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

    - - - -
    - -
    -

    assigns a graph, you can reasign graphs to the same canvas

    - -
    - -
    -

    Parameters:

    - -
      -
    • - graph - LGraph - - -
      - -
      - -
    • -
    -
    - - - -
    -
    -

    setZoom

    - - () - - - - - - - - -
    -

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

    - - - -
    - -
    -

    changes the zoom level of the graph (default is 1), you can pass also a place used to pivot the zoom

    - -
    - - - - -
    -
    -

    startRendering

    - - () - - - - - - - - -
    -

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

    - - - -
    - -
    -

    starts rendering the content of the canvas when needed

    - -
    - - - - -
    -
    -

    stopRendering

    - - () - - - - - - - - -
    -

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

    - - - -
    - -
    -

    stops rendering the content of the canvas (to save resources)

    - -
    - - - - -
    -
    -

    switchLiveMode

    - - () - - - - - - - - -
    -

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

    - - - -
    - -
    -

    switches to live mode (node shapes are not rendered, only the content) -this feature was designed when graphs where meant to create user interfaces

    - -
    - - - - -
    -
    -

    unbindEvents

    - - () - - - - - - - - -
    -

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

    - - - -
    - -
    -

    unbinds mouse events from the canvas

    - -
    - - - - -
    -
    - - - -
    -
    -
    -
    -
    -
    -
    -
    - - - - - - - - - - diff --git a/doc/classes/LGraphNode.html b/doc/classes/LGraphNode.html deleted file mode 100755 index 83f52a946..000000000 --- a/doc/classes/LGraphNode.html +++ /dev/null @@ -1,2964 +0,0 @@ - - - - - LGraphNode - - - - - - - - -
    -
    -
    -

    -
    -
    - API Docs for: -
    -
    -
    - -
    - -
    -
    -
    - Show: - - - - - - - -
    - -
    -
    -
    -

    LGraphNode Class

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

    Base Class for all the node type classes

    - -
    - - -
    - - -
    -
    -

    Item Index

    - -
    -

    Methods

    - - -
    - - - -
    - -
    -

    Methods

    - -
    -

    addConnection

    - -
    - (
      -
    • - name -
    • -
    • - type -
    • -
    • - pos -
    • -
    • - direction -
    • -
    ) -
    - - - - - - - - -
    -

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

    - - - -
    - -
    -

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

    - -
    - -
    -

    Parameters:

    - -
      -
    • - name - String - - -
      - -
      - -
    • -
    • - type - String - - -
      -

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

      - -
      - -
    • -
    • - pos - x,y - - -
      -

      position of the connection inside the node

      - -
      - -
    • -
    • - direction - String - - -
      -

      if is input or output

      - -
      - -
    • -
    -
    - - - -
    -
    -

    addInput

    - -
    - (
      -
    • - name -
    • -
    • - type -
    • -
    • - extra_info -
    • -
    ) -
    - - - - - - - - -
    -

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

    - - - -
    - -
    -

    add a new input slot to use in this node

    - -
    - -
    -

    Parameters:

    - -
      -
    • - name - String - - -
      - -
      - -
    • -
    • - type - String - - -
      -

      string defining the input type ("vec3","number",...), it its a generic one use 0

      - -
      - -
    • -
    • - extra_info - Object - - -
      -

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

      - -
      - -
    • -
    -
    - - - -
    -
    -

    addInputs

    - -
    - (
      -
    • - array -
    • -
    ) -
    - - - - - - - - -
    -

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

    - - - -
    - -
    -

    add several new input slots in this node

    - -
    - -
    -

    Parameters:

    - -
      -
    • - array - Array - - -
      -

      of triplets like [[name,type,extra_info],[...]]

      - -
      - -
    • -
    -
    - - - -
    -
    -

    addOutput

    - -
    - (
      -
    • - name -
    • -
    • - type -
    • -
    • - extra_info -
    • -
    ) -
    - - - - - - - - -
    -

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

    - - - -
    - -
    -

    add a new output slot to use in this node

    - -
    - -
    -

    Parameters:

    - -
      -
    • - name - String - - -
      - -
      - -
    • -
    • - type - String - - -
      -

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

      - -
      - -
    • -
    • - extra_info - Object - - -
      -

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

      - -
      - -
    • -
    -
    - - - -
    -
    -

    addOutputs

    - -
    - (
      -
    • - array -
    • -
    ) -
    - - - - - - - - -
    -

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

    - - - -
    - -
    -

    add a new output slot to use in this node

    - -
    - -
    -

    Parameters:

    - -
      -
    • - array - Array - - -
      -

      of triplets like [[name,type,extra_info],[...]]

      - -
      - -
    • -
    -
    - - - -
    -
    -

    addProperty

    - -
    - (
      -
    • - name -
    • -
    • - default_value -
    • -
    • - type -
    • -
    • - extra_info -
    • -
    ) -
    - - - - - - - - -
    -

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

    - - - -
    - -
    -

    add a new property to this node

    - -
    - -
    -

    Parameters:

    - -
      -
    • - name - String - - -
      - -
      - -
    • -
    • - default_value - - - -
      - -
      - -
    • -
    • - type - String - - -
      -

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

      - -
      - -
    • -
    • - extra_info - Object - - -
      -

      this can be used to have special properties of the property (like values, etc)

      - -
      - -
    • -
    -
    - - - -
    -
    -

    addWidget

    - - () - - - Object - - - - - - - - -
    -

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

    - - - -
    - -
    -

    Allows to pass

    - -
    - - -
    -

    Returns:

    - -
    - Object: -

    the created widget

    - -
    -
    - - -
    -
    -

    clearTriggeredSlot

    - -
    - (
      -
    • - slot -
    • -
    • - link_id -
    • -
    ) -
    - - - - - - - - -
    -

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

    - - - -
    - -
    -

    clears the trigger slot animation

    - -
    - -
    -

    Parameters:

    - -
      -
    • - slot - Number - - -
      -

      the index of the output slot

      - -
      - -
    • -
    • - link_id - Number - - -
      -

      [optional] in case you want to trigger and specific output link in a slot

      - -
      - -
    • -
    -
    - - - -
    -
    -

    collapse

    - - () - - - - - - - - -
    -

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

    - - - -
    - -
    -

    Collapse the node to make it smaller on the canvas

    - -
    - - - - -
    -
    -

    computeSize

    - -
    - (
      -
    • - minHeight -
    • -
    ) -
    - - - Number - - - - - - - - -
    -

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

    - - - -
    - -
    -

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

    - -
    - -
    -

    Parameters:

    - -
      -
    • - minHeight - Number - - -
      - -
      - -
    • -
    -
    - -
    -

    Returns:

    - -
    - Number: -

    the total size

    - -
    -
    - - -
    -
    -

    configure

    - - () - - - - - - - - -
    -

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

    - - - -
    - -
    -

    configure a node from an object containing the serialized info

    - -
    - - - - -
    -
    -

    connect

    - -
    - (
      -
    • - slot -
    • -
    • - node -
    • -
    • - target_slot -
    • -
    ) -
    - - - Object - - - - - - - - -
    -

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

    - - - -
    - -
    -

    connect this node output to the input of another node

    - -
    - -
    -

    Parameters:

    - -
      -
    • - slot - Number_or_string - - -
      -

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

      - -
      - -
    • -
    • - node - LGraphNode - - -
      -

      the target node

      - -
      - -
    • -
    • - target_slot - Number_or_string - - -
      -

      the input slot of the target node (could be the number of the slot or the string with the name of the slot, or -1 to connect a trigger)

      - -
      - -
    • -
    -
    - -
    -

    Returns:

    - -
    - Object: -

    the link_info is created, otherwise null

    - -
    -
    - - -
    -
    -

    disconnectInput

    - -
    - (
      -
    • - slot -
    • -
    ) -
    - - - Boolean - - - - - - - - -
    -

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

    - - - -
    - -
    -

    disconnect one input

    - -
    - -
    -

    Parameters:

    - -
      -
    • - slot - Number_or_string - - -
      -

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

      - -
      - -
    • -
    -
    - -
    -

    Returns:

    - -
    - Boolean: -

    if it was disconnected succesfully

    - -
    -
    - - -
    -
    -

    disconnectOutput

    - -
    - (
      -
    • - slot -
    • -
    • - target_node -
    • -
    ) -
    - - - Boolean - - - - - - - - -
    -

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

    - - - -
    - -
    -

    disconnect one output to an specific node

    - -
    - -
    -

    Parameters:

    - -
      -
    • - slot - Number_or_string - - -
      -

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

      - -
      - -
    • -
    • - target_node - LGraphNode - - -
      -

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

      - -
      - -
    • -
    -
    - -
    -

    Returns:

    - -
    - Boolean: -

    if it was disconnected succesfully

    - -
    -
    - - -
    -
    -

    findInputSlot

    - -
    - (
      -
    • - name -
    • -
    ) -
    - - - Number - - - - - - - - -
    -

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

    - - - -
    - -
    -

    returns the input slot with a given name (used for dynamic slots), -1 if not found

    - -
    - -
    -

    Parameters:

    - -
      -
    • - name - String - - -
      -

      the name of the slot

      - -
      - -
    • -
    -
    - -
    -

    Returns:

    - -
    - Number: -

    the slot (-1 if not found)

    - -
    -
    - - -
    -
    -

    findOutputSlot

    - -
    - (
      -
    • - name -
    • -
    ) -
    - - - Number - - - - - - - - -
    -

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

    - - - -
    - -
    -

    returns the output slot with a given name (used for dynamic slots), -1 if not found

    - -
    - -
    -

    Parameters:

    - -
      -
    • - name - String - - -
      -

      the name of the slot

      - -
      - -
    • -
    -
    - -
    -

    Returns:

    - -
    - Number: -

    the slot (-1 if not found)

    - -
    -
    - - -
    -
    -

    getBounding

    - - () - - - Float32Array4 - - - - - - - - -
    -

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

    - - - -
    - -
    -

    returns the bounding of the object, used for rendering purposes -bounding is: [topleft_cornerx, topleft_cornery, width, height]

    - -
    - - -
    -

    Returns:

    - -
    - Float32Array4: -

    the total size

    - -
    -
    - - -
    -
    -

    getConnectionPos

    - -
    - (
      -
    • - is_input -
    • -
    • - slot -
    • -
    • - out -
    • -
    ) -
    - - - x,y - - - - - - - - -
    -

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

    - - - -
    - -
    -

    returns the center of a connection point in canvas coords

    - -
    - -
    -

    Parameters:

    - -
      -
    • - is_input - Boolean - - -
      -

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

      - -
      - -
    • -
    • - slot - Number_or_string - - -
      -

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

      - -
      - -
    • -
    • - out - Vec2 - - -
      -

      [optional] a place to store the output, to free garbage

      - -
      - -
    • -
    -
    - -
    -

    Returns:

    - -
    - x,y: -

    the position

    - -
    -
    - - -
    -
    -

    getInputData

    - -
    - (
      -
    • - slot -
    • -
    • - force_update -
    • -
    ) -
    - - - - - - - - - - - -
    -

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

    - - - -
    - -
    -

    Retrieves the input data (data traveling through the connection) from one slot

    - -
    - -
    -

    Parameters:

    - -
      -
    • - slot - Number - - -
      - -
      - -
    • -
    • - force_update - Boolean - - -
      -

      if set to true it will force the connected node of this slot to output data into this link

      - -
      - -
    • -
    -
    - -
    -

    Returns:

    - -
    - : -

    data or if it is not connected returns undefined

    - -
    -
    - - -
    -
    -

    getInputDataByName

    - -
    - (
      -
    • - slot_name -
    • -
    • - force_update -
    • -
    ) -
    - - - - - - - - - - - -
    -

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

    - - - -
    - -
    -

    Retrieves the input data from one slot using its name instead of slot number

    - -
    - -
    -

    Parameters:

    - -
      -
    • - slot_name - String - - -
      - -
      - -
    • -
    • - force_update - Boolean - - -
      -

      if set to true it will force the connected node of this slot to output data into this link

      - -
      - -
    • -
    -
    - -
    -

    Returns:

    - -
    - : -

    data or if it is not connected returns null

    - -
    -
    - - -
    -
    -

    getInputDataType

    - -
    - (
      -
    • - slot -
    • -
    ) -
    - - - String - - - - - - - - -
    -

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

    - - - -
    - -
    -

    Retrieves the input data type (in case this supports multiple input types)

    - -
    - -
    -

    Parameters:

    - -
      -
    • - slot - Number - - -
      - -
      - -
    • -
    -
    - -
    -

    Returns:

    - -
    - String: -

    datatype in string format

    - -
    -
    - - -
    -
    -

    getInputInfo

    - -
    - (
      -
    • - slot -
    • -
    ) -
    - - - Object - - - - - - - - -
    -

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

    - - - -
    - -
    -

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

    - -
    - -
    -

    Parameters:

    - -
      -
    • - slot - Number - - -
      - -
      - -
    • -
    -
    - -
    -

    Returns:

    - -
    - Object: -

    object or null { link: id, name: string, type: string or 0 }

    - -
    -
    - - -
    -
    -

    getInputNode

    - -
    - (
      -
    • - slot -
    • -
    ) -
    - - - LGraphNode - - - - - - - - -
    -

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

    - - - -
    - -
    -

    returns the node connected in the input slot

    - -
    - -
    -

    Parameters:

    - -
      -
    • - slot - Number - - -
      - -
      - -
    • -
    -
    - -
    -

    Returns:

    - -
    - LGraphNode: -

    node or null

    - -
    -
    - - -
    -
    -

    getInputOrProperty

    - -
    - (
      -
    • - name -
    • -
    ) -
    - - - - - - - - - - - -
    -

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

    - - - -
    - -
    -

    returns the value of an input with this name, otherwise checks if there is a property with that name

    - -
    - -
    -

    Parameters:

    - -
      -
    • - name - String - - -
      - -
      - -
    • -
    -
    - -
    -

    Returns:

    - -
    - : -

    value

    - -
    -
    - - -
    -
    -

    getOutputData

    - -
    - (
      -
    • - slot -
    • -
    ) -
    - - - Object - - - - - - - - -
    -

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

    - - - -
    - -
    -

    tells you the last output data that went in that slot

    - -
    - -
    -

    Parameters:

    - -
      -
    • - slot - Number - - -
      - -
      - -
    • -
    -
    - -
    -

    Returns:

    - -
    - Object: -

    object or null

    - -
    -
    - - -
    -
    -

    getOutputInfo

    - -
    - (
      -
    • - slot -
    • -
    ) -
    - - - Object - - - - - - - - -
    -

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

    - - - -
    - -
    -

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

    - -
    - -
    -

    Parameters:

    - -
      -
    • - slot - Number - - -
      - -
      - -
    • -
    -
    - -
    -

    Returns:

    - -
    - Object: -

    object or null { name: string, type: string, links: [ ids of links in number ] }

    - -
    -
    - - -
    -
    -

    getOutputNodes

    - -
    - (
      -
    • - slot -
    • -
    ) -
    - - - Array - - - - - - - - -
    -

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

    - - - -
    - -
    -

    retrieves all the nodes connected to this output slot

    - -
    - -
    -

    Parameters:

    - -
      -
    • - slot - Number - - -
      - -
      - -
    • -
    -
    - -
    -

    Returns:

    - -
    - Array: -
    -
    - - -
    -
    -

    getSlotInPosition

    - -
    - (
      -
    • - x -
    • -
    • - y -
    • -
    ) -
    - - - Object - - - - - - - - -
    -

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

    - - - -
    - -
    -

    checks if a point is inside a node slot, and returns info about which slot

    - -
    - -
    -

    Parameters:

    - -
      -
    • - x - Number - - -
      - -
      - -
    • -
    • - y - Number - - -
      - -
      - -
    • -
    -
    - -
    -

    Returns:

    - -
    - Object: -

    if found the object contains { input|output: slot object, slot: number, link_pos: [x,y] }

    - -
    -
    - - -
    -
    -

    getTitle

    - - () - - - - - - - - -
    -

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

    - - - -
    - -
    -

    get the title string

    - -
    - - - - -
    -
    -

    isAnyOutputConnected

    - - () - - - Boolean - - - - - - - - -
    -

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

    - - - -
    - -
    -

    tells you if there is any connection in the output slots

    - -
    - - -
    -

    Returns:

    - -
    - Boolean: -
    -
    - - -
    -
    -

    isInputConnected

    - -
    - (
      -
    • - slot -
    • -
    ) -
    - - - Boolean - - - - - - - - -
    -

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

    - - - -
    - -
    -

    tells you if there is a connection in one input slot

    - -
    - -
    -

    Parameters:

    - -
      -
    • - slot - Number - - -
      - -
      - -
    • -
    -
    - -
    -

    Returns:

    - -
    - Boolean: -
    -
    - - -
    -
    -

    isOutputConnected

    - -
    - (
      -
    • - slot -
    • -
    ) -
    - - - Boolean - - - - - - - - -
    -

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

    - - - -
    - -
    -

    tells you if there is a connection in one output slot

    - -
    - -
    -

    Parameters:

    - -
      -
    • - slot - Number - - -
      - -
      - -
    • -
    -
    - -
    -

    Returns:

    - -
    - Boolean: -
    -
    - - -
    -
    -

    isPointInside

    - -
    - (
      -
    • - x -
    • -
    • - y -
    • -
    ) -
    - - - Boolean - - - - - - - - -
    -

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

    - - - -
    - -
    -

    checks if a point is inside the shape of a node

    - -
    - -
    -

    Parameters:

    - -
      -
    • - x - Number - - -
      - -
      - -
    • -
    • - y - Number - - -
      - -
      - -
    • -
    -
    - -
    -

    Returns:

    - -
    - Boolean: -
    -
    - - -
    -
    -

    pin

    - - () - - - - - - - - -
    -

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

    - - - -
    - -
    -

    Forces the node to do not move or realign on Z

    - -
    - - - - -
    -
    -

    removeInput

    - -
    - (
      -
    • - slot -
    • -
    ) -
    - - - - - - - - -
    -

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

    - - - -
    - -
    -

    remove an existing input slot

    - -
    - -
    -

    Parameters:

    - -
      -
    • - slot - Number - - -
      - -
      - -
    • -
    -
    - - - -
    -
    -

    removeOutput

    - -
    - (
      -
    • - slot -
    • -
    ) -
    - - - - - - - - -
    -

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

    - - - -
    - -
    -

    remove an existing output slot

    - -
    - -
    -

    Parameters:

    - -
      -
    • - slot - Number - - -
      - -
      - -
    • -
    -
    - - - -
    -
    -

    serialize

    - - () - - - - - - - - -
    -

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

    - - - -
    - -
    -

    serialize the content

    - -
    - - - - -
    -
    -

    setOutputData

    - -
    - (
      -
    • - slot -
    • -
    • - data -
    • -
    ) -
    - - - - - - - - -
    -

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

    - - - -
    - -
    -

    sets the output data

    - -
    - -
    -

    Parameters:

    - -
      -
    • - slot - Number - - -
      - -
      - -
    • -
    • - data - - - -
      - -
      - -
    • -
    -
    - - - -
    -
    -

    setOutputDataType

    - -
    - (
      -
    • - slot -
    • -
    • - datatype -
    • -
    ) -
    - - - - - - - - -
    -

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

    - - - -
    - -
    -

    sets the output data type, useful when you want to be able to overwrite the data type

    - -
    - -
    -

    Parameters:

    - -
      -
    • - slot - Number - - -
      - -
      - -
    • -
    • - datatype - String - - -
      - -
      - -
    • -
    -
    - - - -
    -
    -

    toString

    - - () - - - - - - - - -
    -

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

    - - - -
    - -
    -

    serialize and stringify

    - -
    - - - - -
    -
    -

    trigger

    - -
    - (
      -
    • - event -
    • -
    • - param -
    • -
    ) -
    - - - - - - - - -
    -

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

    - - - -
    - -
    -

    Triggers an event in this node, this will trigger any output with the same name

    - -
    - -
    -

    Parameters:

    - -
      -
    • - event - String - - -
      -

      name ( "on_play", ... ) if action is equivalent to false then the event is send to all

      - -
      - -
    • -
    • - param - - - -
      - -
      - -
    • -
    -
    - - - -
    -
    -

    triggerSlot

    - -
    - (
      -
    • - slot -
    • -
    • - param -
    • -
    • - link_id -
    • -
    ) -
    - - - - - - - - -
    -

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

    - - - -
    - -
    -

    Triggers an slot event in this node

    - -
    - -
    -

    Parameters:

    - -
      -
    • - slot - Number - - -
      -

      the index of the output slot

      - -
      - -
    • -
    • - param - - - -
      - -
      - -
    • -
    • - link_id - Number - - -
      -

      [optional] in case you want to trigger and specific output link in a slot

      - -
      - -
    • -
    -
    - - - -
    -
    - - - -
    -
    -
    -
    -
    -
    -
    -
    - - - - - - - - - - diff --git a/doc/classes/LiteGraph.html b/doc/classes/LiteGraph.html deleted file mode 100755 index a748f1536..000000000 --- a/doc/classes/LiteGraph.html +++ /dev/null @@ -1,697 +0,0 @@ - - - - - LiteGraph - - - - - - - - -
    -
    -
    -

    -
    -
    - API Docs for: -
    -
    -
    - -
    - -
    -
    -
    - Show: - - - - - - - -
    - -
    -
    -
    -

    LiteGraph Class

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

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

    - -
    - -
    -

    Constructor

    -
    -

    LiteGraph

    - - () - - - - - - - - -
    -

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

    - - - -
    - -
    - -
    - - - - -
    -
    - -
    - - -
    -
    -

    Item Index

    - -
    -

    Methods

    - - -
    - - - -
    - -
    -

    Methods

    - -
    -

    addNodeMethod

    - -
    - (
      -
    • - func -
    • -
    ) -
    - - - - - - - - -
    -

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

    - - - -
    - -
    -

    Adds this method to all nodetypes, existing and to be created -(You can add it to LGraphNode.prototype but then existing node types wont have it)

    - -
    - -
    -

    Parameters:

    - -
      -
    • - func - Function - - -
      - -
      - -
    • -
    -
    - - - -
    -
    -

    createNode

    - -
    - (
      -
    • - type -
    • -
    • - name -
    • -
    • - options -
    • -
    ) -
    - - - - - - - - -
    -

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

    - - - -
    - -
    -

    Create a node of a given type with a name. The node is not attached to any graph yet.

    - -
    - -
    -

    Parameters:

    - -
      -
    • - type - String - - -
      -

      full name of the node class. p.e. "math/sin"

      - -
      - -
    • -
    • - name - String - - -
      -

      a name to distinguish from other nodes

      - -
      - -
    • -
    • - options - Object - - -
      -

      to set options

      - -
      - -
    • -
    -
    - - - -
    -
    -

    getNodeType

    - -
    - (
      -
    • - type -
    • -
    ) -
    - - - Class - - - - - - - - -
    -

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

    - - - -
    - -
    -

    Returns a registered node type with a given name

    - -
    - -
    -

    Parameters:

    - -
      -
    • - type - String - - -
      -

      full name of the node class. p.e. "math/sin"

      - -
      - -
    • -
    -
    - -
    -

    Returns:

    - -
    - Class: -

    the node class

    - -
    -
    - - -
    -
    -

    getNodeType

    - -
    - (
      -
    • - category -
    • -
    ) -
    - - - Array - - - - - - - - -
    -

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

    - - - -
    - -
    -

    Returns a list of node types matching one category

    - -
    - -
    -

    Parameters:

    - -
      -
    • - category - String - - -
      -

      category name

      - -
      - -
    • -
    -
    - -
    -

    Returns:

    - -
    - Array: -

    array with all the node classes

    - -
    -
    - - -
    -
    -

    getNodeTypesCategories

    - - () - - - Array - - - - - - - - -
    -

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

    - - - -
    - -
    -

    Returns a list with all the node type categories

    - -
    - - -
    -

    Returns:

    - -
    - Array: -

    array with all the names of the categories

    - -
    -
    - - -
    -
    -

    registerNodeType

    - -
    - (
      -
    • - type -
    • -
    • - base_class -
    • -
    ) -
    - - - - - - - - -
    -

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

    - - - -
    - -
    -

    Register a node class so it can be listed when the user wants to create a new one

    - -
    - -
    -

    Parameters:

    - -
      -
    • - type - String - - -
      -

      name of the node and path

      - -
      - -
    • -
    • - base_class - Class - - -
      -

      class containing the structure of a node

      - -
      - -
    • -
    -
    - - - -
    -
    -

    wrapFunctionAsNode

    - -
    - (
      -
    • - name -
    • -
    • - func -
    • -
    • - param_types -
    • -
    • - return_type -
    • -
    • - properties -
    • -
    ) -
    - - - - - - - - -
    -

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

    - - - -
    - -
    -

    Create a new node type by passing a function, it wraps it with a propper 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.

    - -
    - -
    -

    Parameters:

    - -
      -
    • - name - String - - -
      -

      node name with namespace (p.e.: 'math/sum')

      - -
      - -
    • -
    • - func - Function - - -
      - -
      - -
    • -
    • - param_types - Array - - -
      -

      [optional] an array containing the type of every parameter, otherwise parameters will accept any type

      - -
      - -
    • -
    • - return_type - String - - -
      -

      [optional] string with the return type, otherwise it will be generic

      - -
      - -
    • -
    • - properties - Object - - -
      -

      [optional] properties to be configurable

      - -
      - -
    • -
    -
    - - - -
    -
    - - - -
    -
    -
    -
    -
    -
    -
    -
    - - - - - - - - - - diff --git a/doc/classes/index.html b/doc/classes/index.html deleted file mode 100755 index 487fe15b2..000000000 --- a/doc/classes/index.html +++ /dev/null @@ -1,10 +0,0 @@ - - - - Redirector - - - - Click here to redirect - - diff --git a/doc/data.json b/doc/data.json deleted file mode 100755 index 8629b46d7..000000000 --- a/doc/data.json +++ /dev/null @@ -1,2114 +0,0 @@ -{ - "project": {}, - "files": { - "../src/litegraph.js": { - "name": "../src/litegraph.js", - "modules": {}, - "classes": { - "LiteGraph": 1, - "LGraph": 1, - "LGraphNode": 1, - "LGraphCanvas": 1, - "ContextMenu": 1 - }, - "fors": {}, - "namespaces": {} - } - }, - "modules": {}, - "classes": { - "LiteGraph": { - "name": "LiteGraph", - "shortname": "LiteGraph", - "classitems": [], - "plugins": [], - "extensions": [], - "plugin_for": [], - "extension_for": [], - "file": "../src/litegraph.js", - "line": 6, - "description": "The Global Scope. It contains all the registered node classes.", - "is_constructor": 1 - }, - "LGraph": { - "name": "LGraph", - "shortname": "LGraph", - "classitems": [], - "plugins": [], - "extensions": [], - "plugin_for": [], - "extension_for": [], - "file": "../src/litegraph.js", - "line": 438, - "description": "LGraph is the class that contain a full graph. We instantiate one and add nodes to it, and then we can run the execution loop.", - "is_constructor": 1, - "params": [ - { - "name": "o", - "description": "data from previous serialization [optional]", - "type": "Object" - } - ] - }, - "LGraphNode": { - "name": "LGraphNode", - "shortname": "LGraphNode", - "classitems": [], - "plugins": [], - "extensions": [], - "plugin_for": [], - "extension_for": [], - "file": "../src/litegraph.js", - "line": 1830, - "description": "Base Class for all the node type classes", - "params": [ - { - "name": "name", - "description": "a name for the node", - "type": "String" - } - ] - }, - "LGraphCanvas": { - "name": "LGraphCanvas", - "shortname": "LGraphCanvas", - "classitems": [], - "plugins": [], - "extensions": [], - "plugin_for": [], - "extension_for": [], - "file": "../src/litegraph.js", - "line": 4164, - "description": "marks as dirty the canvas, this way it will be rendered again", - "is_constructor": 1, - "params": [ - { - "name": "canvas", - "description": "the canvas where you want to render (it accepts a selector in string format or the canvas element itself)", - "type": "HTMLCanvas" - }, - { - "name": "graph", - "description": "[optional]", - "type": "LGraph" - }, - { - "name": "options", - "description": "[optional] { skip_rendering, autoresize }", - "type": "Object" - } - ], - "itemtype": "method" - }, - "ContextMenu": { - "name": "ContextMenu", - "shortname": "ContextMenu", - "classitems": [], - "plugins": [], - "extensions": [], - "plugin_for": [], - "extension_for": [], - "file": "../src/litegraph.js", - "line": 8395, - "description": "ContextMenu from LiteGUI", - "is_constructor": 1, - "params": [ - { - "name": "values", - "description": "(allows object { title: \"Nice text\", callback: function ... })", - "type": "Array" - }, - { - "name": "options", - "description": "[optional] Some options:\\\n- title: title to show on top of the menu\n- callback: function to call when an option is clicked, it receives the item information\n- ignore_item_callbacks: ignores the callback inside the item, it just calls the options.callback\n- event: you can pass a MouseEvent, this way the ContextMenu appears in that position", - "type": "Object" - } - ] - } - }, - "elements": {}, - "classitems": [ - { - "file": "../src/litegraph.js", - "line": 93, - "description": "Register a node class so it can be listed when the user wants to create a new one", - "itemtype": "method", - "name": "registerNodeType", - "params": [ - { - "name": "type", - "description": "name of the node and path", - "type": "String" - }, - { - "name": "base_class", - "description": "class containing the structure of a node", - "type": "Class" - } - ], - "class": "LiteGraph" - }, - { - "file": "../src/litegraph.js", - "line": 160, - "description": "Create a new node type by passing a function, it wraps it with a propper class and generates inputs according to the parameters of the function.\nUseful to wrap simple methods that do not require properties, and that only process some input to generate an output.", - "itemtype": "method", - "name": "wrapFunctionAsNode", - "params": [ - { - "name": "name", - "description": "node name with namespace (p.e.: 'math/sum')", - "type": "String" - }, - { - "name": "func", - "description": "", - "type": "Function" - }, - { - "name": "param_types", - "description": "[optional] an array containing the type of every parameter, otherwise parameters will accept any type", - "type": "Array" - }, - { - "name": "return_type", - "description": "[optional] string with the return type, otherwise it will be generic", - "type": "String" - }, - { - "name": "properties", - "description": "[optional] properties to be configurable", - "type": "Object" - } - ], - "class": "LiteGraph" - }, - { - "file": "../src/litegraph.js", - "line": 193, - "description": "Adds this method to all nodetypes, existing and to be created\n(You can add it to LGraphNode.prototype but then existing node types wont have it)", - "itemtype": "method", - "name": "addNodeMethod", - "params": [ - { - "name": "func", - "description": "", - "type": "Function" - } - ], - "class": "LiteGraph" - }, - { - "file": "../src/litegraph.js", - "line": 211, - "description": "Create a node of a given type with a name. The node is not attached to any graph yet.", - "itemtype": "method", - "name": "createNode", - "params": [ - { - "name": "type", - "description": "full name of the node class. p.e. \"math/sin\"", - "type": "String" - }, - { - "name": "name", - "description": "a name to distinguish from other nodes", - "type": "String" - }, - { - "name": "options", - "description": "to set options", - "type": "Object" - } - ], - "class": "LiteGraph" - }, - { - "file": "../src/litegraph.js", - "line": 270, - "description": "Returns a registered node type with a given name", - "itemtype": "method", - "name": "getNodeType", - "params": [ - { - "name": "type", - "description": "full name of the node class. p.e. \"math/sin\"", - "type": "String" - } - ], - "return": { - "description": "the node class", - "type": "Class" - }, - "class": "LiteGraph" - }, - { - "file": "../src/litegraph.js", - "line": 281, - "description": "Returns a list of node types matching one category", - "itemtype": "method", - "name": "getNodeType", - "params": [ - { - "name": "category", - "description": "category name", - "type": "String" - } - ], - "return": { - "description": "array with all the node classes", - "type": "Array" - }, - "class": "LiteGraph" - }, - { - "file": "../src/litegraph.js", - "line": 309, - "description": "Returns a list with all the node type categories", - "itemtype": "method", - "name": "getNodeTypesCategories", - "return": { - "description": "array with all the names of the categories", - "type": "Array" - }, - "class": "LiteGraph" - }, - { - "file": "../src/litegraph.js", - "line": 468, - "description": "Removes all nodes from this graph", - "itemtype": "method", - "name": "clear", - "class": "LGraph" - }, - { - "file": "../src/litegraph.js", - "line": 531, - "description": "Attach Canvas to this graph", - "itemtype": "method", - "name": "attachCanvas", - "params": [ - { - "name": "graph_canvas", - "description": "", - "type": "GraphCanvas" - } - ], - "class": "LGraph" - }, - { - "file": "../src/litegraph.js", - "line": 550, - "description": "Detach Canvas from this graph", - "itemtype": "method", - "name": "detachCanvas", - "params": [ - { - "name": "graph_canvas", - "description": "", - "type": "GraphCanvas" - } - ], - "class": "LGraph" - }, - { - "file": "../src/litegraph.js", - "line": 567, - "description": "Starts running this graph every interval milliseconds.", - "itemtype": "method", - "name": "start", - "params": [ - { - "name": "interval", - "description": "amount of milliseconds between executions, if 0 then it renders to the monitor refresh rate", - "type": "Number" - } - ], - "class": "LGraph" - }, - { - "file": "../src/litegraph.js", - "line": 609, - "description": "Stops the execution loop of the graph", - "itemtype": "method", - "name": "stop execution", - "class": "LGraph" - }, - { - "file": "../src/litegraph.js", - "line": 634, - "description": "Run N steps (cycles) of the graph", - "itemtype": "method", - "name": "runStep", - "params": [ - { - "name": "num", - "description": "number of steps to run, default is 1", - "type": "Number" - } - ], - "class": "LGraph" - }, - { - "file": "../src/litegraph.js", - "line": 716, - "description": "Updates the graph execution order according to relevance of the nodes (nodes with only outputs have more relevance than\nnodes with only inputs.", - "itemtype": "method", - "name": "updateExecutionOrder", - "class": "LGraph" - }, - { - "file": "../src/litegraph.js", - "line": 848, - "description": "Returns all the nodes that could affect this one (ancestors) by crawling all the inputs recursively.\nIt doesnt include the node itself", - "itemtype": "method", - "name": "getAncestors", - "return": { - "description": "an array with all the LGraphNodes that affect this node, in order of execution", - "type": "Array" - }, - "class": "LGraph" - }, - { - "file": "../src/litegraph.js", - "line": 885, - "description": "Positions every node in a more readable manner", - "itemtype": "method", - "name": "arrange", - "class": "LGraph" - }, - { - "file": "../src/litegraph.js", - "line": 929, - "description": "Returns the amount of time the graph has been running in milliseconds", - "itemtype": "method", - "name": "getTime", - "return": { - "description": "number of milliseconds the graph has been running", - "type": "Number" - }, - "class": "LGraph" - }, - { - "file": "../src/litegraph.js", - "line": 939, - "description": "Returns the amount of time accumulated using the fixedtime_lapse var. This is used in context where the time increments should be constant", - "itemtype": "method", - "name": "getFixedTime", - "return": { - "description": "number of milliseconds the graph has been running", - "type": "Number" - }, - "class": "LGraph" - }, - { - "file": "../src/litegraph.js", - "line": 950, - "description": "Returns the amount of time it took to compute the latest iteration. Take into account that this number could be not correct\nif the nodes are using graphical actions", - "itemtype": "method", - "name": "getElapsedTime", - "return": { - "description": "number of milliseconds it took the last cycle", - "type": "Number" - }, - "class": "LGraph" - }, - { - "file": "../src/litegraph.js", - "line": 962, - "description": "Sends an event to all the nodes, useful to trigger stuff", - "itemtype": "method", - "name": "sendEventToAllNodes", - "params": [ - { - "name": "eventname", - "description": "the name of the event (function to be called)", - "type": "String" - }, - { - "name": "params", - "description": "parameters in array format", - "type": "Array" - } - ], - "class": "LGraph" - }, - { - "file": "../src/litegraph.js", - "line": 1004, - "description": "Adds a new node instasnce to this graph", - "itemtype": "method", - "name": "add", - "params": [ - { - "name": "node", - "description": "the instance of the node", - "type": "LGraphNode" - } - ], - "class": "LGraph" - }, - { - "file": "../src/litegraph.js", - "line": 1068, - "description": "Removes a node from the graph", - "itemtype": "method", - "name": "remove", - "params": [ - { - "name": "node", - "description": "the instance of the node", - "type": "LGraphNode" - } - ], - "class": "LGraph" - }, - { - "file": "../src/litegraph.js", - "line": 1149, - "description": "Returns a node by its id.", - "itemtype": "method", - "name": "getNodeById", - "params": [ - { - "name": "id", - "description": "", - "type": "Number" - } - ], - "class": "LGraph" - }, - { - "file": "../src/litegraph.js", - "line": 1162, - "description": "Returns a list of nodes that matches a class", - "itemtype": "method", - "name": "findNodesByClass", - "params": [ - { - "name": "classObject", - "description": "the class itself (not an string)", - "type": "Class" - } - ], - "return": { - "description": "a list with all the nodes of this type", - "type": "Array" - }, - "class": "LGraph" - }, - { - "file": "../src/litegraph.js", - "line": 1178, - "description": "Returns a list of nodes that matches a type", - "itemtype": "method", - "name": "findNodesByType", - "params": [ - { - "name": "type", - "description": "the name of the node type", - "type": "String" - } - ], - "return": { - "description": "a list with all the nodes of this type", - "type": "Array" - }, - "class": "LGraph" - }, - { - "file": "../src/litegraph.js", - "line": 1195, - "description": "Returns a list of nodes that matches a name", - "itemtype": "method", - "name": "findNodesByTitle", - "params": [ - { - "name": "name", - "description": "the name of the node to search", - "type": "String" - } - ], - "return": { - "description": "a list with all the nodes with this name", - "type": "Array" - }, - "class": "LGraph" - }, - { - "file": "../src/litegraph.js", - "line": 1211, - "description": "Returns the top-most node in this position of the canvas", - "itemtype": "method", - "name": "getNodeOnPos", - "params": [ - { - "name": "x", - "description": "the x coordinate in canvas space", - "type": "Number" - }, - { - "name": "y", - "description": "the y coordinate in canvas space", - "type": "Number" - }, - { - "name": "nodes_list", - "description": "a list with all the nodes to search from, by default is all the nodes in the graph", - "type": "Array" - } - ], - "return": { - "description": "the node at this position or null", - "type": "LGraphNode" - }, - "class": "LGraph" - }, - { - "file": "../src/litegraph.js", - "line": 1231, - "description": "Returns the top-most group in that position", - "itemtype": "method", - "name": "getGroupOnPos", - "params": [ - { - "name": "x", - "description": "the x coordinate in canvas space", - "type": "Number" - }, - { - "name": "y", - "description": "the y coordinate in canvas space", - "type": "Number" - } - ], - "return": { - "description": "the group or null", - "type": "LGraphGroup" - }, - "class": "LGraph" - }, - { - "file": "../src/litegraph.js", - "line": 1251, - "description": "Tell this graph it has a global graph input of this type", - "itemtype": "method", - "name": "addGlobalInput", - "params": [ - { - "name": "name", - "description": "", - "type": "String" - }, - { - "name": "type", - "description": "", - "type": "String" - }, - { - "name": "value", - "description": "[optional]", - "type": "*" - } - ], - "class": "LGraph" - }, - { - "file": "../src/litegraph.js", - "line": 1274, - "description": "Assign a data to the global graph input", - "itemtype": "method", - "name": "setGlobalInputData", - "params": [ - { - "name": "name", - "description": "", - "type": "String" - }, - { - "name": "data", - "description": "", - "type": "*" - } - ], - "class": "LGraph" - }, - { - "file": "../src/litegraph.js", - "line": 1288, - "description": "Returns the current value of a global graph input", - "itemtype": "method", - "name": "getInputData", - "params": [ - { - "name": "name", - "description": "", - "type": "String" - } - ], - "return": { - "description": "the data", - "type": "*" - }, - "class": "LGraph" - }, - { - "file": "../src/litegraph.js", - "line": 1302, - "description": "Changes the name of a global graph input", - "itemtype": "method", - "name": "renameInput", - "params": [ - { - "name": "old_name", - "description": "", - "type": "String" - }, - { - "name": "new_name", - "description": "", - "type": "String" - } - ], - "class": "LGraph" - }, - { - "file": "../src/litegraph.js", - "line": 1333, - "description": "Changes the type of a global graph input", - "itemtype": "method", - "name": "changeInputType", - "params": [ - { - "name": "name", - "description": "", - "type": "String" - }, - { - "name": "type", - "description": "", - "type": "String" - } - ], - "class": "LGraph" - }, - { - "file": "../src/litegraph.js", - "line": 1353, - "description": "Removes a global graph input", - "itemtype": "method", - "name": "removeInput", - "params": [ - { - "name": "name", - "description": "", - "type": "String" - }, - { - "name": "type", - "description": "", - "type": "String" - } - ], - "class": "LGraph" - }, - { - "file": "../src/litegraph.js", - "line": 1375, - "description": "Creates a global graph output", - "itemtype": "method", - "name": "addOutput", - "params": [ - { - "name": "name", - "description": "", - "type": "String" - }, - { - "name": "type", - "description": "", - "type": "String" - }, - { - "name": "value", - "description": "", - "type": "*" - } - ], - "class": "LGraph" - }, - { - "file": "../src/litegraph.js", - "line": 1394, - "description": "Assign a data to the global output", - "itemtype": "method", - "name": "setOutputData", - "params": [ - { - "name": "name", - "description": "", - "type": "String" - }, - { - "name": "value", - "description": "", - "type": "String" - } - ], - "class": "LGraph" - }, - { - "file": "../src/litegraph.js", - "line": 1408, - "description": "Returns the current value of a global graph output", - "itemtype": "method", - "name": "getOutputData", - "params": [ - { - "name": "name", - "description": "", - "type": "String" - } - ], - "return": { - "description": "the data", - "type": "*" - }, - "class": "LGraph" - }, - { - "file": "../src/litegraph.js", - "line": 1422, - "description": "Renames a global graph output", - "itemtype": "method", - "name": "renameOutput", - "params": [ - { - "name": "old_name", - "description": "", - "type": "String" - }, - { - "name": "new_name", - "description": "", - "type": "String" - } - ], - "class": "LGraph" - }, - { - "file": "../src/litegraph.js", - "line": 1450, - "description": "Changes the type of a global graph output", - "itemtype": "method", - "name": "changeOutputType", - "params": [ - { - "name": "name", - "description": "", - "type": "String" - }, - { - "name": "type", - "description": "", - "type": "String" - } - ], - "class": "LGraph" - }, - { - "file": "../src/litegraph.js", - "line": 1470, - "description": "Removes a global graph output", - "itemtype": "method", - "name": "removeOutput", - "params": [ - { - "name": "name", - "description": "", - "type": "String" - } - ], - "class": "LGraph" - }, - { - "file": "../src/litegraph.js", - "line": 1514, - "description": "returns if the graph is in live mode", - "itemtype": "method", - "name": "isLive", - "class": "LGraph" - }, - { - "file": "../src/litegraph.js", - "line": 1533, - "description": "clears the triggered slot animation in all links (stop visual animation)", - "itemtype": "method", - "name": "clearTriggeredSlots", - "class": "LGraph" - }, - { - "file": "../src/litegraph.js", - "line": 1565, - "description": "Destroys a link", - "itemtype": "method", - "name": "removeLink", - "params": [ - { - "name": "link_id", - "description": "", - "type": "Number" - } - ], - "class": "LGraph" - }, - { - "file": "../src/litegraph.js", - "line": 1582, - "description": "Creates a Object containing all the info about this graph, it can be serialized", - "itemtype": "method", - "name": "serialize", - "return": { - "description": "value of the node", - "type": "Object" - }, - "class": "LGraph" - }, - { - "file": "../src/litegraph.js", - "line": 1619, - "description": "Configure a graph from a JSON string", - "itemtype": "method", - "name": "configure", - "params": [ - { - "name": "str", - "description": "configure a graph from a JSON string", - "type": "String" - }, - { - "name": "returns", - "description": "if there was any error parsing", - "type": "Boolean" - } - ], - "class": "LGraph" - }, - { - "file": "../src/litegraph.js", - "line": 1881, - "description": "configure a node from an object containing the serialized info", - "itemtype": "method", - "name": "configure", - "class": "LGraphNode" - }, - { - "file": "../src/litegraph.js", - "line": 1949, - "description": "serialize the content", - "itemtype": "method", - "name": "serialize", - "class": "LGraphNode" - }, - { - "file": "../src/litegraph.js", - "line": 2039, - "description": "serialize and stringify", - "itemtype": "method", - "name": "toString", - "class": "LGraphNode" - }, - { - "file": "../src/litegraph.js", - "line": 2051, - "description": "get the title string", - "itemtype": "method", - "name": "getTitle", - "class": "LGraphNode" - }, - { - "file": "../src/litegraph.js", - "line": 2064, - "description": "sets the output data", - "itemtype": "method", - "name": "setOutputData", - "params": [ - { - "name": "slot", - "description": "", - "type": "Number" - }, - { - "name": "data", - "description": "", - "type": "*" - } - ], - "class": "LGraphNode" - }, - { - "file": "../src/litegraph.js", - "line": 2100, - "description": "sets the output data type, useful when you want to be able to overwrite the data type", - "itemtype": "method", - "name": "setOutputDataType", - "params": [ - { - "name": "slot", - "description": "", - "type": "Number" - }, - { - "name": "datatype", - "description": "", - "type": "String" - } - ], - "class": "LGraphNode" - }, - { - "file": "../src/litegraph.js", - "line": 2129, - "description": "Retrieves the input data (data traveling through the connection) from one slot", - "itemtype": "method", - "name": "getInputData", - "params": [ - { - "name": "slot", - "description": "", - "type": "Number" - }, - { - "name": "force_update", - "description": "if set to true it will force the connected node of this slot to output data into this link", - "type": "Boolean" - } - ], - "return": { - "description": "data or if it is not connected returns undefined", - "type": "*" - }, - "class": "LGraphNode" - }, - { - "file": "../src/litegraph.js", - "line": 2165, - "description": "Retrieves the input data type (in case this supports multiple input types)", - "itemtype": "method", - "name": "getInputDataType", - "params": [ - { - "name": "slot", - "description": "", - "type": "Number" - } - ], - "return": { - "description": "datatype in string format", - "type": "String" - }, - "class": "LGraphNode" - }, - { - "file": "../src/litegraph.js", - "line": 2191, - "description": "Retrieves the input data from one slot using its name instead of slot number", - "itemtype": "method", - "name": "getInputDataByName", - "params": [ - { - "name": "slot_name", - "description": "", - "type": "String" - }, - { - "name": "force_update", - "description": "if set to true it will force the connected node of this slot to output data into this link", - "type": "Boolean" - } - ], - "return": { - "description": "data or if it is not connected returns null", - "type": "*" - }, - "class": "LGraphNode" - }, - { - "file": "../src/litegraph.js", - "line": 2207, - "description": "tells you if there is a connection in one input slot", - "itemtype": "method", - "name": "isInputConnected", - "params": [ - { - "name": "slot", - "description": "", - "type": "Number" - } - ], - "return": { - "description": "", - "type": "Boolean" - }, - "class": "LGraphNode" - }, - { - "file": "../src/litegraph.js", - "line": 2220, - "description": "tells you info about an input connection (which node, type, etc)", - "itemtype": "method", - "name": "getInputInfo", - "params": [ - { - "name": "slot", - "description": "", - "type": "Number" - } - ], - "return": { - "description": "object or null { link: id, name: string, type: string or 0 }", - "type": "Object" - }, - "class": "LGraphNode" - }, - { - "file": "../src/litegraph.js", - "line": 2235, - "description": "returns the node connected in the input slot", - "itemtype": "method", - "name": "getInputNode", - "params": [ - { - "name": "slot", - "description": "", - "type": "Number" - } - ], - "return": { - "description": "node or null", - "type": "LGraphNode" - }, - "class": "LGraphNode" - }, - { - "file": "../src/litegraph.js", - "line": 2257, - "description": "returns the value of an input with this name, otherwise checks if there is a property with that name", - "itemtype": "method", - "name": "getInputOrProperty", - "params": [ - { - "name": "name", - "description": "", - "type": "String" - } - ], - "return": { - "description": "value", - "type": "*" - }, - "class": "LGraphNode" - }, - { - "file": "../src/litegraph.js", - "line": 2284, - "description": "tells you the last output data that went in that slot", - "itemtype": "method", - "name": "getOutputData", - "params": [ - { - "name": "slot", - "description": "", - "type": "Number" - } - ], - "return": { - "description": "object or null", - "type": "Object" - }, - "class": "LGraphNode" - }, - { - "file": "../src/litegraph.js", - "line": 2302, - "description": "tells you info about an output connection (which node, type, etc)", - "itemtype": "method", - "name": "getOutputInfo", - "params": [ - { - "name": "slot", - "description": "", - "type": "Number" - } - ], - "return": { - "description": "object or null { name: string, type: string, links: [ ids of links in number ] }", - "type": "Object" - }, - "class": "LGraphNode" - }, - { - "file": "../src/litegraph.js", - "line": 2318, - "description": "tells you if there is a connection in one output slot", - "itemtype": "method", - "name": "isOutputConnected", - "params": [ - { - "name": "slot", - "description": "", - "type": "Number" - } - ], - "return": { - "description": "", - "type": "Boolean" - }, - "class": "LGraphNode" - }, - { - "file": "../src/litegraph.js", - "line": 2331, - "description": "tells you if there is any connection in the output slots", - "itemtype": "method", - "name": "isAnyOutputConnected", - "return": { - "description": "", - "type": "Boolean" - }, - "class": "LGraphNode" - }, - { - "file": "../src/litegraph.js", - "line": 2347, - "description": "retrieves all the nodes connected to this output slot", - "itemtype": "method", - "name": "getOutputNodes", - "params": [ - { - "name": "slot", - "description": "", - "type": "Number" - } - ], - "return": { - "description": "", - "type": "Array" - }, - "class": "LGraphNode" - }, - { - "file": "../src/litegraph.js", - "line": 2380, - "description": "Triggers an event in this node, this will trigger any output with the same name", - "itemtype": "method", - "name": "trigger", - "params": [ - { - "name": "event", - "description": "name ( \"on_play\", ... ) if action is equivalent to false then the event is send to all", - "type": "String" - }, - { - "name": "param", - "description": "", - "type": "*" - } - ], - "class": "LGraphNode" - }, - { - "file": "../src/litegraph.js", - "line": 2403, - "description": "Triggers an slot event in this node", - "itemtype": "method", - "name": "triggerSlot", - "params": [ - { - "name": "slot", - "description": "the index of the output slot", - "type": "Number" - }, - { - "name": "param", - "description": "", - "type": "*" - }, - { - "name": "link_id", - "description": "[optional] in case you want to trigger and specific output link in a slot", - "type": "Number" - } - ], - "class": "LGraphNode" - }, - { - "file": "../src/litegraph.js", - "line": 2453, - "description": "clears the trigger slot animation", - "itemtype": "method", - "name": "clearTriggeredSlot", - "params": [ - { - "name": "slot", - "description": "the index of the output slot", - "type": "Number" - }, - { - "name": "link_id", - "description": "[optional] in case you want to trigger and specific output link in a slot", - "type": "Number" - } - ], - "class": "LGraphNode" - }, - { - "file": "../src/litegraph.js", - "line": 2485, - "description": "add a new property to this node", - "itemtype": "method", - "name": "addProperty", - "params": [ - { - "name": "name", - "description": "", - "type": "String" - }, - { - "name": "default_value", - "description": "", - "type": "*" - }, - { - "name": "type", - "description": "string defining the output type (\"vec3\",\"number\",...)", - "type": "String" - }, - { - "name": "extra_info", - "description": "this can be used to have special properties of the property (like values, etc)", - "type": "Object" - } - ], - "class": "LGraphNode" - }, - { - "file": "../src/litegraph.js", - "line": 2511, - "description": "add a new output slot to use in this node", - "itemtype": "method", - "name": "addOutput", - "params": [ - { - "name": "name", - "description": "", - "type": "String" - }, - { - "name": "type", - "description": "string defining the output type (\"vec3\",\"number\",...)", - "type": "String" - }, - { - "name": "extra_info", - "description": "this can be used to have special properties of an output (label, special color, position, etc)", - "type": "Object" - } - ], - "class": "LGraphNode" - }, - { - "file": "../src/litegraph.js", - "line": 2535, - "description": "add a new output slot to use in this node", - "itemtype": "method", - "name": "addOutputs", - "params": [ - { - "name": "array", - "description": "of triplets like [[name,type,extra_info],[...]]", - "type": "Array" - } - ], - "class": "LGraphNode" - }, - { - "file": "../src/litegraph.js", - "line": 2561, - "description": "remove an existing output slot", - "itemtype": "method", - "name": "removeOutput", - "params": [ - { - "name": "slot", - "description": "", - "type": "Number" - } - ], - "class": "LGraphNode" - }, - { - "file": "../src/litegraph.js", - "line": 2590, - "description": "add a new input slot to use in this node", - "itemtype": "method", - "name": "addInput", - "params": [ - { - "name": "name", - "description": "", - "type": "String" - }, - { - "name": "type", - "description": "string defining the input type (\"vec3\",\"number\",...), it its a generic one use 0", - "type": "String" - }, - { - "name": "extra_info", - "description": "this can be used to have special properties of an input (label, color, position, etc)", - "type": "Object" - } - ], - "class": "LGraphNode" - }, - { - "file": "../src/litegraph.js", - "line": 2615, - "description": "add several new input slots in this node", - "itemtype": "method", - "name": "addInputs", - "params": [ - { - "name": "array", - "description": "of triplets like [[name,type,extra_info],[...]]", - "type": "Array" - } - ], - "class": "LGraphNode" - }, - { - "file": "../src/litegraph.js", - "line": 2641, - "description": "remove an existing input slot", - "itemtype": "method", - "name": "removeInput", - "params": [ - { - "name": "slot", - "description": "", - "type": "Number" - } - ], - "class": "LGraphNode" - }, - { - "file": "../src/litegraph.js", - "line": 2665, - "description": "add an special connection to this node (used for special kinds of graphs)", - "itemtype": "method", - "name": "addConnection", - "params": [ - { - "name": "name", - "description": "", - "type": "String" - }, - { - "name": "type", - "description": "string defining the input type (\"vec3\",\"number\",...)", - "type": "String" - }, - { - "name": "pos", - "description": "position of the connection inside the node", - "type": "[x,y]" - }, - { - "name": "direction", - "description": "if is input or output", - "type": "String" - } - ], - "class": "LGraphNode" - }, - { - "file": "../src/litegraph.js", - "line": 2686, - "description": "computes the size of a node according to its inputs and output slots", - "itemtype": "method", - "name": "computeSize", - "params": [ - { - "name": "minHeight", - "description": "", - "type": "Number" - } - ], - "return": { - "description": "the total size", - "type": "Number" - }, - "class": "LGraphNode" - }, - { - "file": "../src/litegraph.js", - "line": 2753, - "description": "Allows to pass", - "itemtype": "method", - "name": "addWidget", - "return": { - "description": "the created widget", - "type": "Object" - }, - "class": "LGraphNode" - }, - { - "file": "../src/litegraph.js", - "line": 2791, - "description": "returns the bounding of the object, used for rendering purposes\nbounding is: [topleft_cornerx, topleft_cornery, width, height]", - "itemtype": "method", - "name": "getBounding", - "return": { - "description": "the total size", - "type": "Float32Array[4]" - }, - "class": "LGraphNode" - }, - { - "file": "../src/litegraph.js", - "line": 2810, - "description": "checks if a point is inside the shape of a node", - "itemtype": "method", - "name": "isPointInside", - "params": [ - { - "name": "x", - "description": "", - "type": "Number" - }, - { - "name": "y", - "description": "", - "type": "Number" - } - ], - "return": { - "description": "", - "type": "Boolean" - }, - "class": "LGraphNode" - }, - { - "file": "../src/litegraph.js", - "line": 2836, - "description": "checks if a point is inside a node slot, and returns info about which slot", - "itemtype": "method", - "name": "getSlotInPosition", - "params": [ - { - "name": "x", - "description": "", - "type": "Number" - }, - { - "name": "y", - "description": "", - "type": "Number" - } - ], - "return": { - "description": "if found the object contains { input|output: slot object, slot: number, link_pos: [x,y] }", - "type": "Object" - }, - "class": "LGraphNode" - }, - { - "file": "../src/litegraph.js", - "line": 2868, - "description": "returns the input slot with a given name (used for dynamic slots), -1 if not found", - "itemtype": "method", - "name": "findInputSlot", - "params": [ - { - "name": "name", - "description": "the name of the slot", - "type": "String" - } - ], - "return": { - "description": "the slot (-1 if not found)", - "type": "Number" - }, - "class": "LGraphNode" - }, - { - "file": "../src/litegraph.js", - "line": 2884, - "description": "returns the output slot with a given name (used for dynamic slots), -1 if not found", - "itemtype": "method", - "name": "findOutputSlot", - "params": [ - { - "name": "name", - "description": "the name of the slot", - "type": "String" - } - ], - "return": { - "description": "the slot (-1 if not found)", - "type": "Number" - }, - "class": "LGraphNode" - }, - { - "file": "../src/litegraph.js", - "line": 2899, - "description": "connect this node output to the input of another node", - "itemtype": "method", - "name": "connect", - "params": [ - { - "name": "slot", - "description": "(could be the number of the slot or the string with the name of the slot)", - "type": "Number_or_string" - }, - { - "name": "node", - "description": "the target node", - "type": "LGraphNode" - }, - { - "name": "target_slot", - "description": "the input slot of the target node (could be the number of the slot or the string with the name of the slot, or -1 to connect a trigger)", - "type": "Number_or_string" - } - ], - "return": { - "description": "the link_info is created, otherwise null", - "type": "Object" - }, - "class": "LGraphNode" - }, - { - "file": "../src/litegraph.js", - "line": 3024, - "description": "disconnect one output to an specific node", - "itemtype": "method", - "name": "disconnectOutput", - "params": [ - { - "name": "slot", - "description": "(could be the number of the slot or the string with the name of the slot)", - "type": "Number_or_string" - }, - { - "name": "target_node", - "description": "the target node to which this slot is connected [Optional, if not target_node is specified all nodes will be disconnected]", - "type": "LGraphNode" - } - ], - "return": { - "description": "if it was disconnected succesfully", - "type": "Boolean" - }, - "class": "LGraphNode" - }, - { - "file": "../src/litegraph.js", - "line": 3132, - "description": "disconnect one input", - "itemtype": "method", - "name": "disconnectInput", - "params": [ - { - "name": "slot", - "description": "(could be the number of the slot or the string with the name of the slot)", - "type": "Number_or_string" - } - ], - "return": { - "description": "if it was disconnected succesfully", - "type": "Boolean" - }, - "class": "LGraphNode" - }, - { - "file": "../src/litegraph.js", - "line": 3206, - "description": "returns the center of a connection point in canvas coords", - "itemtype": "method", - "name": "getConnectionPos", - "params": [ - { - "name": "is_input", - "description": "true if if a input slot, false if it is an output", - "type": "Boolean" - }, - { - "name": "slot", - "description": "(could be the number of the slot or the string with the name of the slot)", - "type": "Number_or_string" - }, - { - "name": "out", - "description": "[optional] a place to store the output, to free garbage", - "type": "Vec2" - } - ], - "return": { - "description": "the position", - "type": "[x,y]" - }, - "class": "LGraphNode" - }, - { - "file": "../src/litegraph.js", - "line": 3390, - "description": "Collapse the node to make it smaller on the canvas", - "itemtype": "method", - "name": "collapse", - "class": "LGraphNode" - }, - { - "file": "../src/litegraph.js", - "line": 3406, - "description": "Forces the node to do not move or realign on Z", - "itemtype": "method", - "name": "pin", - "class": "LGraphNode" - }, - { - "file": "../src/litegraph.js", - "line": 3837, - "description": "clears all the data inside", - "itemtype": "method", - "name": "clear", - "class": "LGraphCanvas" - }, - { - "file": "../src/litegraph.js", - "line": 3880, - "description": "assigns a graph, you can reasign graphs to the same canvas", - "itemtype": "method", - "name": "setGraph", - "params": [ - { - "name": "graph", - "description": "", - "type": "LGraph" - } - ], - "class": "LGraphCanvas" - }, - { - "file": "../src/litegraph.js", - "line": 3911, - "description": "opens a graph contained inside a node in the current graph", - "itemtype": "method", - "name": "openSubgraph", - "params": [ - { - "name": "graph", - "description": "", - "type": "LGraph" - } - ], - "class": "LGraphCanvas" - }, - { - "file": "../src/litegraph.js", - "line": 3938, - "description": "closes a subgraph contained inside a node", - "itemtype": "method", - "name": "closeSubgraph", - "params": [ - { - "name": "assigns", - "description": "a graph", - "type": "LGraph" - } - ], - "class": "LGraphCanvas" - }, - { - "file": "../src/litegraph.js", - "line": 3961, - "description": "assigns a canvas", - "itemtype": "method", - "name": "setCanvas", - "params": [ - { - "name": "assigns", - "description": "a canvas (also accepts the ID of the element (not a selector)", - "type": "Canvas" - } - ], - "class": "LGraphCanvas" - }, - { - "file": "../src/litegraph.js", - "line": 4038, - "description": "binds mouse, keyboard, touch and drag events to the canvas", - "itemtype": "method", - "name": "bindEvents", - "class": "LGraphCanvas" - }, - { - "file": "../src/litegraph.js", - "line": 4090, - "description": "unbinds mouse events from the canvas", - "itemtype": "method", - "name": "unbindEvents", - "class": "LGraphCanvas" - }, - { - "file": "../src/litegraph.js", - "line": 4138, - "description": "this function allows to render the canvas using WebGL instead of Canvas2D\nthis is useful if you plant to render 3D objects inside your nodes, it uses litegl.js for webgl and canvas2DtoWebGL to emulate the Canvas2D calls in webGL", - "itemtype": "method", - "name": "enableWebGL", - "class": "LGraphCanvas" - }, - { - "file": "../src/litegraph.js", - "line": 4180, - "description": "Used to attach the canvas in a popup", - "itemtype": "method", - "name": "getCanvasWindow", - "return": { - "description": "returns the window where the canvas is attached (the DOM root node)", - "type": "Window" - }, - "class": "LGraphCanvas" - }, - { - "file": "../src/litegraph.js", - "line": 4194, - "description": "starts rendering the content of the canvas when needed", - "itemtype": "method", - "name": "startRendering", - "class": "LGraphCanvas" - }, - { - "file": "../src/litegraph.js", - "line": 4218, - "description": "stops rendering the content of the canvas (to save resources)", - "itemtype": "method", - "name": "stopRendering", - "class": "LGraphCanvas" - }, - { - "file": "../src/litegraph.js", - "line": 4501, - "description": "Called when a mouse move event has to be processed", - "itemtype": "method", - "name": "processMouseMove", - "class": "LGraphCanvas" - }, - { - "file": "../src/litegraph.js", - "line": 4675, - "description": "Called when a mouse up event has to be processed", - "itemtype": "method", - "name": "processMouseUp", - "class": "LGraphCanvas" - }, - { - "file": "../src/litegraph.js", - "line": 4847, - "description": "Called when a mouse wheel event has to be processed", - "itemtype": "method", - "name": "processMouseWheel", - "class": "LGraphCanvas" - }, - { - "file": "../src/litegraph.js", - "line": 4876, - "description": "retuns true if a position (in graph space) is on top of a node little corner box", - "itemtype": "method", - "name": "isOverNodeBox", - "class": "LGraphCanvas" - }, - { - "file": "../src/litegraph.js", - "line": 4888, - "description": "retuns true if a position (in graph space) is on top of a node input slot", - "itemtype": "method", - "name": "isOverNodeInput", - "class": "LGraphCanvas" - }, - { - "file": "../src/litegraph.js", - "line": 4917, - "description": "process a key event", - "itemtype": "method", - "name": "processKey", - "class": "LGraphCanvas" - }, - { - "file": "../src/litegraph.js", - "line": 5074, - "description": "process a item drop event on top the canvas", - "itemtype": "method", - "name": "processDrop", - "class": "LGraphCanvas" - }, - { - "file": "../src/litegraph.js", - "line": 5192, - "description": "selects a given node (or adds it to the current selection)", - "itemtype": "method", - "name": "selectNode", - "class": "LGraphCanvas" - }, - { - "file": "../src/litegraph.js", - "line": 5204, - "description": "selects several nodes (or adds them to the current selection)", - "itemtype": "method", - "name": "selectNodes", - "class": "LGraphCanvas" - }, - { - "file": "../src/litegraph.js", - "line": 5242, - "description": "removes a node from the current selection", - "itemtype": "method", - "name": "deselectNode", - "class": "LGraphCanvas" - }, - { - "file": "../src/litegraph.js", - "line": 5268, - "description": "removes all nodes from the current selection", - "itemtype": "method", - "name": "deselectAllNodes", - "class": "LGraphCanvas" - }, - { - "file": "../src/litegraph.js", - "line": 5291, - "description": "deletes all nodes in the current selection from the graph", - "itemtype": "method", - "name": "deleteSelectedNodes", - "class": "LGraphCanvas" - }, - { - "file": "../src/litegraph.js", - "line": 5308, - "description": "centers the camera on a given node", - "itemtype": "method", - "name": "centerOnNode", - "class": "LGraphCanvas" - }, - { - "file": "../src/litegraph.js", - "line": 5319, - "description": "adds some useful properties to a mouse event, like the position in graph coordinates", - "itemtype": "method", - "name": "adjustMouseEvent", - "class": "LGraphCanvas" - }, - { - "file": "../src/litegraph.js", - "line": 5347, - "description": "changes the zoom level of the graph (default is 1), you can pass also a place used to pivot the zoom", - "itemtype": "method", - "name": "setZoom", - "class": "LGraphCanvas" - }, - { - "file": "../src/litegraph.js", - "line": 5378, - "description": "converts a coordinate from graph coordinates to canvas2D coordinates", - "itemtype": "method", - "name": "convertOffsetToCanvas", - "class": "LGraphCanvas" - }, - { - "file": "../src/litegraph.js", - "line": 5387, - "description": "converts a coordinate from Canvas2D coordinates to graph space", - "itemtype": "method", - "name": "convertCanvasToOffset", - "class": "LGraphCanvas" - }, - { - "file": "../src/litegraph.js", - "line": 5403, - "description": "brings a node to front (above all other nodes)", - "itemtype": "method", - "name": "bringToFront", - "class": "LGraphCanvas" - }, - { - "file": "../src/litegraph.js", - "line": 5416, - "description": "sends a node to the back (below all other nodes)", - "itemtype": "method", - "name": "sendToBack", - "class": "LGraphCanvas" - }, - { - "file": "../src/litegraph.js", - "line": 5436, - "description": "checks which nodes are visible (inside the camera area)", - "itemtype": "method", - "name": "computeVisibleNodes", - "class": "LGraphCanvas" - }, - { - "file": "../src/litegraph.js", - "line": 5461, - "description": "renders the whole canvas content, by rendering in two separated canvas, one containing the background grid and the connections, and one containing the nodes)", - "itemtype": "method", - "name": "draw", - "class": "LGraphCanvas" - }, - { - "file": "../src/litegraph.js", - "line": 5488, - "description": "draws the front canvas (the one containing all the nodes)", - "itemtype": "method", - "name": "drawFrontCanvas", - "class": "LGraphCanvas" - }, - { - "file": "../src/litegraph.js", - "line": 5632, - "description": "draws some useful stats in the corner of the canvas", - "itemtype": "method", - "name": "renderInfo", - "class": "LGraphCanvas" - }, - { - "file": "../src/litegraph.js", - "line": 5659, - "description": "draws the back canvas (the one containing the background and the connections)", - "itemtype": "method", - "name": "drawBackCanvas", - "class": "LGraphCanvas" - }, - { - "file": "../src/litegraph.js", - "line": 5807, - "description": "draws the given node inside the canvas", - "itemtype": "method", - "name": "drawNode", - "class": "LGraphCanvas" - }, - { - "file": "../src/litegraph.js", - "line": 6115, - "description": "draws the shape of the given node in the canvas", - "itemtype": "method", - "name": "drawNodeShape", - "class": "LGraphCanvas" - }, - { - "file": "../src/litegraph.js", - "line": 6311, - "description": "draws every connection visible in the canvas\nOPTIMIZE THIS: precatch connections position instead of recomputing them every time", - "itemtype": "method", - "name": "drawConnections", - "class": "LGraphCanvas" - }, - { - "file": "../src/litegraph.js", - "line": 6398, - "description": "draws a link between two points", - "itemtype": "method", - "name": "renderLink", - "params": [ - { - "name": "a", - "description": "start pos", - "type": "Vec2" - }, - { - "name": "b", - "description": "end pos", - "type": "Vec2" - }, - { - "name": "link", - "description": "the link object with all the link info", - "type": "Object" - }, - { - "name": "skip_border", - "description": "ignore the shadow of the link", - "type": "Boolean" - }, - { - "name": "flow", - "description": "show flow animation (for events)", - "type": "Boolean" - }, - { - "name": "color", - "description": "the color for the link", - "type": "String" - }, - { - "name": "start_dir", - "description": "the direction enum", - "type": "Number" - }, - { - "name": "end_dir", - "description": "the direction enum", - "type": "Number" - }, - { - "name": "num_sublines", - "description": "number of sublines (useful to represent vec3 or rgb)", - "type": "Number" - } - ], - "class": "LGraphCanvas" - }, - { - "file": "../src/litegraph.js", - "line": 6663, - "description": "draws the widgets stored inside a node", - "itemtype": "method", - "name": "drawNodeWidgets", - "class": "LGraphCanvas" - }, - { - "file": "../src/litegraph.js", - "line": 6813, - "description": "process an event on widgets", - "itemtype": "method", - "name": "processNodeWidgets", - "class": "LGraphCanvas" - }, - { - "file": "../src/litegraph.js", - "line": 6924, - "description": "draws every group area in the background", - "itemtype": "method", - "name": "drawGroups", - "class": "LGraphCanvas" - }, - { - "file": "../src/litegraph.js", - "line": 6979, - "description": "resizes the canvas to a given size, if no size is passed, then it tries to fill the parentNode", - "itemtype": "method", - "name": "resize", - "class": "LGraphCanvas" - }, - { - "file": "../src/litegraph.js", - "line": 7002, - "description": "switches to live mode (node shapes are not rendered, only the content)\nthis feature was designed when graphs where meant to create user interfaces", - "itemtype": "method", - "name": "switchLiveMode", - "class": "LGraphCanvas" - } - ], - "warnings": [] -} \ No newline at end of file diff --git a/doc/elements/index.html b/doc/elements/index.html deleted file mode 100755 index 487fe15b2..000000000 --- a/doc/elements/index.html +++ /dev/null @@ -1,10 +0,0 @@ - - - - Redirector - - - - Click here to redirect - - diff --git a/doc/files/.._src_litegraph.js.html b/doc/files/.._src_litegraph.js.html deleted file mode 100755 index 069ec1ee6..000000000 --- a/doc/files/.._src_litegraph.js.html +++ /dev/null @@ -1,8916 +0,0 @@ - - - - - ../src/litegraph.js - - - - - - - - -
    -
    -
    -

    -
    -
    - API Docs for: -
    -
    -
    - -
    - -
    -
    -
    - Show: - - - - - - - -
    - -
    -
    -
    -

    File: ../src/litegraph.js

    - -
    -
    -(function(global){
    -// *************************************************************
    -//   LiteGraph CLASS                                     *******
    -// *************************************************************
    -
    -/**
    -* The Global Scope. It contains all the registered node classes.
    -*
    -* @class LiteGraph
    -* @constructor
    -*/
    -
    -var LiteGraph = global.LiteGraph = {
    -
    -	VERSION: 0.4,
    -
    -	CANVAS_GRID_SIZE: 10,
    -	
    -	NODE_TITLE_HEIGHT: 30,
    -	NODE_TITLE_TEXT_Y: 20,
    -	NODE_SLOT_HEIGHT: 20,
    -	NODE_WIDGET_HEIGHT: 20,
    -	NODE_WIDTH: 140,
    -	NODE_MIN_WIDTH: 50,
    -	NODE_COLLAPSED_RADIUS: 10,
    -	NODE_COLLAPSED_WIDTH: 80,
    -	NODE_TITLE_COLOR: "#999",
    -	NODE_TEXT_SIZE: 14,
    -	NODE_TEXT_COLOR: "#AAA",
    -	NODE_SUBTEXT_SIZE: 12,
    -	NODE_DEFAULT_COLOR: "#333",
    -	NODE_DEFAULT_BGCOLOR: "#353535",
    -	NODE_DEFAULT_BOXCOLOR: "#666",
    -	NODE_DEFAULT_SHAPE: "box",
    -	DEFAULT_SHADOW_COLOR: "rgba(0,0,0,0.5)",
    -	DEFAULT_GROUP_FONT: 24,
    -
    -	LINK_COLOR: "#9A9",
    -	EVENT_LINK_COLOR: "#A86",
    -	CONNECTING_LINK_COLOR: "#AFA",
    -
    -	MAX_NUMBER_OF_NODES: 1000, //avoid infinite loops
    -	DEFAULT_POSITION: [100,100],//default node position
    -	VALID_SHAPES: ["default","box","round","card"], //,"circle"
    -
    -	//shapes are used for nodes but also for slots
    -	BOX_SHAPE: 1,
    -	ROUND_SHAPE: 2,
    -	CIRCLE_SHAPE: 3,
    -	CARD_SHAPE: 4,
    -	ARROW_SHAPE: 5,
    -
    -	//enums
    -	INPUT: 1,
    -	OUTPUT: 2,
    -
    -	EVENT: -1, //for outputs
    -	ACTION: -1, //for inputs
    -
    -	ALWAYS: 0,
    -	ON_EVENT: 1,
    -	NEVER: 2,
    -	ON_TRIGGER: 3,
    -
    -	UP: 1,
    -	DOWN:2,
    -	LEFT:3,
    -	RIGHT:4,
    -	CENTER:5,
    -
    -	STRAIGHT_LINK: 0,
    -	LINEAR_LINK: 1,
    -	SPLINE_LINK: 2,
    -
    -	NORMAL_TITLE: 0,
    -	NO_TITLE: 1,
    -	TRANSPARENT_TITLE: 2,
    -	AUTOHIDE_TITLE: 3,
    -
    -	proxy: null, //used to redirect calls
    -	node_images_path: "",
    -
    -	debug: false,
    -	catch_exceptions: true,
    -	throw_errors: true,
    -	allow_scripts: false, //if set to true some nodes like Formula would be allowed to evaluate code that comes from unsafe sources (like node configuration), which could lead to exploits
    -	registered_node_types: {}, //nodetypes by string
    -	node_types_by_file_extension: {}, //used for droping files in the canvas
    -	Nodes: {}, //node types by classname
    -
    -	searchbox_extras: {}, //used to add extra features to the search box
    -
    -	/**
    -	* Register a node class so it can be listed when the user wants to create a new one
    -	* @method registerNodeType
    -	* @param {String} type name of the node and path
    -	* @param {Class} base_class class containing the structure of a node
    -	*/
    -
    -	registerNodeType: function(type, base_class)
    -	{
    -		if(!base_class.prototype)
    -			throw("Cannot register a simple object, it must be a class with a prototype");
    -		base_class.type = type;
    -
    -		if(LiteGraph.debug)
    -			console.log("Node registered: " + type);
    -
    -		var categories = type.split("/");
    -		var classname = base_class.name;
    -
    -		var pos = type.lastIndexOf("/");
    -		base_class.category = type.substr(0,pos);
    -
    -		if(!base_class.title)
    -			base_class.title = classname;
    -		//info.name = name.substr(pos+1,name.length - pos);
    -
    -		//extend class
    -		if(base_class.prototype) //is a class
    -			for(var i in LGraphNode.prototype)
    -				if(!base_class.prototype[i])
    -					base_class.prototype[i] = LGraphNode.prototype[i];
    -
    -		Object.defineProperty( base_class.prototype, "shape",{
    -			set: function(v) {
    -				switch(v)
    -				{
    -					case "default": delete this._shape; break;
    -					case "box": this._shape = LiteGraph.BOX_SHAPE; break;
    -					case "round": this._shape = LiteGraph.ROUND_SHAPE; break;
    -					case "circle": this._shape = LiteGraph.CIRCLE_SHAPE; break;
    -					case "card": this._shape = LiteGraph.CARD_SHAPE; break;
    -					default:
    -						this._shape = v;
    -				}
    -			},
    -			get: function(v)
    -			{
    -				return this._shape;
    -			},
    -			enumerable: true
    -		});
    -
    -		this.registered_node_types[ type ] = base_class;
    -		if(base_class.constructor.name)
    -			this.Nodes[ classname ] = base_class;
    -
    -		//warnings
    -		if(base_class.prototype.onPropertyChange)
    -			console.warn("LiteGraph node class " + type + " has onPropertyChange method, it must be called onPropertyChanged with d at the end");
    -
    -		if( base_class.supported_extensions )
    -		{
    -			for(var i in base_class.supported_extensions )
    -				this.node_types_by_file_extension[ base_class.supported_extensions[i].toLowerCase() ] = base_class;
    -		}
    -	},
    -
    -	/**
    -	* Create a new node type by passing a function, it wraps it with a propper 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.
    -	* @method wrapFunctionAsNode
    -	* @param {String} name node name with namespace (p.e.: 'math/sum')
    -	* @param {Function} func
    -	* @param {Array} param_types [optional] an array containing the type of every parameter, otherwise parameters will accept any type
    -	* @param {String} return_type [optional] string with the return type, otherwise it will be generic
    -	* @param {Object} properties [optional] properties to be configurable
    -	*/
    -	wrapFunctionAsNode: function( name, func, param_types, return_type, properties )
    -	{
    -		var params = Array(func.length);
    -		var code = "";
    -		var names = LiteGraph.getParameterNames( func );
    -		for(var i = 0; i < names.length; ++i)
    -			code += "this.addInput('"+names[i]+"',"+(param_types && param_types[i] ? "'" + param_types[i] + "'" : "0") + ");\n";
    -		code += "this.addOutput('out',"+( return_type ? "'" + return_type + "'" : 0 )+");\n";
    -		if(properties)
    -			code += "this.properties = " + JSON.stringify(properties) + ";\n";
    -		var classobj = Function(code);
    -		classobj.title = name.split("/").pop();
    -		classobj.desc = "Generated from " + func.name;
    -		classobj.prototype.onExecute = function onExecute()
    -		{
    -			for(var i = 0; i < params.length; ++i)
    -				params[i] = this.getInputData(i);
    -			var r = func.apply( this, params );
    -			this.setOutputData(0,r);
    -		}
    -		this.registerNodeType( name, classobj );
    -	},
    -
    -	/**
    -	* Adds this method to all nodetypes, existing and to be created
    -	* (You can add it to LGraphNode.prototype but then existing node types wont have it)
    -	* @method addNodeMethod
    -	* @param {Function} func
    -	*/
    -	addNodeMethod: function( name, func )
    -	{
    -		LGraphNode.prototype[name] = func;
    -		for(var i in this.registered_node_types)
    -		{
    -			var type = this.registered_node_types[i];
    -			if(type.prototype[name])
    -				type.prototype["_" + name] = type.prototype[name]; //keep old in case of replacing
    -			type.prototype[name] = func;
    -		}
    -	},
    -
    -	/**
    -	* Create a node of a given type with a name. The node is not attached to any graph yet.
    -	* @method createNode
    -	* @param {String} type full name of the node class. p.e. "math/sin"
    -	* @param {String} name a name to distinguish from other nodes
    -	* @param {Object} options to set options
    -	*/
    -
    -	createNode: function( type, title, options )
    -	{
    -		var base_class = this.registered_node_types[type];
    -		if (!base_class)
    -		{
    -			if(LiteGraph.debug)
    -				console.log("GraphNode type \"" + type + "\" not registered.");
    -			return null;
    -		}
    -
    -		var prototype = base_class.prototype || base_class;
    -
    -		title = title || base_class.title || type;
    -
    -		var node = null;
    -
    -		if( LiteGraph.catch_exceptions )
    -		{
    -			try
    -			{
    -				node = new base_class( title );
    -			}
    -			catch (err)
    -			{
    -				console.error(err);
    -				return null;
    -			}
    -		}
    -		else
    -			node = new base_class( title );
    -
    -		node.type = type;
    -
    -		if(!node.title && title) node.title = title;
    -		if(!node.properties) node.properties = {};
    -		if(!node.properties_info) node.properties_info = [];
    -		if(!node.flags) node.flags = {};
    -		if(!node.size) node.size = node.computeSize();
    -		if(!node.pos) node.pos = LiteGraph.DEFAULT_POSITION.concat();
    -		if(!node.mode) node.mode = LiteGraph.ALWAYS;
    -
    -		//extra options
    -		if(options)
    -		{
    -			for(var i in options)
    -				node[i] = options[i];
    -		}
    -
    -		return node;
    -	},
    -
    -	/**
    -	* Returns a registered node type with a given name
    -	* @method getNodeType
    -	* @param {String} type full name of the node class. p.e. "math/sin"
    -	* @return {Class} the node class
    -	*/
    -	getNodeType: function(type)
    -	{
    -		return this.registered_node_types[type];
    -	},
    -
    -	/**
    -	* Returns a list of node types matching one category
    -	* @method getNodeType
    -	* @param {String} category category name
    -	* @return {Array} array with all the node classes
    -	*/
    -
    -	getNodeTypesInCategory: function( category, filter )
    -	{
    -		var r = [];
    -		for(var i in this.registered_node_types)
    -		{
    -			var type = this.registered_node_types[i];
    -			if(filter && type.filter && type.filter != filter)
    -				continue;
    -
    -			if(category == "" )
    -			{
    -				if (type.category == null)
    -					r.push(type);
    -			}
    -			else if (type.category == category)
    -				r.push(type);
    -		}
    -
    -		return r;
    -	},
    -
    -	/**
    -	* Returns a list with all the node type categories
    -	* @method getNodeTypesCategories
    -	* @return {Array} array with all the names of the categories
    -	*/
    -
    -	getNodeTypesCategories: function()
    -	{
    -		var categories = {"":1};
    -		for(var i in this.registered_node_types)
    -			if(this.registered_node_types[i].category && !this.registered_node_types[i].skip_list)
    -				categories[ this.registered_node_types[i].category ] = 1;
    -		var result = [];
    -		for(var i in categories)
    -			result.push(i);
    -		return result;
    -	},
    -
    -	//debug purposes: reloads all the js scripts that matches a wilcard
    -	reloadNodes: function (folder_wildcard)
    -	{
    -		var tmp = document.getElementsByTagName("script");
    -		//weird, this array changes by its own, so we use a copy
    -		var script_files = [];
    -		for(var i in tmp)
    -			script_files.push(tmp[i]);
    -
    -
    -		var docHeadObj = document.getElementsByTagName("head")[0];
    -		folder_wildcard = document.location.href + folder_wildcard;
    -
    -		for(var i in script_files)
    -		{
    -			var src = script_files[i].src;
    -			if( !src || src.substr(0,folder_wildcard.length ) != folder_wildcard)
    -				continue;
    -
    -			try
    -			{
    -				if(LiteGraph.debug)
    -					console.log("Reloading: " + src);
    -				var dynamicScript = document.createElement("script");
    -				dynamicScript.type = "text/javascript";
    -				dynamicScript.src = src;
    -				docHeadObj.appendChild(dynamicScript);
    -				docHeadObj.removeChild(script_files[i]);
    -			}
    -			catch (err)
    -			{
    -				if(LiteGraph.throw_errors)
    -					throw err;
    -				if(LiteGraph.debug)
    -					console.log("Error while reloading " + src);
    -			}
    -		}
    -
    -		if(LiteGraph.debug)
    -			console.log("Nodes reloaded");
    -	},
    -
    -	//separated just to improve if it doesnt work
    -	cloneObject: function(obj, target)
    -	{
    -		if(obj == null) return null;
    -		var r = JSON.parse( JSON.stringify( obj ) );
    -		if(!target) return r;
    -
    -		for(var i in r)
    -			target[i] = r[i];
    -		return target;
    -	},
    -
    -	isValidConnection: function( type_a, type_b )
    -	{
    -		if( !type_a ||  //generic output
    -			!type_b || //generic input
    -			type_a == type_b || //same type (is valid for triggers)
    -			type_a == LiteGraph.EVENT && type_b == LiteGraph.ACTION )
    -        return true;
    -
    -		// Enforce string type to handle toLowerCase call (-1 number not ok)
    -		type_a = String(type_a); 
    -		type_b = String(type_b);
    -		type_a = type_a.toLowerCase();
    -		type_b = type_b.toLowerCase();
    -
    -		// For nodes supporting multiple connection types
    -		if( type_a.indexOf(",") == -1 && type_b.indexOf(",") == -1 )
    -			return type_a == type_b;
    -
    -		// Check all permutations to see if one is valid
    -		var supported_types_a = type_a.split(",");
    -		var supported_types_b = type_b.split(",");
    -		for(var i = 0; i < supported_types_a.length; ++i)
    -			for(var j = 0; j < supported_types_b.length; ++j)
    -				if( supported_types_a[i] == supported_types_b[j] )
    -					return true;
    -
    -		return false;
    -	},
    -
    -	registerSearchboxExtra: function( node_type, description, data )
    -	{
    -		this.searchbox_extras[ description ] = { type: node_type, desc: description, data: data };
    -	}
    -};
    -
    -//timer that works everywhere
    -if(typeof(performance) != "undefined")
    -	LiteGraph.getTime = performance.now.bind(performance);
    -else if(typeof(Date) != "undefined" && Date.now)
    -	LiteGraph.getTime = Date.now.bind(Date);
    -else if(typeof(process) != "undefined")
    -	LiteGraph.getTime = function(){
    -		var t = process.hrtime();
    -		return t[0]*0.001 + t[1]*(1e-6);
    -	}
    -else
    -  LiteGraph.getTime = function getTime() { return (new Date).getTime(); }
    -
    -
    -
    -
    -
    -
    -//*********************************************************************************
    -// LGraph CLASS
    -//*********************************************************************************
    -
    -/**
    -* LGraph is the class that contain a full graph. We instantiate one and add nodes to it, and then we can run the execution loop.
    -*
    -* @class LGraph
    -* @constructor
    -* @param {Object} o data from previous serialization [optional]
    -*/
    -
    -function LGraph( o )
    -{
    -	if (LiteGraph.debug)
    -		console.log("Graph created");
    -	this.list_of_graphcanvas = null;
    -	this.clear();
    -
    -	if(o)
    -		this.configure(o);
    -}
    -
    -global.LGraph = LiteGraph.LGraph = LGraph;
    -
    -//default supported types
    -LGraph.supported_types = ["number","string","boolean"];
    -
    -//used to know which types of connections support this graph (some graphs do not allow certain types)
    -LGraph.prototype.getSupportedTypes = function() { return this.supported_types || LGraph.supported_types; }
    -
    -LGraph.STATUS_STOPPED = 1;
    -LGraph.STATUS_RUNNING = 2;
    -
    -/**
    -* Removes all nodes from this graph
    -* @method clear
    -*/
    -
    -LGraph.prototype.clear = function()
    -{
    -	this.stop();
    -	this.status = LGraph.STATUS_STOPPED;
    -
    -	this.last_node_id = 1;
    -	this.last_link_id = 1;
    -
    -	this._version = -1; //used to detect changes
    -
    -	//safe clear
    -	if(this._nodes)
    -	for(var i = 0; i < this._nodes.length; ++i)
    -	{
    -		var node = this._nodes[i];
    -		if(node.onRemoved)
    -			node.onRemoved();
    -	}
    -
    -	//nodes
    -	this._nodes = [];
    -	this._nodes_by_id = {};
    -	this._nodes_in_order = []; //nodes that are executable sorted in execution order
    -	this._nodes_executable = null; //nodes that contain onExecute
    -
    -	//other scene stuff
    -	this._groups = [];
    -
    -	//links
    -	this.links = {}; //container with all the links
    -
    -	//iterations
    -	this.iteration = 0;
    -
    -	//custom data
    -	this.config = {};
    -
    -	//timing
    -	this.globaltime = 0;
    -	this.runningtime = 0;
    -	this.fixedtime =  0;
    -	this.fixedtime_lapse = 0.01;
    -	this.elapsed_time = 0.01;
    -	this.last_update_time = 0;
    -	this.starttime = 0;
    -
    -	this.catch_errors = true;
    -
    -	//subgraph_data
    -	this.inputs = {};
    -	this.outputs = {};
    -
    -	//notify canvas to redraw
    -	this.change();
    -
    -	this.sendActionToCanvas("clear");
    -}
    -
    -/**
    -* Attach Canvas to this graph
    -* @method attachCanvas
    -* @param {GraphCanvas} graph_canvas
    -*/
    -
    -LGraph.prototype.attachCanvas = function(graphcanvas)
    -{
    -	if(graphcanvas.constructor != LGraphCanvas)
    -		throw("attachCanvas expects a LGraphCanvas instance");
    -	if(graphcanvas.graph && graphcanvas.graph != this)
    -		graphcanvas.graph.detachCanvas( graphcanvas );
    -
    -	graphcanvas.graph = this;
    -	if(!this.list_of_graphcanvas)
    -		this.list_of_graphcanvas = [];
    -	this.list_of_graphcanvas.push(graphcanvas);
    -}
    -
    -/**
    -* Detach Canvas from this graph
    -* @method detachCanvas
    -* @param {GraphCanvas} graph_canvas
    -*/
    -LGraph.prototype.detachCanvas = function(graphcanvas)
    -{
    -	if(!this.list_of_graphcanvas)
    -		return;
    -
    -	var pos = this.list_of_graphcanvas.indexOf( graphcanvas );
    -	if(pos == -1)
    -		return;
    -	graphcanvas.graph = null;
    -	this.list_of_graphcanvas.splice(pos,1);
    -}
    -
    -/**
    -* Starts running this graph every interval milliseconds.
    -* @method start
    -* @param {number} interval amount of milliseconds between executions, if 0 then it renders to the monitor refresh rate
    -*/
    -
    -LGraph.prototype.start = function( interval )
    -{
    -	if( this.status == LGraph.STATUS_RUNNING )
    -		return;
    -	this.status = LGraph.STATUS_RUNNING;
    -
    -	if(this.onPlayEvent)
    -		this.onPlayEvent();
    -
    -	this.sendEventToAllNodes("onStart");
    -
    -	//launch
    -	this.starttime = LiteGraph.getTime();
    -	this.last_update_time = this.starttime;
    -	interval = interval || 0;
    -	var that = this;
    -
    -	if(interval == 0 && typeof(window) != "undefined" && window.requestAnimationFrame )
    -	{
    -		function on_frame()
    -		{
    -			if(that.execution_timer_id != -1)
    -				return;
    -			window.requestAnimationFrame(on_frame);
    -			that.runStep(1, !this.catch_errors );
    -		}
    -		this.execution_timer_id = -1;
    -		on_frame();
    -	}
    -	else
    -		this.execution_timer_id = setInterval( function() {
    -			//execute
    -			that.runStep(1, !this.catch_errors );
    -		},interval);
    -}
    -
    -/**
    -* Stops the execution loop of the graph
    -* @method stop execution
    -*/
    -
    -LGraph.prototype.stop = function()
    -{
    -	if( this.status == LGraph.STATUS_STOPPED )
    -		return;
    -
    -	this.status = LGraph.STATUS_STOPPED;
    -
    -	if(this.onStopEvent)
    -		this.onStopEvent();
    -
    -	if(this.execution_timer_id != null)
    -	{
    -		if( this.execution_timer_id != -1 )
    -			clearInterval(this.execution_timer_id);
    -		this.execution_timer_id = null;
    -	}
    -
    -	this.sendEventToAllNodes("onStop");
    -}
    -
    -/**
    -* Run N steps (cycles) of the graph
    -* @method runStep
    -* @param {number} num number of steps to run, default is 1
    -*/
    -
    -LGraph.prototype.runStep = function( num, do_not_catch_errors )
    -{
    -	num = num || 1;
    -
    -	var start = LiteGraph.getTime();
    -	this.globaltime = 0.001 * (start - this.starttime);
    -
    -	var nodes = this._nodes_executable ? this._nodes_executable : this._nodes;
    -	if(!nodes)
    -		return;
    -
    -	if( do_not_catch_errors )
    -	{
    -		//iterations
    -		for(var i = 0; i < num; i++)
    -		{
    -			for( var j = 0, l = nodes.length; j < l; ++j )
    -			{
    -				var node = nodes[j];
    -				if( node.mode == LiteGraph.ALWAYS && node.onExecute )
    -					node.onExecute();
    -			}
    -
    -			this.fixedtime += this.fixedtime_lapse;
    -			if( this.onExecuteStep )
    -				this.onExecuteStep();
    -		}
    -
    -		if( this.onAfterExecute )
    -			this.onAfterExecute();
    -	}
    -	else
    -	{
    -		try
    -		{
    -			//iterations
    -			for(var i = 0; i < num; i++)
    -			{
    -				for( var j = 0, l = nodes.length; j < l; ++j )
    -				{
    -					var node = nodes[j];
    -					if( node.mode == LiteGraph.ALWAYS && node.onExecute )
    -						node.onExecute();
    -				}
    -
    -				this.fixedtime += this.fixedtime_lapse;
    -				if( this.onExecuteStep )
    -					this.onExecuteStep();
    -			}
    -
    -			if( this.onAfterExecute )
    -				this.onAfterExecute();
    -			this.errors_in_execution = false;
    -		}
    -		catch (err)
    -		{
    -			this.errors_in_execution = true;
    -			if(LiteGraph.throw_errors)
    -				throw err;
    -			if(LiteGraph.debug)
    -				console.log("Error during execution: " + err);
    -			this.stop();
    -		}
    -	}
    -
    -	var now = LiteGraph.getTime();
    -	var elapsed = now - start;
    -	if (elapsed == 0)
    -		elapsed = 1;
    -	this.execution_time = 0.001 * elapsed;
    -	this.globaltime += 0.001 * elapsed;
    -	this.iteration += 1;
    -	this.elapsed_time = (now - this.last_update_time) * 0.001;
    -	this.last_update_time = now;
    -}
    -
    -/**
    -* Updates the graph execution order according to relevance of the nodes (nodes with only outputs have more relevance than
    -* nodes with only inputs.
    -* @method updateExecutionOrder
    -*/
    -LGraph.prototype.updateExecutionOrder = function()
    -{
    -	this._nodes_in_order = this.computeExecutionOrder( false );
    -	this._nodes_executable = [];
    -	for(var i = 0; i < this._nodes_in_order.length; ++i)
    -		if( this._nodes_in_order[i].onExecute )
    -			this._nodes_executable.push( this._nodes_in_order[i] );
    -}
    -
    -//This is more internal, it computes the order and returns it
    -LGraph.prototype.computeExecutionOrder = function( only_onExecute, set_level )
    -{
    -	var L = [];
    -	var S = [];
    -	var M = {};
    -	var visited_links = {}; //to avoid repeating links
    -	var remaining_links = {}; //to a
    -
    -	//search for the nodes without inputs (starting nodes)
    -	for (var i = 0, l = this._nodes.length; i < l; ++i)
    -	{
    -		var node = this._nodes[i];
    -		if( only_onExecute && !node.onExecute )
    -			continue;
    -
    -		M[node.id] = node; //add to pending nodes
    -
    -		var num = 0; //num of input connections
    -		if(node.inputs)
    -			for(var j = 0, l2 = node.inputs.length; j < l2; j++)
    -				if(node.inputs[j] && node.inputs[j].link != null)
    -					num += 1;
    -
    -		if(num == 0) //is a starting node
    -		{
    -			S.push(node);
    -			if(set_level)
    -				node._level = 1;
    -		}
    -		else //num of input links
    -		{
    -			if(set_level)
    -				node._level = 0;
    -			remaining_links[node.id] = num;
    -		}
    -	}
    -
    -	while(true)
    -	{
    -		if(S.length == 0)
    -			break;
    -
    -		//get an starting node
    -		var node = S.shift();
    -		L.push(node); //add to ordered list
    -		delete M[node.id]; //remove from the pending nodes
    -
    -		if(!node.outputs)
    -			continue;
    -
    -		//for every output
    -		for(var i = 0; i < node.outputs.length; i++)
    -		{
    -			var output = node.outputs[i];
    -			//not connected
    -			if(output == null || output.links == null || output.links.length == 0)
    -				continue;
    -
    -			//for every connection
    -			for(var j = 0; j < output.links.length; j++)
    -			{
    -				var link_id = output.links[j];
    -				var link = this.links[link_id];
    -				if(!link)
    -					continue;
    -
    -				//already visited link (ignore it)
    -				if(visited_links[ link.id ])
    -					continue;
    -
    -				var target_node = this.getNodeById( link.target_id );
    -				if(target_node == null)
    -				{
    -					visited_links[ link.id ] = true;
    -					continue;
    -				}
    -
    -				if(set_level && (!target_node._level || target_node._level <= node._level))
    -					target_node._level = node._level + 1;
    -
    -				visited_links[link.id] = true; //mark as visited
    -				remaining_links[target_node.id] -= 1; //reduce the number of links remaining
    -				if (remaining_links[ target_node.id ] == 0)
    -					S.push(target_node); //if no more links, then add to starters array
    -			}
    -		}
    -	}
    -
    -	//the remaining ones (loops)
    -	for(var i in M)
    -		L.push( M[i] );
    -
    -	if( L.length != this._nodes.length && LiteGraph.debug )
    -		console.warn("something went wrong, nodes missing");
    -
    -	var l = L.length;
    -
    -	//save order number in the node
    -	for(var i = 0; i < l; ++i)
    -		L[i].order = i;
    -
    -	//sort now by priority
    -	L = L.sort(function(A,B){ 
    -		var Ap = A.constructor.priority || A.priority || 0;
    -		var Bp = B.constructor.priority || B.priority || 0;
    -		if(Ap == Bp)
    -			return A.order - B.order;
    -		return Ap - Bp;
    -	});
    -
    -	//save order number in the node, again...
    -	for(var i = 0; i < l; ++i)
    -		L[i].order = i;
    -
    -	return L;
    -}
    -
    -/**
    -* Returns all the nodes that could affect this one (ancestors) by crawling all the inputs recursively.
    -* It doesnt include the node itself
    -* @method getAncestors
    -* @return {Array} an array with all the LGraphNodes that affect this node, in order of execution
    -*/
    -LGraph.prototype.getAncestors = function( node )
    -{
    -	var ancestors = [];
    -	var pending = [node];
    -	var visited = {};
    -
    -	while (pending.length)
    -	{
    -		var current = pending.shift();
    -		if(!current.inputs)
    -			continue;
    -		if( !visited[ current.id ] && current != node )
    -		{
    -			visited[ current.id ] = true;
    -			ancestors.push( current );
    -		}
    -
    -		for(var i = 0; i < current.inputs.length;++i)
    -		{
    -			var input = current.getInputNode(i);
    -			if( input && ancestors.indexOf( input ) == -1)
    -			{
    -				pending.push( input );
    -			}
    -		}
    -	}
    -
    -	ancestors.sort(function(a,b){ return a.order - b.order;});
    -	return ancestors;
    -}
    -
    -/**
    -* Positions every node in a more readable manner
    -* @method arrange
    -*/
    -LGraph.prototype.arrange = function( margin )
    -{
    -	margin = margin || 40;
    -
    -	var nodes = this.computeExecutionOrder( false, true );
    -	var columns = [];
    -	for(var i = 0; i < nodes.length; ++i)
    -	{
    -		var node = nodes[i];
    -		var col = node._level || 1;
    -		if(!columns[col])
    -			columns[col] = [];
    -		columns[col].push( node );
    -	}
    -
    -	var x = margin;
    -
    -	for(var i = 0; i < columns.length; ++i)
    -	{
    -		var column = columns[i];
    -		if(!column)
    -			continue;
    -		var max_size = 100;
    -		var y = margin;
    -		for(var j = 0; j < column.length; ++j)
    -		{
    -			var node = column[j];
    -			node.pos[0] = x;
    -			node.pos[1] = y;
    -			if(node.size[0] > max_size)
    -				max_size = node.size[0];
    -			y += node.size[1] + margin;
    -		}
    -		x += max_size + margin;
    -	}
    -
    -	this.setDirtyCanvas(true,true);
    -}
    -
    -
    -/**
    -* Returns the amount of time the graph has been running in milliseconds
    -* @method getTime
    -* @return {number} number of milliseconds the graph has been running
    -*/
    -LGraph.prototype.getTime = function()
    -{
    -	return this.globaltime;
    -}
    -
    -/**
    -* Returns the amount of time accumulated using the fixedtime_lapse var. This is used in context where the time increments should be constant
    -* @method getFixedTime
    -* @return {number} number of milliseconds the graph has been running
    -*/
    -
    -LGraph.prototype.getFixedTime = function()
    -{
    -	return this.fixedtime;
    -}
    -
    -/**
    -* Returns the amount of time it took to compute the latest iteration. Take into account that this number could be not correct
    -* if the nodes are using graphical actions
    -* @method getElapsedTime
    -* @return {number} number of milliseconds it took the last cycle
    -*/
    -
    -LGraph.prototype.getElapsedTime = function()
    -{
    -	return this.elapsed_time;
    -}
    -
    -/**
    -* Sends an event to all the nodes, useful to trigger stuff
    -* @method sendEventToAllNodes
    -* @param {String} eventname the name of the event (function to be called)
    -* @param {Array} params parameters in array format
    -*/
    -
    -LGraph.prototype.sendEventToAllNodes = function( eventname, params, mode )
    -{
    -	mode = mode || LiteGraph.ALWAYS;
    -
    -	var nodes = this._nodes_in_order ? this._nodes_in_order : this._nodes;
    -	if(!nodes)
    -		return;
    -
    -	for( var j = 0, l = nodes.length; j < l; ++j )
    -	{
    -		var node = nodes[j];
    -		if( !node[eventname] || node.mode != mode )
    -			continue;
    -		if(params === undefined)
    -			node[eventname]();
    -		else if(params && params.constructor === Array)
    -			node[eventname].apply( node, params );
    -		else
    -			node[eventname](params);
    -	}
    -}
    -
    -LGraph.prototype.sendActionToCanvas = function(action, params)
    -{
    -	if(!this.list_of_graphcanvas)
    -		return;
    -
    -	for(var i = 0; i < this.list_of_graphcanvas.length; ++i)
    -	{
    -		var c = this.list_of_graphcanvas[i];
    -		if( c[action] )
    -			c[action].apply(c, params);
    -	}
    -}
    -
    -/**
    -* Adds a new node instasnce to this graph
    -* @method add
    -* @param {LGraphNode} node the instance of the node
    -*/
    -
    -LGraph.prototype.add = function( node, skip_compute_order)
    -{
    -	if(!node)
    -		return;
    -
    -	//groups
    -	if( node.constructor === LGraphGroup )
    -	{
    -		this._groups.push( node );
    -		this.setDirtyCanvas(true);
    -		this.change();
    -		node.graph = this;
    -		this._version++;
    -		return;
    -	}
    -
    -	//nodes
    -	if(node.id != -1 && this._nodes_by_id[node.id] != null)
    -	{
    -		console.warn("LiteGraph: there is already a node with this ID, changing it");
    -		node.id = ++this.last_node_id;
    -	}
    -
    -	if(this._nodes.length >= LiteGraph.MAX_NUMBER_OF_NODES)
    -		throw("LiteGraph: max number of nodes in a graph reached");
    -
    -	//give him an id
    -	if(node.id == null || node.id == -1)
    -		node.id = ++this.last_node_id;
    -	else if (this.last_node_id < node.id)
    -		this.last_node_id = node.id;
    -
    -
    -	node.graph = this;
    -	this._version++;
    -
    -	this._nodes.push(node);
    -	this._nodes_by_id[node.id] = node;
    -
    -	if(node.onAdded)
    -		node.onAdded( this );
    -
    -	if(this.config.align_to_grid)
    -		node.alignToGrid();
    -
    -	if(!skip_compute_order)
    -		this.updateExecutionOrder();
    -
    -	if(this.onNodeAdded)
    -		this.onNodeAdded(node);
    -
    -
    -	this.setDirtyCanvas(true);
    -	this.change();
    -
    -	return node; //to chain actions
    -}
    -
    -/**
    -* Removes a node from the graph
    -* @method remove
    -* @param {LGraphNode} node the instance of the node
    -*/
    -
    -LGraph.prototype.remove = function(node)
    -{
    -	if(node.constructor === LiteGraph.LGraphGroup)
    -	{
    -		var index = this._groups.indexOf(node);
    -		if(index != -1)
    -			this._groups.splice(index,1);
    -		node.graph = null;
    -		this._version++;
    -		this.setDirtyCanvas(true,true);
    -		this.change();
    -		return;
    -	}
    -
    -	if(this._nodes_by_id[node.id] == null)
    -		return; //not found
    -
    -	if(node.ignore_remove)
    -		return; //cannot be removed
    -
    -	//disconnect inputs
    -	if(node.inputs)
    -		for(var i = 0; i < node.inputs.length; i++)
    -		{
    -			var slot = node.inputs[i];
    -			if(slot.link != null)
    -				node.disconnectInput(i);
    -		}
    -
    -	//disconnect outputs
    -	if(node.outputs)
    -		for(var i = 0; i < node.outputs.length; i++)
    -		{
    -			var slot = node.outputs[i];
    -			if(slot.links != null && slot.links.length)
    -				node.disconnectOutput(i);
    -		}
    -
    -	//node.id = -1; //why?
    -
    -	//callback
    -	if(node.onRemoved)
    -		node.onRemoved();
    -
    -	node.graph = null;
    -	this._version++;
    -
    -	//remove from canvas render
    -	if(this.list_of_graphcanvas)
    -	{
    -		for(var i = 0; i < this.list_of_graphcanvas.length; ++i)
    -		{
    -			var canvas = this.list_of_graphcanvas[i];
    -			if(canvas.selected_nodes[node.id])
    -				delete canvas.selected_nodes[node.id];
    -			if(canvas.node_dragged == node)
    -				canvas.node_dragged = null;
    -		}
    -	}
    -
    -	//remove from containers
    -	var pos = this._nodes.indexOf(node);
    -	if(pos != -1)
    -		this._nodes.splice(pos,1);
    -	delete this._nodes_by_id[node.id];
    -
    -	if(this.onNodeRemoved)
    -		this.onNodeRemoved(node);
    -
    -	this.setDirtyCanvas(true,true);
    -	this.change();
    -
    -	this.updateExecutionOrder();
    -}
    -
    -/**
    -* Returns a node by its id.
    -* @method getNodeById
    -* @param {Number} id
    -*/
    -
    -LGraph.prototype.getNodeById = function( id )
    -{
    -	if( id == null )
    -		return null;
    -	return this._nodes_by_id[ id ];
    -}
    -
    -/**
    -* Returns a list of nodes that matches a class
    -* @method findNodesByClass
    -* @param {Class} classObject the class itself (not an string)
    -* @return {Array} a list with all the nodes of this type
    -*/
    -
    -LGraph.prototype.findNodesByClass = function(classObject)
    -{
    -	var r = [];
    -	for(var i = 0, l = this._nodes.length; i < l; ++i)
    -		if(this._nodes[i].constructor === classObject)
    -			r.push(this._nodes[i]);
    -	return r;
    -}
    -
    -/**
    -* Returns a list of nodes that matches a type
    -* @method findNodesByType
    -* @param {String} type the name of the node type
    -* @return {Array} a list with all the nodes of this type
    -*/
    -
    -LGraph.prototype.findNodesByType = function(type)
    -{
    -	var type = type.toLowerCase();
    -	var r = [];
    -	for(var i = 0, l = this._nodes.length; i < l; ++i)
    -		if(this._nodes[i].type.toLowerCase() == type )
    -			r.push(this._nodes[i]);
    -	return r;
    -}
    -
    -/**
    -* Returns a list of nodes that matches a name
    -* @method findNodesByTitle
    -* @param {String} name the name of the node to search
    -* @return {Array} a list with all the nodes with this name
    -*/
    -
    -LGraph.prototype.findNodesByTitle = function(title)
    -{
    -	var result = [];
    -	for(var i = 0, l = this._nodes.length; i < l; ++i)
    -		if(this._nodes[i].title == title)
    -			result.push(this._nodes[i]);
    -	return result;
    -}
    -
    -/**
    -* Returns the top-most node in this position of the canvas
    -* @method getNodeOnPos
    -* @param {number} x the x coordinate in canvas space
    -* @param {number} y the y coordinate in canvas space
    -* @param {Array} nodes_list a list with all the nodes to search from, by default is all the nodes in the graph
    -* @return {LGraphNode} the node at this position or null
    -*/
    -LGraph.prototype.getNodeOnPos = function( x, y, nodes_list, margin )
    -{
    -	nodes_list = nodes_list || this._nodes;
    -	for (var i = nodes_list.length - 1; i >= 0; i--)
    -	{
    -		var n = nodes_list[i];
    -		if(n.isPointInside( x, y, margin ))
    -			return n;
    -	}
    -	return null;
    -}
    -
    -/**
    -* Returns the top-most group in that position
    -* @method getGroupOnPos
    -* @param {number} x the x coordinate in canvas space
    -* @param {number} y the y coordinate in canvas space
    -* @return {LGraphGroup} the group or null
    -*/
    -LGraph.prototype.getGroupOnPos = function(x,y)
    -{
    -	for (var i = this._groups.length - 1; i >= 0; i--)
    -	{
    -		var g = this._groups[i];
    -		if(g.isPointInside( x, y, 2, true ))
    -			return g;
    -	}
    -	return null;
    -}
    -
    -// ********** GLOBALS *****************
    -
    -/**
    -* Tell this graph it has a global graph input of this type
    -* @method addGlobalInput
    -* @param {String} name
    -* @param {String} type
    -* @param {*} value [optional]
    -*/
    -LGraph.prototype.addInput = function(name, type, value)
    -{
    -	var input = this.inputs[ name ];
    -	if( input ) //already exist
    -		return;
    -
    -	this.inputs[ name ] = { name: name, type: type, value: value };
    -	this._version++;
    -
    -	if(this.onInputAdded)
    -		this.onInputAdded(name, type);
    -
    -	if(this.onInputsOutputsChange)
    -		this.onInputsOutputsChange();
    -}
    -
    -/**
    -* Assign a data to the global graph input
    -* @method setGlobalInputData
    -* @param {String} name
    -* @param {*} data
    -*/
    -LGraph.prototype.setInputData = function(name, data)
    -{
    -	var input = this.inputs[name];
    -	if (!input)
    -		return;
    -	input.value = data;
    -}
    -
    -/**
    -* Returns the current value of a global graph input
    -* @method getInputData
    -* @param {String} name
    -* @return {*} the data
    -*/
    -LGraph.prototype.getInputData = function(name)
    -{
    -	var input = this.inputs[name];
    -	if (!input)
    -		return null;
    -	return input.value;
    -}
    -
    -/**
    -* Changes the name of a global graph input
    -* @method renameInput
    -* @param {String} old_name
    -* @param {String} new_name
    -*/
    -LGraph.prototype.renameInput = function(old_name, name)
    -{
    -	if(name == old_name)
    -		return;
    -
    -	if(!this.inputs[old_name])
    -		return false;
    -
    -	if(this.inputs[name])
    -	{
    -		console.error("there is already one input with that name");
    -		return false;
    -	}
    -
    -	this.inputs[name] = this.inputs[old_name];
    -	delete this.inputs[old_name];
    -	this._version++;
    -
    -	if(this.onInputRenamed)
    -		this.onInputRenamed(old_name, name);
    -
    -	if(this.onInputsOutputsChange)
    -		this.onInputsOutputsChange();
    -}
    -
    -/**
    -* Changes the type of a global graph input
    -* @method changeInputType
    -* @param {String} name
    -* @param {String} type
    -*/
    -LGraph.prototype.changeInputType = function(name, type)
    -{
    -	if(!this.inputs[name])
    -		return false;
    -
    -	if(this.inputs[name].type && this.inputs[name].type.toLowerCase() == type.toLowerCase() )
    -		return;
    -
    -	this.inputs[name].type = type;
    -	this._version++;
    -	if(this.onInputTypeChanged)
    -		this.onInputTypeChanged(name, type);
    -}
    -
    -/**
    -* Removes a global graph input
    -* @method removeInput
    -* @param {String} name
    -* @param {String} type
    -*/
    -LGraph.prototype.removeInput = function(name)
    -{
    -	if(!this.inputs[name])
    -		return false;
    -
    -	delete this.inputs[name];
    -	this._version++;
    -
    -	if(this.onInputRemoved)
    -		this.onInputRemoved(name);
    -
    -	if(this.onInputsOutputsChange)
    -		this.onInputsOutputsChange();
    -	return true;
    -}
    -
    -/**
    -* Creates a global graph output
    -* @method addOutput
    -* @param {String} name
    -* @param {String} type
    -* @param {*} value
    -*/
    -LGraph.prototype.addOutput = function(name, type, value)
    -{
    -	this.outputs[name] = { name: name, type: type, value: value };
    -	this._version++;
    -
    -	if(this.onOutputAdded)
    -		this.onOutputAdded(name, type);
    -
    -	if(this.onInputsOutputsChange)
    -		this.onInputsOutputsChange();
    -}
    -
    -/**
    -* Assign a data to the global output
    -* @method setOutputData
    -* @param {String} name
    -* @param {String} value
    -*/
    -LGraph.prototype.setOutputData = function(name, value)
    -{
    -	var output = this.outputs[ name ];
    -	if (!output)
    -		return;
    -	output.value = value;
    -}
    -
    -/**
    -* Returns the current value of a global graph output
    -* @method getOutputData
    -* @param {String} name
    -* @return {*} the data
    -*/
    -LGraph.prototype.getOutputData = function(name)
    -{
    -	var output = this.outputs[name];
    -	if (!output)
    -		return null;
    -	return output.value;
    -}
    -
    -/**
    -* Renames a global graph output
    -* @method renameOutput
    -* @param {String} old_name
    -* @param {String} new_name
    -*/
    -LGraph.prototype.renameOutput = function(old_name, name)
    -{
    -	if(!this.outputs[old_name])
    -		return false;
    -
    -	if(this.outputs[name])
    -	{
    -		console.error("there is already one output with that name");
    -		return false;
    -	}
    -
    -	this.outputs[name] = this.outputs[old_name];
    -	delete this.outputs[old_name];
    -	this._version++;
    -
    -	if(this.onOutputRenamed)
    -		this.onOutputRenamed(old_name, name);
    -
    -	if(this.onInputsOutputsChange)
    -		this.onInputsOutputsChange();
    -}
    -
    -/**
    -* Changes the type of a global graph output
    -* @method changeOutputType
    -* @param {String} name
    -* @param {String} type
    -*/
    -LGraph.prototype.changeOutputType = function(name, type)
    -{
    -	if(!this.outputs[name])
    -		return false;
    -
    -	if(this.outputs[name].type && this.outputs[name].type.toLowerCase() == type.toLowerCase() )
    -		return;
    -
    -	this.outputs[name].type = type;
    -	this._version++;
    -	if(this.onOutputTypeChanged)
    -		this.onOutputTypeChanged(name, type);
    -}
    -
    -/**
    -* Removes a global graph output
    -* @method removeOutput
    -* @param {String} name
    -*/
    -LGraph.prototype.removeOutput = function(name)
    -{
    -	if(!this.outputs[name])
    -		return false;
    -	delete this.outputs[name];
    -	this._version++;
    -
    -	if(this.onOutputRemoved)
    -		this.onOutputRemoved(name);
    -
    -	if(this.onInputsOutputsChange)
    -		this.onInputsOutputsChange();
    -	return true;
    -}
    -
    -LGraph.prototype.triggerInput = function(name,value)
    -{
    -	var nodes = this.findNodesByTitle(name);
    -	for(var i = 0; i < nodes.length; ++i)
    -		nodes[i].onTrigger(value);
    -}
    -
    -LGraph.prototype.setCallback = function(name,func)
    -{
    -	var nodes = this.findNodesByTitle(name);
    -	for(var i = 0; i < nodes.length; ++i)
    -		nodes[i].setTrigger(func);
    -}
    -
    -
    -LGraph.prototype.connectionChange = function( node, link_info )
    -{
    -	this.updateExecutionOrder();
    -	if( this.onConnectionChange )
    -		this.onConnectionChange( node );
    -	this._version++;
    -	this.sendActionToCanvas("onConnectionChange");
    -}
    -
    -/**
    -* returns if the graph is in live mode
    -* @method isLive
    -*/
    -
    -LGraph.prototype.isLive = function()
    -{
    -	if(!this.list_of_graphcanvas)
    -		return false;
    -
    -	for(var i = 0; i < this.list_of_graphcanvas.length; ++i)
    -	{
    -		var c = this.list_of_graphcanvas[i];
    -		if(c.live_mode)
    -			return true;
    -	}
    -	return false;
    -}
    -
    -/**
    -* clears the triggered slot animation in all links (stop visual animation)
    -* @method clearTriggeredSlots
    -*/
    -LGraph.prototype.clearTriggeredSlots = function()
    -{
    -	for(var i in this.links)
    -	{
    -		var link_info = this.links[i];
    -		if( !link_info )
    -			continue;
    -		if( link_info._last_time )
    -			link_info._last_time = 0;
    -	}
    -}
    -
    -
    -/* Called when something visually changed (not the graph!) */
    -LGraph.prototype.change = function()
    -{
    -	if(LiteGraph.debug)
    -		console.log("Graph changed");
    -	this.sendActionToCanvas("setDirty",[true,true]);
    -	if(this.on_change)
    -		this.on_change(this);
    -}
    -
    -LGraph.prototype.setDirtyCanvas = function(fg,bg)
    -{
    -	this.sendActionToCanvas("setDirty",[fg,bg]);
    -}
    -
    -/**
    -* Destroys a link
    -* @method removeLink
    -* @param {Number} link_id
    -*/
    -LGraph.prototype.removeLink = function(link_id)
    -{
    -	var link = this.links[ link_id ];
    -	if(!link)
    -		return;
    -	var node = this.getNodeById( link.target_id );
    -	if(node)
    -		node.disconnectInput( link.target_slot );
    -}
    -
    -
    -//save and recover app state ***************************************
    -/**
    -* Creates a Object containing all the info about this graph, it can be serialized
    -* @method serialize
    -* @return {Object} value of the node
    -*/
    -LGraph.prototype.serialize = function()
    -{
    -	var nodes_info = [];
    -	for(var i = 0, l = this._nodes.length; i < l; ++i)
    -		nodes_info.push( this._nodes[i].serialize() );
    -
    -	//pack link info into a non-verbose format
    -	var links = [];
    -	for(var i in this.links) //links is an OBJECT
    -	{
    -		var link = this.links[i];
    -		links.push([ link.id, link.origin_id, link.origin_slot, link.target_id, link.target_slot, link.type ]);
    -	}
    -
    -	var groups_info = [];
    -	for(var i = 0; i < this._groups.length; ++i)
    -		groups_info.push( this._groups[i].serialize() );
    -
    -	var data = {
    -		last_node_id: this.last_node_id,
    -		last_link_id: this.last_link_id,
    -		nodes: nodes_info,
    -		links: links, 
    -		groups: groups_info,
    -		config: this.config,
    -		version: LiteGraph.VERSION
    -	};
    -
    -	return data;
    -}
    -
    -
    -/**
    -* Configure a graph from a JSON string
    -* @method configure
    -* @param {String} str configure a graph from a JSON string
    -* @param {Boolean} returns if there was any error parsing
    -*/
    -LGraph.prototype.configure = function( data, keep_old )
    -{
    -	if(!data)
    -		return;
    -
    -	if(!keep_old)
    -		this.clear();
    -
    -	var nodes = data.nodes;
    -
    -	//decode links info (they are very verbose)
    -	if(data.links && data.links.constructor === Array)
    -	{
    -		var links = [];
    -		for(var i = 0; i < data.links.length; ++i)
    -		{
    -			var link_data = data.links[i];
    -			var link = new LLink();
    -			link.configure( link_data );
    -			links[ link.id ] = link;
    -		}
    -		data.links = links;
    -	}
    -
    -	//copy all stored fields
    -	for (var i in data)
    -		this[i] = data[i];
    -
    -	var error = false;
    -
    -	//create nodes
    -	this._nodes = [];
    -	if(nodes)
    -	{
    -		for(var i = 0, l = nodes.length; i < l; ++i)
    -		{
    -			var n_info = nodes[i]; //stored info
    -			var node = LiteGraph.createNode( n_info.type, n_info.title );
    -			if(!node)
    -			{
    -				if(LiteGraph.debug)
    -					console.log("Node not found or has errors: " + n_info.type);
    -
    -				//in case of error we create a replacement node to avoid losing info
    -				node = new LGraphNode();
    -				node.last_serialization = n_info;
    -				node.has_errors = true;
    -				error = true;
    -				//continue;
    -			}
    -
    -			node.id = n_info.id; //id it or it will create a new id
    -			this.add(node, true); //add before configure, otherwise configure cannot create links
    -		}
    -
    -		//configure nodes afterwards so they can reach each other
    -		for(var i = 0, l = nodes.length; i < l; ++i)
    -		{
    -			var n_info = nodes[i];
    -			var node = this.getNodeById( n_info.id );
    -			if(node)
    -				node.configure( n_info );
    -		}
    -	}
    -
    -	//groups
    -	this._groups.length = 0;
    -	if( data.groups )
    -	for(var i = 0; i < data.groups.length; ++i )
    -	{
    -		var group = new LiteGraph.LGraphGroup();
    -		group.configure( data.groups[i] );
    -		this.add( group );
    -	}
    -
    -	this.updateExecutionOrder();
    -	this._version++;
    -	this.setDirtyCanvas(true,true);
    -	return error;
    -}
    -
    -LGraph.prototype.load = function(url)
    -{
    -	var that = this;
    -	var req = new XMLHttpRequest();
    -	req.open('GET', url, true);
    -	req.send(null);
    -	req.onload = function (oEvent) {
    -		if(req.status !== 200)
    -		{
    -			console.error("Error loading graph:",req.status,req.response);
    -			return;
    -		}
    -		var data = JSON.parse( req.response );
    -		that.configure(data);
    -	}
    -	req.onerror = function(err)
    -	{
    -		console.error("Error loading graph:",err);
    -	}
    -}
    -
    -LGraph.prototype.onNodeTrace = function(node, msg, color)
    -{
    -	//TODO
    -}
    -
    -//this is the class in charge of storing link information
    -function LLink( id, type, origin_id, origin_slot, target_id, target_slot )
    -{
    -	this.id = id;
    -	this.type = type;
    -	this.origin_id = origin_id;
    -	this.origin_slot = origin_slot;
    -	this.target_id = target_id;
    -	this.target_slot = target_slot;
    -
    -	this._data = null;
    -	this._pos = new Float32Array(2); //center
    -}
    -
    -LLink.prototype.configure = function(o)
    -{
    -	if(o.constructor === Array)
    -	{
    -		this.id = o[0];
    -		this.origin_id = o[1];
    -		this.origin_slot = o[2];
    -		this.target_id = o[3];
    -		this.target_slot = o[4];
    -		this.type = o[5];
    -	}
    -	else
    -	{
    -		this.id = o.id;
    -		this.type = o.type;
    -		this.origin_id = o.origin_id;
    -		this.origin_slot = o.origin_slot;
    -		this.target_id = o.target_id;
    -		this.target_slot = o.target_slot;
    -	}
    -}
    -
    -LLink.prototype.serialize = function()
    -{
    -	return [ this.id, this.type, this.origin_id, this.origin_slot, this.target_id, this.target_slot ];
    -}
    -
    -LiteGraph.LLink = LLink;
    -
    -// *************************************************************
    -//   Node CLASS                                          *******
    -// *************************************************************
    -
    -/*
    -	title: string
    -	pos: [x,y]
    -	size: [x,y]
    -
    -	input|output: every connection
    -		+  { name:string, type:string, pos: [x,y]=Optional, direction: "input"|"output", links: Array });
    -
    -	general properties:
    -		+ clip_area: if you render outside the node, it will be cliped
    -		+ unsafe_execution: not allowed for safe execution
    -		+ skip_repeated_outputs: when adding new outputs, it wont show if there is one already connected
    -		+ resizable: if set to false it wont be resizable with the mouse
    -		+ horizontal: slots are distributed horizontally
    -		+ widgets_up: widgets start from the top of the node
    -	
    -	flags object:
    -		+ collapsed: if it is collapsed
    -
    -	supported callbacks:
    -		+ onAdded: when added to graph (warning: this is called BEFORE the node is configured when loading)
    -		+ onRemoved: when removed from graph
    -		+ onStart:	when the graph starts playing
    -		+ onStop:	when the graph stops playing
    -		+ onDrawForeground: render the inside widgets inside the node
    -		+ onDrawBackground: render the background area inside the node (only in edit mode)
    -		+ onMouseDown
    -		+ onMouseMove
    -		+ onMouseUp
    -		+ onMouseEnter
    -		+ onMouseLeave
    -		+ onExecute: execute the node
    -		+ onPropertyChanged: when a property is changed in the panel (return true to skip default behaviour)
    -		+ onGetInputs: returns an array of possible inputs
    -		+ onGetOutputs: returns an array of possible outputs
    -		+ onBounding: in case this node has a bigger bounding than the node itself (the callback receives the bounding as [x,y,w,h])
    -		+ onDblClick: double clicked in the node
    -		+ onInputDblClick: input slot double clicked (can be used to automatically create a node connected)
    -		+ onOutputDblClick: output slot double clicked (can be used to automatically create a node connected)
    -		+ onConfigure: called after the node has been configured
    -		+ onSerialize: to add extra info when serializing (the callback receives the object that should be filled with the data)
    -		+ onSelected
    -		+ onDeselected
    -		+ onDropItem : DOM item dropped over the node
    -		+ onDropFile : file dropped over the node
    -		+ onConnectInput : if returns false the incoming connection will be canceled
    -		+ onConnectionsChange : a connection changed (new one or removed) (LiteGraph.INPUT or LiteGraph.OUTPUT, slot, true if connected, link_info, input_info )
    -		+ onAction: action slot triggered
    -		+ getExtraMenuOptions: to add option to context menu
    -*/
    -
    -/**
    -* Base Class for all the node type classes
    -* @class LGraphNode
    -* @param {String} name a name for the node
    -*/
    -
    -function LGraphNode(title)
    -{
    -	this._ctor(title);
    -}
    -
    -global.LGraphNode = LiteGraph.LGraphNode = LGraphNode;
    -
    -LGraphNode.prototype._ctor = function( title )
    -{
    -	this.title = title || "Unnamed";
    -	this.size = [LiteGraph.NODE_WIDTH,60];
    -	this.graph = null;
    -
    -	this._pos = new Float32Array(10,10);
    -
    -	Object.defineProperty( this, "pos", {
    -		set: function(v)
    -		{
    -			if(!v || v.length < 2)
    -				return;
    -			this._pos[0] = v[0];
    -			this._pos[1] = v[1];
    -		},
    -		get: function()
    -		{
    -			return this._pos;
    -		},
    -		enumerable: true
    -	});
    -
    -	this.id = -1; //not know till not added
    -	this.type = null;
    -
    -	//inputs available: array of inputs
    -	this.inputs = [];
    -	this.outputs = [];
    -	this.connections = [];
    -
    -	//local data
    -	this.properties = {}; //for the values
    -	this.properties_info = []; //for the info
    -
    -	this.flags = {};
    -}
    -
    -/**
    -* configure a node from an object containing the serialized info
    -* @method configure
    -*/
    -LGraphNode.prototype.configure = function(info)
    -{
    -	if(this.graph)
    -		this.graph._version++;
    -
    -	for (var j in info)
    -	{
    -		if(j == "properties")
    -		{
    -			//i dont want to clone properties, I want to reuse the old container
    -			for(var k in info.properties)
    -			{
    -				this.properties[k] = info.properties[k];
    -				if(this.onPropertyChanged)
    -					this.onPropertyChanged(k,info.properties[k]);
    -			}
    -			continue;
    -		}
    -
    -		if(info[j] == null)
    -			continue;
    -
    -		else if (typeof(info[j]) == 'object') //object
    -		{
    -			if(this[j] && this[j].configure)
    -				this[j].configure( info[j] );
    -			else
    -				this[j] = LiteGraph.cloneObject(info[j], this[j]);
    -		}
    -		else //value
    -			this[j] = info[j];
    -	}
    -
    -	if(!info.title)
    -		this.title = this.constructor.title;
    -
    -	if(this.onConnectionsChange)
    -	{
    -		if(this.inputs)
    -		for(var i = 0; i < this.inputs.length; ++i)
    -		{
    -			var input = this.inputs[i];
    -			var link_info = this.graph ? this.graph.links[ input.link ] : null;
    -			this.onConnectionsChange( LiteGraph.INPUT, i, true, link_info, input ); //link_info has been created now, so its updated
    -		}
    -
    -		if(this.outputs)
    -		for(var i = 0; i < this.outputs.length; ++i)
    -		{
    -			var output = this.outputs[i];
    -			if(!output.links)
    -				continue;
    -			for(var j = 0; j < output.links.length; ++j)
    -			{
    -				var link_info = this.graph ? this.graph.links[ output.links[j] ] : null;
    -				this.onConnectionsChange( LiteGraph.OUTPUT, i, true, link_info, output ); //link_info has been created now, so its updated
    -			}
    -		}
    -	}
    -
    -	if( this.onConfigure )
    -		this.onConfigure( info );
    -}
    -
    -/**
    -* serialize the content
    -* @method serialize
    -*/
    -
    -LGraphNode.prototype.serialize = function()
    -{
    -	//create serialization object
    -	var o = {
    -		id: this.id,
    -		type: this.type,
    -		pos: this.pos,
    -		size: this.size,
    -		flags: LiteGraph.cloneObject(this.flags),
    -		mode: this.mode
    -	};
    -
    -	//special case for when there were errors
    -	if( this.constructor === LGraphNode && this.last_serialization )
    -		return this.last_serialization;
    -
    -	if( this.inputs )
    -		o.inputs = this.inputs;
    -
    -	if( this.outputs )
    -	{
    -		//clear outputs last data (because data in connections is never serialized but stored inside the outputs info)
    -		for(var i = 0; i < this.outputs.length; i++)
    -			delete this.outputs[i]._data;
    -		o.outputs = this.outputs;
    -	}
    -
    -	if( this.title && this.title != this.constructor.title )
    -		o.title = this.title;
    -
    -	if( this.properties )
    -		o.properties = LiteGraph.cloneObject( this.properties );
    -
    -	if( !o.type )
    -		o.type = this.constructor.type;
    -
    -	if( this.color )
    -		o.color = this.color;
    -	if( this.bgcolor )
    -		o.bgcolor = this.bgcolor;
    -	if( this.boxcolor )
    -		o.boxcolor = this.boxcolor;
    -	if( this.shape )
    -		o.shape = this.shape;
    -
    -	if(this.onSerialize)
    -	{
    -		if( this.onSerialize(o) )
    -			console.warn("node onSerialize shouldnt return anything, data should be stored in the object pass in the first parameter");
    -	}
    -
    -	return o;
    -}
    -
    -
    -/* Creates a clone of this node */
    -LGraphNode.prototype.clone = function()
    -{
    -	var node = LiteGraph.createNode(this.type);
    -	if(!node)
    -		return null;
    -
    -	//we clone it because serialize returns shared containers
    -	var data = LiteGraph.cloneObject( this.serialize() );
    -
    -	//remove links
    -	if(data.inputs)
    -		for(var i = 0; i < data.inputs.length; ++i)
    -			data.inputs[i].link = null;
    -
    -	if(data.outputs)
    -		for(var i = 0; i < data.outputs.length; ++i)
    -		{
    -			if(data.outputs[i].links)
    -				data.outputs[i].links.length = 0;
    -		}
    -
    -	delete data["id"];
    -	//remove links
    -	node.configure(data);
    -
    -	return node;
    -}
    -
    -
    -/**
    -* serialize and stringify
    -* @method toString
    -*/
    -
    -LGraphNode.prototype.toString = function()
    -{
    -	return JSON.stringify( this.serialize() );
    -}
    -//LGraphNode.prototype.unserialize = function(info) {} //this cannot be done from within, must be done in LiteGraph
    -
    -
    -/**
    -* get the title string
    -* @method getTitle
    -*/
    -
    -LGraphNode.prototype.getTitle = function()
    -{
    -	return this.title || this.constructor.title;
    -}
    -
    -
    -
    -// Execution *************************
    -/**
    -* sets the output data
    -* @method setOutputData
    -* @param {number} slot
    -* @param {*} data
    -*/
    -LGraphNode.prototype.setOutputData = function(slot, data)
    -{
    -	if(!this.outputs)
    -		return;
    -
    -	//this maybe slow and a niche case
    -	//if(slot && slot.constructor === String)
    -	//	slot = this.findOutputSlot(slot);
    -
    -	if(slot == -1 || slot >= this.outputs.length)
    -		return;
    -
    -	var output_info = this.outputs[slot];
    -	if(!output_info)
    -		return;
    -
    -	//store data in the output itself in case we want to debug
    -	output_info._data = data;
    -
    -	//if there are connections, pass the data to the connections
    -	if( this.outputs[slot].links )
    -	{
    -		for(var i = 0; i < this.outputs[slot].links.length; i++)
    -		{
    -			var link_id = this.outputs[slot].links[i];
    -			this.graph.links[ link_id ].data = data;
    -		}
    -	}
    -}
    -
    -/**
    -* sets the output data type, useful when you want to be able to overwrite the data type
    -* @method setOutputDataType
    -* @param {number} slot
    -* @param {String} datatype
    -*/
    -LGraphNode.prototype.setOutputDataType = function(slot, type)
    -{
    -	if(!this.outputs)
    -		return;
    -	if(slot == -1 || slot >= this.outputs.length)
    -		return;
    -	var output_info = this.outputs[slot];
    -	if(!output_info)
    -		return;
    -	//store data in the output itself in case we want to debug
    -	output_info.type = type;
    -
    -	//if there are connections, pass the data to the connections
    -	if( this.outputs[slot].links )
    -	{
    -		for(var i = 0; i < this.outputs[slot].links.length; i++)
    -		{
    -			var link_id = this.outputs[slot].links[i];
    -			this.graph.links[ link_id ].type = type;
    -		}
    -	}
    -}
    -
    -/**
    -* Retrieves the input data (data traveling through the connection) from one slot
    -* @method getInputData
    -* @param {number} slot
    -* @param {boolean} force_update if set to true it will force the connected node of this slot to output data into this link
    -* @return {*} data or if it is not connected returns undefined
    -*/
    -LGraphNode.prototype.getInputData = function( slot, force_update )
    -{
    -	if(!this.inputs)
    -		return; //undefined;
    -
    -	if(slot >= this.inputs.length || this.inputs[slot].link == null)
    -		return;
    -
    -	var link_id = this.inputs[slot].link;
    -	var link = this.graph.links[ link_id ];
    -	if(!link) //bug: weird case but it happens sometimes
    -		return null;
    -
    -	if(!force_update)
    -		return link.data;
    -
    -	//special case: used to extract data from the incomming connection before the graph has been executed
    -	var node = this.graph.getNodeById( link.origin_id );
    -	if(!node)
    -		return link.data;
    -
    -	if(node.updateOutputData)
    -		node.updateOutputData( link.origin_slot );
    -	else if(node.onExecute)
    -		node.onExecute();
    -
    -	return link.data;
    -}
    -
    -/**
    -* Retrieves the input data type (in case this supports multiple input types)
    -* @method getInputDataType
    -* @param {number} slot
    -* @return {String} datatype in string format
    -*/
    -LGraphNode.prototype.getInputDataType = function( slot )
    -{
    -	if(!this.inputs)
    -		return null; //undefined;
    -
    -	if(slot >= this.inputs.length || this.inputs[slot].link == null)
    -		return null;
    -	var link_id = this.inputs[slot].link;
    -	var link = this.graph.links[ link_id ];
    -	if(!link) //bug: weird case but it happens sometimes
    -		return null;
    -	var node = this.graph.getNodeById( link.origin_id );
    -	if(!node)
    -		return link.type;
    -	var output_info = node.outputs[ link.origin_slot ];
    -	if(output_info)
    -		return output_info.type;
    -	return null;
    -}
    -
    -/**
    -* Retrieves the input data from one slot using its name instead of slot number
    -* @method getInputDataByName
    -* @param {String} slot_name
    -* @param {boolean} force_update if set to true it will force the connected node of this slot to output data into this link
    -* @return {*} data or if it is not connected returns null
    -*/
    -LGraphNode.prototype.getInputDataByName = function( slot_name, force_update )
    -{
    -	var slot = this.findInputSlot( slot_name );
    -	if( slot == -1 )
    -		return null;
    -	return this.getInputData( slot, force_update );
    -}
    -
    -
    -/**
    -* tells you if there is a connection in one input slot
    -* @method isInputConnected
    -* @param {number} slot
    -* @return {boolean}
    -*/
    -LGraphNode.prototype.isInputConnected = function(slot)
    -{
    -	if(!this.inputs)
    -		return false;
    -	return (slot < this.inputs.length && this.inputs[slot].link != null);
    -}
    -
    -/**
    -* tells you info about an input connection (which node, type, etc)
    -* @method getInputInfo
    -* @param {number} slot
    -* @return {Object} object or null { link: id, name: string, type: string or 0 }
    -*/
    -LGraphNode.prototype.getInputInfo = function(slot)
    -{
    -	if(!this.inputs)
    -		return null;
    -	if(slot < this.inputs.length)
    -		return this.inputs[slot];
    -	return null;
    -}
    -
    -/**
    -* returns the node connected in the input slot
    -* @method getInputNode
    -* @param {number} slot
    -* @return {LGraphNode} node or null
    -*/
    -LGraphNode.prototype.getInputNode = function( slot )
    -{
    -	if(!this.inputs)
    -		return null;
    -	if(slot >= this.inputs.length)
    -		return null;
    -	var input = this.inputs[slot];
    -	if(!input || input.link === null)
    -		return null;
    -	var link_info = this.graph.links[ input.link ];
    -	if(!link_info)
    -		return null;
    -	return this.graph.getNodeById( link_info.origin_id );
    -}
    -
    -
    -/**
    -* returns the value of an input with this name, otherwise checks if there is a property with that name
    -* @method getInputOrProperty
    -* @param {string} name
    -* @return {*} value
    -*/
    -LGraphNode.prototype.getInputOrProperty = function( name )
    -{
    -	if(!this.inputs || !this.inputs.length)
    -		return this.properties ? this.properties[name] : null;
    -
    -	for(var i = 0, l = this.inputs.length; i < l; ++i)
    -	{
    -		var input_info = this.inputs[i];
    -		if(name == input_info.name && input_info.link != null)
    -		{
    -			var link = this.graph.links[ input_info.link ];
    -			if(link)
    -				return link.data;
    -		}
    -	}
    -	return this.properties[ name ];
    -}
    -
    -
    -
    -
    -/**
    -* tells you the last output data that went in that slot
    -* @method getOutputData
    -* @param {number} slot
    -* @return {Object}  object or null
    -*/
    -LGraphNode.prototype.getOutputData = function(slot)
    -{
    -	if(!this.outputs)
    -		return null;
    -	if(slot >= this.outputs.length)
    -		return null;
    -
    -	var info = this.outputs[slot];
    -	return info._data;
    -}
    -
    -
    -/**
    -* tells you info about an output connection (which node, type, etc)
    -* @method getOutputInfo
    -* @param {number} slot
    -* @return {Object}  object or null { name: string, type: string, links: [ ids of links in number ] }
    -*/
    -LGraphNode.prototype.getOutputInfo = function(slot)
    -{
    -	if(!this.outputs)
    -		return null;
    -	if(slot < this.outputs.length)
    -		return this.outputs[slot];
    -	return null;
    -}
    -
    -
    -/**
    -* tells you if there is a connection in one output slot
    -* @method isOutputConnected
    -* @param {number} slot
    -* @return {boolean}
    -*/
    -LGraphNode.prototype.isOutputConnected = function(slot)
    -{
    -	if(!this.outputs)
    -		return false;
    -	return (slot < this.outputs.length && this.outputs[slot].links && this.outputs[slot].links.length);
    -}
    -
    -/**
    -* tells you if there is any connection in the output slots
    -* @method isAnyOutputConnected
    -* @return {boolean}
    -*/
    -LGraphNode.prototype.isAnyOutputConnected = function()
    -{
    -	if(!this.outputs)
    -		return false;
    -	for(var i = 0; i < this.outputs.length; ++i)
    -		if( this.outputs[i].links && this.outputs[i].links.length )
    -			return true;
    -	return false;
    -}
    -
    -
    -/**
    -* retrieves all the nodes connected to this output slot
    -* @method getOutputNodes
    -* @param {number} slot
    -* @return {array}
    -*/
    -LGraphNode.prototype.getOutputNodes = function(slot)
    -{
    -	if(!this.outputs || this.outputs.length == 0)
    -		return null;
    -
    -	if(slot >= this.outputs.length)
    -		return null;
    -
    -	var output = this.outputs[slot];
    -	if(!output.links || output.links.length == 0)
    -		return null;
    -
    -	var r = [];
    -	for(var i = 0; i < output.links.length; i++)
    -	{
    -		var link_id = output.links[i];
    -		var link = this.graph.links[ link_id ];
    -		if(link)
    -		{
    -			var target_node = this.graph.getNodeById( link.target_id );
    -			if( target_node )
    -				r.push( target_node );
    -		}
    -	}
    -	return r;
    -}
    -
    -/**
    -* Triggers an event in this node, this will trigger any output with the same name
    -* @method trigger
    -* @param {String} event name ( "on_play", ... ) if action is equivalent to false then the event is send to all
    -* @param {*} param
    -*/
    -LGraphNode.prototype.trigger = function( action, param )
    -{
    -	if( !this.outputs || !this.outputs.length )
    -		return;
    -
    -	if(this.graph)
    -		this.graph._last_trigger_time = LiteGraph.getTime();
    -
    -	for(var i = 0; i < this.outputs.length; ++i)
    -	{
    -		var output = this.outputs[ i ];
    -		if(!output || output.type !== LiteGraph.EVENT || (action && output.name != action) )
    -			continue;
    -		this.triggerSlot( i, param );
    -	}
    -}
    -
    -/**
    -* Triggers an slot event in this node
    -* @method triggerSlot
    -* @param {Number} slot the index of the output slot
    -* @param {*} param
    -* @param {Number} link_id [optional] in case you want to trigger and specific output link in a slot
    -*/
    -LGraphNode.prototype.triggerSlot = function( slot, param, link_id )
    -{
    -	if( !this.outputs )
    -		return;
    -
    -	var output = this.outputs[ slot ];
    -	if( !output )
    -		return;
    -
    -	var links = output.links;
    -	if(!links || !links.length)
    -		return;
    -
    -	if(this.graph)
    -		this.graph._last_trigger_time = LiteGraph.getTime();
    -
    -	//for every link attached here
    -	for(var k = 0; k < links.length; ++k)
    -	{
    -		var id = links[k];
    -		if( link_id != null && link_id != id ) //to skip links
    -			continue;
    -		var link_info = this.graph.links[ links[k] ];
    -		if(!link_info) //not connected
    -			continue;
    -		link_info._last_time = LiteGraph.getTime();
    -		var node = this.graph.getNodeById( link_info.target_id );
    -		if(!node) //node not found?
    -			continue;
    -
    -		//used to mark events in graph
    -		var target_connection = node.inputs[ link_info.target_slot ];
    -
    -		if(node.onAction)
    -			node.onAction( target_connection.name, param );
    -		else if(node.mode === LiteGraph.ON_TRIGGER)
    -		{
    -			if(node.onExecute)
    -				node.onExecute(param);
    -		}
    -	}
    -}
    -
    -/**
    -* clears the trigger slot animation
    -* @method clearTriggeredSlot
    -* @param {Number} slot the index of the output slot
    -* @param {Number} link_id [optional] in case you want to trigger and specific output link in a slot
    -*/
    -LGraphNode.prototype.clearTriggeredSlot = function( slot, link_id )
    -{
    -	if( !this.outputs )
    -		return;
    -
    -	var output = this.outputs[ slot ];
    -	if( !output )
    -		return;
    -
    -	var links = output.links;
    -	if(!links || !links.length)
    -		return;
    -
    -	//for every link attached here
    -	for(var k = 0; k < links.length; ++k)
    -	{
    -		var id = links[k];
    -		if( link_id != null && link_id != id ) //to skip links
    -			continue;
    -		var link_info = this.graph.links[ links[k] ];
    -		if(!link_info) //not connected
    -			continue;
    -		link_info._last_time = 0;
    -	}
    -}
    -
    -/**
    -* add a new property to this node
    -* @method addProperty
    -* @param {string} name
    -* @param {*} default_value
    -* @param {string} type string defining the output type ("vec3","number",...)
    -* @param {Object} extra_info this can be used to have special properties of the property (like values, etc)
    -*/
    -LGraphNode.prototype.addProperty = function( name, default_value, type, extra_info )
    -{
    -	var o = { name: name, type: type, default_value: default_value };
    -	if(extra_info)
    -		for(var i in extra_info)
    -			o[i] = extra_info[i];
    -	if(!this.properties_info)
    -		this.properties_info = [];
    -	this.properties_info.push(o);
    -	if(!this.properties)
    -		this.properties = {};
    -	this.properties[ name ] = default_value;
    -	return o;
    -}
    -
    -
    -//connections
    -
    -/**
    -* add a new output slot to use in this node
    -* @method addOutput
    -* @param {string} name
    -* @param {string} type string defining the output type ("vec3","number",...)
    -* @param {Object} extra_info this can be used to have special properties of an output (label, special color, position, etc)
    -*/
    -LGraphNode.prototype.addOutput = function(name,type,extra_info)
    -{
    -	var o = { name: name, type: type, links: null };
    -	if(extra_info)
    -		for(var i in extra_info)
    -			o[i] = extra_info[i];
    -
    -	if(!this.outputs)
    -		this.outputs = [];
    -	this.outputs.push(o);
    -	if(this.onOutputAdded)
    -		this.onOutputAdded(o);
    -	this.size = this.computeSize();
    -	this.setDirtyCanvas(true,true);
    -	return o;
    -}
    -
    -/**
    -* add a new output slot to use in this node
    -* @method addOutputs
    -* @param {Array} array of triplets like [[name,type,extra_info],[...]]
    -*/
    -LGraphNode.prototype.addOutputs = function(array)
    -{
    -	for(var i = 0; i < array.length; ++i)
    -	{
    -		var info = array[i];
    -		var o = {name:info[0],type:info[1],link:null};
    -		if(array[2])
    -			for(var j in info[2])
    -				o[j] = info[2][j];
    -
    -		if(!this.outputs)
    -			this.outputs = [];
    -		this.outputs.push(o);
    -		if(this.onOutputAdded)
    -			this.onOutputAdded(o);
    -	}
    -
    -	this.size = this.computeSize();
    -	this.setDirtyCanvas(true,true);
    -}
    -
    -/**
    -* remove an existing output slot
    -* @method removeOutput
    -* @param {number} slot
    -*/
    -LGraphNode.prototype.removeOutput = function(slot)
    -{
    -	this.disconnectOutput(slot);
    -	this.outputs.splice(slot,1);
    -	for(var i = slot; i < this.outputs.length; ++i)
    -	{
    -		if( !this.outputs[i] || !this.outputs[i].links )
    -			continue;
    -		var links = this.outputs[i].links;
    -		for(var j = 0; j < links.length; ++j)
    -		{
    -			var link = this.graph.links[ links[j] ];
    -			if(!link)
    -				continue;
    -			link.origin_slot -= 1;
    -		}
    -	}
    -
    -	this.size = this.computeSize();
    -	if(this.onOutputRemoved)
    -		this.onOutputRemoved(slot);
    -	this.setDirtyCanvas(true,true);
    -}
    -
    -/**
    -* add a new input slot to use in this node
    -* @method addInput
    -* @param {string} name
    -* @param {string} type string defining the input type ("vec3","number",...), it its a generic one use 0
    -* @param {Object} extra_info this can be used to have special properties of an input (label, color, position, etc)
    -*/
    -LGraphNode.prototype.addInput = function(name,type,extra_info)
    -{
    -	type = type || 0;
    -	var o = {name:name,type:type,link:null};
    -	if(extra_info)
    -		for(var i in extra_info)
    -			o[i] = extra_info[i];
    -
    -	if(!this.inputs)
    -		this.inputs = [];
    -	this.inputs.push(o);
    -	this.size = this.computeSize();
    -	if(this.onInputAdded)
    -		this.onInputAdded(o);
    -	this.setDirtyCanvas(true,true);
    -	return o;
    -}
    -
    -/**
    -* add several new input slots in this node
    -* @method addInputs
    -* @param {Array} array of triplets like [[name,type,extra_info],[...]]
    -*/
    -LGraphNode.prototype.addInputs = function(array)
    -{
    -	for(var i = 0; i < array.length; ++i)
    -	{
    -		var info = array[i];
    -		var o = {name:info[0], type:info[1], link:null};
    -		if(array[2])
    -			for(var j in info[2])
    -				o[j] = info[2][j];
    -
    -		if(!this.inputs)
    -			this.inputs = [];
    -		this.inputs.push(o);
    -		if(this.onInputAdded)
    -			this.onInputAdded(o);
    -	}
    -
    -	this.size = this.computeSize();
    -	this.setDirtyCanvas(true,true);
    -}
    -
    -/**
    -* remove an existing input slot
    -* @method removeInput
    -* @param {number} slot
    -*/
    -LGraphNode.prototype.removeInput = function(slot)
    -{
    -	this.disconnectInput(slot);
    -	this.inputs.splice(slot,1);
    -	for(var i = slot; i < this.inputs.length; ++i)
    -	{
    -		if(!this.inputs[i])
    -			continue;
    -		var link = this.graph.links[ this.inputs[i].link ];
    -		if(!link)
    -			continue;
    -		link.target_slot -= 1;
    -	}
    -	this.size = this.computeSize();
    -	if(this.onInputRemoved)
    -		this.onInputRemoved(slot);
    -	this.setDirtyCanvas(true,true);
    -}
    -
    -/**
    -* add an special connection to this node (used for special kinds of graphs)
    -* @method addConnection
    -* @param {string} name
    -* @param {string} type string defining the input type ("vec3","number",...)
    -* @param {[x,y]} pos position of the connection inside the node
    -* @param {string} direction if is input or output
    -*/
    -LGraphNode.prototype.addConnection = function(name,type,pos,direction)
    -{
    -	var o = {
    -		name: name,
    -		type: type,
    -		pos: pos,
    -		direction: direction,
    -		links: null
    -	};
    -	this.connections.push( o );
    -	return o;
    -}
    -
    -/**
    -* computes the size of a node according to its inputs and output slots
    -* @method computeSize
    -* @param {number} minHeight
    -* @return {number} the total size
    -*/
    -LGraphNode.prototype.computeSize = function( minHeight, out )
    -{
    -	if( this.constructor.size )
    -		return this.constructor.size.concat();
    -
    -	var rows = Math.max( this.inputs ? this.inputs.length : 1, this.outputs ? this.outputs.length : 1);
    -	var size = out || new Float32Array([0,0]);
    -	rows = Math.max(rows, 1);
    -	var font_size = LiteGraph.NODE_TEXT_SIZE; //although it should be graphcanvas.inner_text_font size
    -	size[1] = (this.constructor.slot_start_y || 0) + rows * LiteGraph.NODE_SLOT_HEIGHT;
    -	if( this.widgets && this.widgets.length )
    -		size[1] += this.widgets.length * (LiteGraph.NODE_WIDGET_HEIGHT + 4) + 8;
    -
    -	var font_size = font_size;
    -	var title_width = compute_text_size( this.title );
    -	var input_width = 0;
    -	var output_width = 0;
    -
    -	if(this.inputs)
    -		for(var i = 0, l = this.inputs.length; i < l; ++i)
    -		{
    -			var input = this.inputs[i];
    -			var text = input.label || input.name || "";
    -			var text_width = compute_text_size( text );
    -			if(input_width < text_width)
    -				input_width = text_width;
    -		}
    -
    -	if(this.outputs)
    -		for(var i = 0, l = this.outputs.length; i < l; ++i)
    -		{
    -			var output = this.outputs[i];
    -			var text = output.label || output.name || "";
    -			var text_width = compute_text_size( text );
    -			if(output_width < text_width)
    -				output_width = text_width;
    -		}
    -
    -	size[0] = Math.max( input_width + output_width + 10, title_width );
    -	size[0] = Math.max( size[0], LiteGraph.NODE_WIDTH );
    -	if(this.widgets && this.widgets.length)
    -		size[0] = Math.max( size[0], LiteGraph.NODE_WIDTH * 1.5 );
    -
    -	if(this.onResize)
    -		this.onResize(size);
    -
    -	function compute_text_size( text )
    -	{
    -		if(!text)
    -			return 0;
    -		return font_size * text.length * 0.6;
    -	}
    -
    -	if(this.constructor.min_height && size[1] < this.constructor.min_height)
    -		size[1] = this.constructor.min_height;
    -
    -	size[1] += 6; //margin
    -
    -	return size;
    -}
    -
    -/**
    -* Allows to pass 
    -* 
    -* @method addWidget
    -* @return {Object} the created widget
    -*/
    -LGraphNode.prototype.addWidget = function( type, name, value, callback, options )
    -{
    -	if(!this.widgets)
    -		this.widgets = [];
    -	var w = {
    -		type: type.toLowerCase(),
    -		name: name,
    -		value: value,
    -		callback: callback,
    -		options: options || {}
    -	};
    -
    -	if(w.options.y !== undefined )
    -		w.y = w.options.y;
    -
    -	if( !callback )
    -		console.warn("LiteGraph addWidget('button',...) without a callback");
    -	if( type == "combo" && !w.options.values )
    -		throw("LiteGraph addWidget('combo',...) requires to pass values in options: { values:['red','blue'] }");
    -	this.widgets.push(w);
    -	return w;
    -}
    -
    -LGraphNode.prototype.addCustomWidget = function( custom_widget )
    -{
    -	if(!this.widgets)
    -		this.widgets = [];
    -	this.widgets.push(custom_widget);
    -	return custom_widget;
    -}
    -
    -
    -/**
    -* returns the bounding of the object, used for rendering purposes
    -* bounding is: [topleft_cornerx, topleft_cornery, width, height]
    -* @method getBounding
    -* @return {Float32Array[4]} the total size
    -*/
    -LGraphNode.prototype.getBounding = function( out )
    -{
    -	out = out || new Float32Array(4);
    -	out[0] = this.pos[0] - 4;
    -	out[1] = this.pos[1] - LiteGraph.NODE_TITLE_HEIGHT;
    -	out[2] = this.size[0] + 4;
    -	out[3] = this.size[1] + LiteGraph.NODE_TITLE_HEIGHT;
    -
    -	if( this.onBounding )
    -		this.onBounding( out );
    -	return out;
    -}
    -
    -/**
    -* checks if a point is inside the shape of a node
    -* @method isPointInside
    -* @param {number} x
    -* @param {number} y
    -* @return {boolean}
    -*/
    -LGraphNode.prototype.isPointInside = function( x, y, margin, skip_title )
    -{
    -	margin = margin || 0;
    -
    -	var margin_top = this.graph && this.graph.isLive() ? 0 : 20;
    -	if(skip_title)
    -		margin_top = 0;
    -	if(this.flags && this.flags.collapsed)
    -	{
    -		//if ( distance([x,y], [this.pos[0] + this.size[0]*0.5, this.pos[1] + this.size[1]*0.5]) < LiteGraph.NODE_COLLAPSED_RADIUS)
    -		if( isInsideRectangle( x, y, this.pos[0] - margin, this.pos[1] - LiteGraph.NODE_TITLE_HEIGHT - margin, (this._collapsed_width||LiteGraph.NODE_COLLAPSED_WIDTH) + 2 * margin, LiteGraph.NODE_TITLE_HEIGHT + 2 * margin ) )
    -			return true;
    -	}
    -	else if ( (this.pos[0] - 4 - margin) < x && (this.pos[0] + this.size[0] + 4 + margin) > x
    -		&& (this.pos[1] - margin_top - margin) < y && (this.pos[1] + this.size[1] + margin) > y)
    -		return true;
    -	return false;
    -}
    -
    -/**
    -* checks if a point is inside a node slot, and returns info about which slot
    -* @method getSlotInPosition
    -* @param {number} x
    -* @param {number} y
    -* @return {Object} if found the object contains { input|output: slot object, slot: number, link_pos: [x,y] }
    -*/
    -LGraphNode.prototype.getSlotInPosition = function( x, y )
    -{
    -	//search for inputs
    -	var link_pos = new Float32Array(2);
    -	if(this.inputs)
    -		for(var i = 0, l = this.inputs.length; i < l; ++i)
    -		{
    -			var input = this.inputs[i];
    -			this.getConnectionPos( true,i, link_pos );
    -			if( isInsideRectangle(x, y, link_pos[0] - 10, link_pos[1] - 5, 20,10) )
    -				return { input: input, slot: i, link_pos: link_pos, locked: input.locked };
    -		}
    -
    -	if(this.outputs)
    -		for(var i = 0, l = this.outputs.length; i < l; ++i)
    -		{
    -			var output = this.outputs[i];
    -			this.getConnectionPos(false,i,link_pos);
    -			if( isInsideRectangle(x, y, link_pos[0] - 10, link_pos[1] - 5, 20,10) )
    -				return { output: output, slot: i, link_pos: link_pos, locked: output.locked };
    -		}
    -
    -	return null;
    -}
    -
    -/**
    -* returns the input slot with a given name (used for dynamic slots), -1 if not found
    -* @method findInputSlot
    -* @param {string} name the name of the slot
    -* @return {number} the slot (-1 if not found)
    -*/
    -LGraphNode.prototype.findInputSlot = function(name)
    -{
    -	if(!this.inputs)
    -		return -1;
    -	for(var i = 0, l = this.inputs.length; i < l; ++i)
    -		if(name == this.inputs[i].name)
    -			return i;
    -	return -1;
    -}
    -
    -/**
    -* returns the output slot with a given name (used for dynamic slots), -1 if not found
    -* @method findOutputSlot
    -* @param {string} name the name of the slot
    -* @return {number} the slot (-1 if not found)
    -*/
    -LGraphNode.prototype.findOutputSlot = function(name)
    -{
    -	if(!this.outputs) return -1;
    -	for(var i = 0, l = this.outputs.length; i < l; ++i)
    -		if(name == this.outputs[i].name)
    -			return i;
    -	return -1;
    -}
    -
    -/**
    -* connect this node output to the input of another node
    -* @method connect
    -* @param {number_or_string} slot (could be the number of the slot or the string with the name of the slot)
    -* @param {LGraphNode} node the target node
    -* @param {number_or_string} target_slot the input slot of the target node (could be the number of the slot or the string with the name of the slot, or -1 to connect a trigger)
    -* @return {Object} the link_info is created, otherwise null
    -*/
    -LGraphNode.prototype.connect = function( slot, target_node, target_slot )
    -{
    -	target_slot = target_slot || 0;
    -
    -	if(!this.graph) //could be connected before adding it to a graph
    -	{
    -		console.log("Connect: Error, node doesnt belong to any graph. Nodes must be added first to a graph before connecting them."); //due to link ids being associated with graphs
    -		return null;
    -	}
    -
    -
    -	//seek for the output slot
    -	if( slot.constructor === String )
    -	{
    -		slot = this.findOutputSlot(slot);
    -		if(slot == -1)
    -		{
    -			if(LiteGraph.debug)
    -				console.log("Connect: Error, no slot of name " + slot);
    -			return null;
    -		}
    -	}
    -	else if(!this.outputs || slot >= this.outputs.length)
    -	{
    -		if(LiteGraph.debug)
    -			console.log("Connect: Error, slot number not found");
    -		return null;
    -	}
    -
    -	if(target_node && target_node.constructor === Number)
    -		target_node = this.graph.getNodeById( target_node );
    -	if(!target_node)
    -		throw("target node is null");
    -
    -	//avoid loopback
    -	if(target_node == this)
    -		return null;
    -
    -	//you can specify the slot by name
    -	if(target_slot.constructor === String)
    -	{
    -		target_slot = target_node.findInputSlot( target_slot );
    -		if(target_slot == -1)
    -		{
    -			if(LiteGraph.debug)
    -				console.log("Connect: Error, no slot of name " + target_slot);
    -			return null;
    -		}
    -	}
    -	else if( target_slot === LiteGraph.EVENT )
    -	{
    -		//search for first slot with event?
    -		/*
    -		//create input for trigger
    -		var input = target_node.addInput("onTrigger", LiteGraph.EVENT );
    -		target_slot = target_node.inputs.length - 1; //last one is the one created
    -		target_node.mode = LiteGraph.ON_TRIGGER;
    -		*/
    -		return null;
    -	}
    -	else if( !target_node.inputs || target_slot >= target_node.inputs.length )
    -	{
    -		if(LiteGraph.debug)
    -			console.log("Connect: Error, slot number not found");
    -		return null;
    -	}
    -
    -	//if there is something already plugged there, disconnect
    -	if(target_node.inputs[ target_slot ].link != null )
    -		target_node.disconnectInput( target_slot );
    -
    -	//why here??
    -	//this.setDirtyCanvas(false,true);
    -	//this.graph.connectionChange( this );
    -
    -	var output = this.outputs[slot];
    -
    -	//allows nodes to block connection
    -	if(target_node.onConnectInput)
    -		if( target_node.onConnectInput( target_slot, output.type, output ) === false)
    -			return null;
    -
    -	var input = target_node.inputs[target_slot];
    -	var link_info = null;
    -
    -	if( LiteGraph.isValidConnection( output.type, input.type ) )
    -	{
    -		link_info = new LLink( this.graph.last_link_id++, input.type, this.id, slot, target_node.id, target_slot );
    -
    -		//add to graph links list
    -		this.graph.links[ link_info.id ] = link_info;
    -
    -		//connect in output
    -		if( output.links == null )
    -			output.links = [];
    -		output.links.push( link_info.id );
    -		//connect in input
    -		target_node.inputs[target_slot].link = link_info.id;
    -		if(this.graph)
    -			this.graph._version++;
    -		if(this.onConnectionsChange)
    -			this.onConnectionsChange( LiteGraph.OUTPUT, slot, true, link_info, output ); //link_info has been created now, so its updated
    -		if(target_node.onConnectionsChange)
    -			target_node.onConnectionsChange( LiteGraph.INPUT, target_slot, true, link_info, input );
    -		if( this.graph && this.graph.onNodeConnectionChange )
    -		{
    -			this.graph.onNodeConnectionChange( LiteGraph.INPUT, target_node, target_slot, this, slot );
    -			this.graph.onNodeConnectionChange( LiteGraph.OUTPUT, this, slot, target_node, target_slot );
    -		}
    -	}
    -
    -	this.setDirtyCanvas(false,true);
    -	this.graph.connectionChange( this, link_info );
    -
    -	return link_info;
    -}
    -
    -/**
    -* disconnect one output to an specific node
    -* @method disconnectOutput
    -* @param {number_or_string} slot (could be the number of the slot or the string with the name of the slot)
    -* @param {LGraphNode} target_node the target node to which this slot is connected [Optional, if not target_node is specified all nodes will be disconnected]
    -* @return {boolean} if it was disconnected succesfully
    -*/
    -LGraphNode.prototype.disconnectOutput = function( slot, target_node )
    -{
    -	if( slot.constructor === String )
    -	{
    -		slot = this.findOutputSlot(slot);
    -		if(slot == -1)
    -		{
    -			if(LiteGraph.debug)
    -				console.log("Connect: Error, no slot of name " + slot);
    -			return false;
    -		}
    -	}
    -	else if(!this.outputs || slot >= this.outputs.length)
    -	{
    -		if(LiteGraph.debug)
    -			console.log("Connect: Error, slot number not found");
    -		return false;
    -	}
    -
    -	//get output slot
    -	var output = this.outputs[slot];
    -	if(!output || !output.links || output.links.length == 0)
    -		return false;
    -
    -	//one of the output links in this slot
    -	if(target_node)
    -	{
    -		if(target_node.constructor === Number)
    -			target_node = this.graph.getNodeById( target_node );
    -		if(!target_node)
    -			throw("Target Node not found");
    -
    -		for(var i = 0, l = output.links.length; i < l; i++)
    -		{
    -			var link_id = output.links[i];
    -			var link_info = this.graph.links[ link_id ];
    -
    -			//is the link we are searching for...
    -			if( link_info.target_id == target_node.id )
    -			{
    -				output.links.splice(i,1); //remove here
    -				var input = target_node.inputs[ link_info.target_slot ];
    -				input.link = null; //remove there
    -				delete this.graph.links[ link_id ]; //remove the link from the links pool
    -				if(this.graph)
    -					this.graph._version++;
    -				if(target_node.onConnectionsChange)
    -					target_node.onConnectionsChange( LiteGraph.INPUT, link_info.target_slot, false, link_info, input ); //link_info hasnt been modified so its ok
    -				if(this.onConnectionsChange)
    -					this.onConnectionsChange( LiteGraph.OUTPUT, slot, false, link_info, output );
    -				if( this.graph && this.graph.onNodeConnectionChange )
    -					this.graph.onNodeConnectionChange( LiteGraph.OUTPUT, this, slot );
    -				if( this.graph && this.graph.onNodeConnectionChange )
    -				{
    -					this.graph.onNodeConnectionChange( LiteGraph.OUTPUT, this, slot );
    -					this.graph.onNodeConnectionChange( LiteGraph.INPUT, target_node, link_info.target_slot );
    -				}
    -				break;
    -			}
    -		}
    -	}
    -	else //all the links in this output slot
    -	{
    -		for(var i = 0, l = output.links.length; i < l; i++)
    -		{
    -			var link_id = output.links[i];
    -			var link_info = this.graph.links[ link_id ];
    -			if(!link_info) //bug: it happens sometimes
    -				continue;
    -
    -			var target_node = this.graph.getNodeById( link_info.target_id );
    -			var input = null;
    -			if(this.graph)
    -				this.graph._version++;
    -			if(target_node)
    -			{
    -				input = target_node.inputs[ link_info.target_slot ];
    -				input.link = null; //remove other side link
    -				if(target_node.onConnectionsChange)
    -					target_node.onConnectionsChange( LiteGraph.INPUT, link_info.target_slot, false, link_info, input ); //link_info hasnt been modified so its ok
    -				if( this.graph && this.graph.onNodeConnectionChange )
    -					this.graph.onNodeConnectionChange( LiteGraph.INPUT, target_node, link_info.target_slot );
    -			}
    -			delete this.graph.links[ link_id ]; //remove the link from the links pool
    -			if(this.onConnectionsChange)
    -				this.onConnectionsChange( LiteGraph.OUTPUT, slot, false, link_info, output );
    -			if( this.graph && this.graph.onNodeConnectionChange )
    -			{
    -				this.graph.onNodeConnectionChange( LiteGraph.OUTPUT, this, slot );
    -				this.graph.onNodeConnectionChange( LiteGraph.INPUT, target_node, link_info.target_slot );
    -			}
    -		}
    -		output.links = null;
    -	}
    -
    -
    -	this.setDirtyCanvas(false,true);
    -	this.graph.connectionChange( this );
    -	return true;
    -}
    -
    -/**
    -* disconnect one input
    -* @method disconnectInput
    -* @param {number_or_string} slot (could be the number of the slot or the string with the name of the slot)
    -* @return {boolean} if it was disconnected succesfully
    -*/
    -LGraphNode.prototype.disconnectInput = function( slot )
    -{
    -	//seek for the output slot
    -	if( slot.constructor === String )
    -	{
    -		slot = this.findInputSlot(slot);
    -		if(slot == -1)
    -		{
    -			if(LiteGraph.debug)
    -				console.log("Connect: Error, no slot of name " + slot);
    -			return false;
    -		}
    -	}
    -	else if(!this.inputs || slot >= this.inputs.length)
    -	{
    -		if(LiteGraph.debug)
    -			console.log("Connect: Error, slot number not found");
    -		return false;
    -	}
    -
    -	var input = this.inputs[slot];
    -	if(!input)
    -		return false;
    -
    -	var link_id = this.inputs[slot].link;
    -	this.inputs[slot].link = null;
    -
    -	//remove other side
    -	var link_info = this.graph.links[ link_id ];
    -	if( link_info )
    -	{
    -		var target_node = this.graph.getNodeById( link_info.origin_id );
    -		if(!target_node)
    -			return false;
    -
    -		var output = target_node.outputs[ link_info.origin_slot ];
    -		if(!output || !output.links || output.links.length == 0)
    -			return false;
    -
    -		//search in the inputs list for this link
    -		for(var i = 0, l = output.links.length; i < l; i++)
    -		{
    -			if( output.links[i] == link_id )
    -			{
    -				output.links.splice(i,1);
    -				break;
    -			}
    -		}
    -
    -		delete this.graph.links[ link_id ]; //remove from the pool
    -		if(this.graph)
    -			this.graph._version++;
    -		if( this.onConnectionsChange )
    -			this.onConnectionsChange( LiteGraph.INPUT, slot, false, link_info, input );
    -		if( target_node.onConnectionsChange )
    -			target_node.onConnectionsChange( LiteGraph.OUTPUT, i, false, link_info, output );
    -		if( this.graph && this.graph.onNodeConnectionChange )
    -		{
    -			this.graph.onNodeConnectionChange( LiteGraph.OUTPUT, target_node, i );
    -			this.graph.onNodeConnectionChange( LiteGraph.INPUT, this, slot );
    -		}
    -	}
    -
    -	this.setDirtyCanvas(false,true);
    -	this.graph.connectionChange( this );
    -	return true;
    -}
    -
    -/**
    -* returns the center of a connection point in canvas coords
    -* @method getConnectionPos
    -* @param {boolean} is_input true if if a input slot, false if it is an output
    -* @param {number_or_string} slot (could be the number of the slot or the string with the name of the slot)
    -* @param {vec2} out [optional] a place to store the output, to free garbage
    -* @return {[x,y]} the position
    -**/
    -LGraphNode.prototype.getConnectionPos = function( is_input, slot_number, out )
    -{
    -	out = out || new Float32Array(2);
    -	var num_slots = 0;
    -	if( is_input && this.inputs )
    -		num_slots = this.inputs.length;
    -	if( !is_input && this.outputs )
    -		num_slots = this.outputs.length;
    -
    -	var offset = LiteGraph.NODE_SLOT_HEIGHT * 0.5;
    -
    -	if(this.flags.collapsed)
    -	{
    -		var w = (this._collapsed_width || LiteGraph.NODE_COLLAPSED_WIDTH);
    -		if( this.horizontal )
    -		{
    -			out[0] = this.pos[0] + w * 0.5; 
    -			if(is_input)
    -				out[1] = this.pos[1] - LiteGraph.NODE_TITLE_HEIGHT;
    -			else
    -				out[1] = this.pos[1];
    -		}
    -		else
    -		{
    -			if(is_input)
    -				out[0] = this.pos[0];
    -			else
    -				out[0] = this.pos[0] + w;
    -			out[1] = this.pos[1] - LiteGraph.NODE_TITLE_HEIGHT * 0.5;
    -		}
    -		return out;
    -	}
    -
    -	//weird feature that never got finished
    -	if(is_input && slot_number == -1)
    -	{
    -		out[0] = this.pos[0] + LiteGraph.NODE_TITLE_HEIGHT * 0.5;
    -		out[1] = this.pos[1] + LiteGraph.NODE_TITLE_HEIGHT * 0.5;
    -		return out;
    -	}
    -
    -	//hardcoded pos
    -	if(is_input && num_slots > slot_number && this.inputs[ slot_number ].pos)
    -	{
    -		out[0] = this.pos[0] + this.inputs[slot_number].pos[0];
    -		out[1] = this.pos[1] + this.inputs[slot_number].pos[1];
    -		return out;
    -	}
    -	else if(!is_input && num_slots > slot_number && this.outputs[ slot_number ].pos)
    -	{
    -		out[0] = this.pos[0] + this.outputs[slot_number].pos[0];
    -		out[1] = this.pos[1] + this.outputs[slot_number].pos[1];
    -		return out;
    -	}
    -
    -	//horizontal distributed slots
    -	if(this.horizontal)
    -	{
    -		out[0] = this.pos[0] + (slot_number + 0.5) * (this.size[0] / num_slots);
    -		if(is_input)
    -			out[1] = this.pos[1] - LiteGraph.NODE_TITLE_HEIGHT;
    -		else
    -			out[1] = this.pos[1] + this.size[1];
    -		return out;
    -	}
    -	
    -	//default vertical slots
    -	if(is_input)
    -		out[0] = this.pos[0] + offset;
    -	else
    -		out[0] = this.pos[0] + this.size[0] + 1 - offset;
    -	out[1] = this.pos[1] + (slot_number + 0.7 ) * LiteGraph.NODE_SLOT_HEIGHT + (this.constructor.slot_start_y || 0);
    -	return out;
    -}
    -
    -/* Force align to grid */
    -LGraphNode.prototype.alignToGrid = function()
    -{
    -	this.pos[0] = LiteGraph.CANVAS_GRID_SIZE * Math.round(this.pos[0] / LiteGraph.CANVAS_GRID_SIZE);
    -	this.pos[1] = LiteGraph.CANVAS_GRID_SIZE * Math.round(this.pos[1] / LiteGraph.CANVAS_GRID_SIZE);
    -}
    -
    -
    -/* Console output */
    -LGraphNode.prototype.trace = function(msg)
    -{
    -	if(!this.console)
    -		this.console = [];
    -	this.console.push(msg);
    -	if(this.console.length > LGraphNode.MAX_CONSOLE)
    -		this.console.shift();
    -
    -	this.graph.onNodeTrace(this,msg);
    -}
    -
    -/* Forces to redraw or the main canvas (LGraphNode) or the bg canvas (links) */
    -LGraphNode.prototype.setDirtyCanvas = function(dirty_foreground, dirty_background)
    -{
    -	if(!this.graph)
    -		return;
    -	this.graph.sendActionToCanvas("setDirty",[dirty_foreground, dirty_background]);
    -}
    -
    -LGraphNode.prototype.loadImage = function(url)
    -{
    -	var img = new Image();
    -	img.src = LiteGraph.node_images_path + url;
    -	img.ready = false;
    -
    -	var that = this;
    -	img.onload = function() {
    -		this.ready = true;
    -		that.setDirtyCanvas(true);
    -	}
    -	return img;
    -}
    -
    -//safe LGraphNode action execution (not sure if safe)
    -/*
    -LGraphNode.prototype.executeAction = function(action)
    -{
    -	if(action == "") return false;
    -
    -	if( action.indexOf(";") != -1 || action.indexOf("}") != -1)
    -	{
    -		this.trace("Error: Action contains unsafe characters");
    -		return false;
    -	}
    -
    -	var tokens = action.split("(");
    -	var func_name = tokens[0];
    -	if( typeof(this[func_name]) != "function")
    -	{
    -		this.trace("Error: Action not found on node: " + func_name);
    -		return false;
    -	}
    -
    -	var code = action;
    -
    -	try
    -	{
    -		var _foo = eval;
    -		eval = null;
    -		(new Function("with(this) { " + code + "}")).call(this);
    -		eval = _foo;
    -	}
    -	catch (err)
    -	{
    -		this.trace("Error executing action {" + action + "} :" + err);
    -		return false;
    -	}
    -
    -	return true;
    -}
    -*/
    -
    -/* Allows to get onMouseMove and onMouseUp events even if the mouse is out of focus */
    -LGraphNode.prototype.captureInput = function(v)
    -{
    -	if(!this.graph || !this.graph.list_of_graphcanvas)
    -		return;
    -
    -	var list = this.graph.list_of_graphcanvas;
    -
    -	for(var i = 0; i < list.length; ++i)
    -	{
    -		var c = list[i];
    -		//releasing somebody elses capture?!
    -		if(!v && c.node_capturing_input != this)
    -			continue;
    -
    -		//change
    -		c.node_capturing_input = v ? this : null;
    -	}
    -}
    -
    -/**
    -* Collapse the node to make it smaller on the canvas
    -* @method collapse
    -**/
    -LGraphNode.prototype.collapse = function( force )
    -{
    -	this.graph._version++;
    -	if(this.constructor.collapsable === false && !force)
    -		return;
    -	if(!this.flags.collapsed)
    -		this.flags.collapsed = true;
    -	else
    -		this.flags.collapsed = false;
    -	this.setDirtyCanvas(true,true);
    -}
    -
    -/**
    -* Forces the node to do not move or realign on Z
    -* @method pin
    -**/
    -
    -LGraphNode.prototype.pin = function(v)
    -{
    -	this.graph._version++;
    -	if(v === undefined)
    -		this.flags.pinned = !this.flags.pinned;
    -	else
    -		this.flags.pinned = v;
    -}
    -
    -LGraphNode.prototype.localToScreen = function(x,y, graphcanvas)
    -{
    -	return [(x + this.pos[0]) * graphcanvas.scale + graphcanvas.offset[0],
    -		(y + this.pos[1]) * graphcanvas.scale + graphcanvas.offset[1]];
    -}
    -
    -
    -
    -
    -function LGraphGroup( title )
    -{
    -	this._ctor( title );
    -}
    -
    -global.LGraphGroup = LiteGraph.LGraphGroup = LGraphGroup;
    -
    -LGraphGroup.prototype._ctor = function( title )
    -{
    -	this.title = title || "Group";
    -	this.font_size = 24;
    -	this.color = LGraphCanvas.node_colors.pale_blue ? LGraphCanvas.node_colors.pale_blue.groupcolor : "#AAA";
    -	this._bounding = new Float32Array([10,10,140,80]);
    -	this._pos = this._bounding.subarray(0,2);
    -	this._size = this._bounding.subarray(2,4);
    -	this._nodes = [];
    -	this.graph = null;
    -
    -	Object.defineProperty( this, "pos", {
    -		set: function(v)
    -		{
    -			if(!v || v.length < 2)
    -				return;
    -			this._pos[0] = v[0];
    -			this._pos[1] = v[1];
    -		},
    -		get: function()
    -		{
    -			return this._pos;
    -		},
    -		enumerable: true
    -	});
    -
    -	Object.defineProperty( this, "size", {
    -		set: function(v)
    -		{
    -			if(!v || v.length < 2)
    -				return;
    -			this._size[0] = Math.max(140,v[0]);
    -			this._size[1] = Math.max(80,v[1]);
    -		},
    -		get: function()
    -		{
    -			return this._size;
    -		},
    -		enumerable: true
    -	});
    -}
    -
    -LGraphGroup.prototype.configure = function(o)
    -{
    -	this.title = o.title;
    -	this._bounding.set( o.bounding );
    -	this.color = o.color;
    -	this.font = o.font;
    -}
    -
    -LGraphGroup.prototype.serialize = function()
    -{
    -	var b = this._bounding;
    -	return {
    -		title: this.title,
    -		bounding: [ Math.round(b[0]), Math.round(b[1]), Math.round(b[2]), Math.round(b[3]) ],
    -		color: this.color,
    -		font: this.font
    -	};
    -}
    -
    -LGraphGroup.prototype.move = function(deltax, deltay, ignore_nodes)
    -{
    -	this._pos[0] += deltax;
    -	this._pos[1] += deltay;
    -	if(ignore_nodes)
    -		return;
    -	for(var i = 0; i < this._nodes.length; ++i)
    -	{
    -		var node = this._nodes[i];
    -		node.pos[0] += deltax;
    -		node.pos[1] += deltay;
    -	}
    -}
    -
    -LGraphGroup.prototype.recomputeInsideNodes = function()
    -{
    -	this._nodes.length = 0;
    -	var nodes = this.graph._nodes;
    -	var node_bounding = new Float32Array(4);
    -
    -	for(var i = 0; i < nodes.length; ++i)
    -	{
    -		var node = nodes[i];
    -		node.getBounding( node_bounding );
    -		if(!overlapBounding( this._bounding, node_bounding ))
    -			continue; //out of the visible area
    -		this._nodes.push( node );
    -	}
    -}
    -
    -LGraphGroup.prototype.isPointInside = LGraphNode.prototype.isPointInside;
    -LGraphGroup.prototype.setDirtyCanvas = LGraphNode.prototype.setDirtyCanvas;
    -
    -
    -
    -//****************************************
    -
    -//Scale and Offset
    -function DragAndScale( element, skip_events )
    -{
    -	this.offset = new Float32Array([0,0]);
    -	this.scale = 1;
    -	this.max_scale = 10;
    -	this.min_scale = 0.1;
    -	this.onredraw = null;
    -	this.enabled = true;
    -	this.last_mouse = [0,0];
    -	this.element = null;
    -	this.visible_area = new Float32Array(4);
    -
    -	if(element)
    -	{
    -		this.element = element;
    -		if(!skip_events)
    -			this.bindEvents( element );
    -	}
    -}
    -
    -LiteGraph.DragAndScale = DragAndScale;
    -
    -DragAndScale.prototype.bindEvents = function( element )
    -{
    -	this.last_mouse = new Float32Array(2);
    -
    -	this._binded_mouse_callback = this.onMouse.bind(this);
    -
    -	element.addEventListener("mousedown", this._binded_mouse_callback );
    -	element.addEventListener("mousemove", this._binded_mouse_callback );
    -
    -	element.addEventListener("mousewheel", this._binded_mouse_callback, false);
    -	element.addEventListener("wheel", this._binded_mouse_callback, false);
    -}
    -
    -DragAndScale.prototype.computeVisibleArea = function()
    -{
    -	if(!this.element)
    -	{
    -		this.visible_area[0] = this.visible_area[1] = this.visible_area[2] = this.visible_area[3] = 0;
    -		return;
    -	}
    -	var width = this.element.width;
    -	var height = this.element.height;
    -	var startx = -this.offset[0];
    -	var starty = -this.offset[1];
    -	var endx = startx + width / this.scale;
    -	var endy = starty + height / this.scale;
    -	this.visible_area[0] = startx;
    -	this.visible_area[1] = starty;
    -	this.visible_area[2] = endx - startx;
    -	this.visible_area[3] = endy - starty;
    -}
    -
    -DragAndScale.prototype.onMouse = function(e)
    -{
    -	if(!this.enabled)
    -		return;
    -
    -	var canvas = this.element;
    -	var rect = canvas.getBoundingClientRect();
    -	var x = e.clientX - rect.left;
    -	var y = e.clientY - rect.top;
    -	e.canvasx = x;
    -	e.canvasy = y;
    -	e.dragging = this.dragging;
    -
    -	var ignore = false;
    -	if(this.onmouse)
    -		ignore = this.onmouse(e);
    -
    -	if(e.type == "mousedown")
    -	{
    -		this.dragging = true;
    -		canvas.removeEventListener("mousemove", this._binded_mouse_callback );
    -		document.body.addEventListener("mousemove", this._binded_mouse_callback  );
    -		document.body.addEventListener("mouseup", this._binded_mouse_callback );
    -	}
    -	else if(e.type == "mousemove")
    -	{
    -		if(!ignore)
    -		{
    -			var deltax = x - this.last_mouse[0];
    -			var deltay = y - this.last_mouse[1];
    -			if( this.dragging )
    -				this.mouseDrag( deltax, deltay );
    -		}
    -	}
    -	else if(e.type == "mouseup")
    -	{
    -		this.dragging = false;
    -		document.body.removeEventListener("mousemove", this._binded_mouse_callback );
    -		document.body.removeEventListener("mouseup", this._binded_mouse_callback );
    -		canvas.addEventListener("mousemove", this._binded_mouse_callback  );
    -	}
    -	else if(e.type == "mousewheel" || e.type == "wheel" || e.type == "DOMMouseScroll")
    -	{ 
    -		e.eventType = "mousewheel";
    -		if(e.type == "wheel")
    -			e.wheel = -e.deltaY;
    -		else
    -			e.wheel = (e.wheelDeltaY != null ? e.wheelDeltaY : e.detail * -60);
    -
    -		//from stack overflow
    -		e.delta = e.wheelDelta ? e.wheelDelta/40 : e.deltaY ? -e.deltaY/3 : 0;
    -		this.changeDeltaScale(1.0 + e.delta * 0.05);
    -	}
    -
    -	this.last_mouse[0] = x;
    -	this.last_mouse[1] = y;
    -
    -	e.preventDefault();
    -	e.stopPropagation();
    -	return false;
    -}
    -
    -DragAndScale.prototype.toCanvasContext = function( ctx )
    -{
    -	ctx.scale( this.scale, this.scale );
    -	ctx.translate( this.offset[0], this.offset[1] );
    -}
    -
    -DragAndScale.prototype.convertOffsetToCanvas = function(pos)
    -{
    -	//return [pos[0] / this.scale - this.offset[0], pos[1] / this.scale - this.offset[1]];
    -	return [ (pos[0] + this.offset[0]) * this.scale, (pos[1] + this.offset[1]) * this.scale ];
    -}
    -
    -DragAndScale.prototype.convertCanvasToOffset = function(pos, out)
    -{
    -	out = out || [0,0];
    -	out[0] = pos[0] / this.scale - this.offset[0];
    -	out[1] = pos[1] / this.scale - this.offset[1];
    -	return out;
    -}
    -
    -DragAndScale.prototype.mouseDrag = function(x,y)
    -{
    -	this.offset[0] += x / this.scale;
    -	this.offset[1] += y / this.scale;
    -
    -	if(	this.onredraw )
    -		this.onredraw( this );
    -}
    -
    -DragAndScale.prototype.changeScale = function( value, zooming_center )
    -{
    -	if(value < this.min_scale)
    -		value = this.min_scale;
    -	else if(value > this.max_scale)
    -		value = this.max_scale;
    -
    -	if(value == this.scale)
    -		return;
    -
    -	if(!this.element)
    -		return;
    -
    -	var rect = this.element.getBoundingClientRect();
    -	if(!rect)
    -		return;
    -
    -	zooming_center = zooming_center || [rect.width * 0.5,rect.height * 0.5];
    -	var center = this.convertCanvasToOffset( zooming_center );
    -	this.scale = value;
    -	if( Math.abs( this.scale - 1 ) < 0.01 )
    -		this.scale = 1;
    -
    -	var new_center = this.convertCanvasToOffset( zooming_center );
    -	var delta_offset = [new_center[0] - center[0], new_center[1] - center[1]];
    -
    -	this.offset[0] += delta_offset[0];
    -	this.offset[1] += delta_offset[1];
    -
    -	if(	this.onredraw )
    -		this.onredraw( this );
    -}
    -
    -DragAndScale.prototype.changeDeltaScale = function( value, zooming_center )
    -{
    -	this.changeScale( this.scale * value, zooming_center );
    -}
    -
    -DragAndScale.prototype.reset = function()
    -{
    -	this.scale = 1;
    -	this.offset[0] = 0;
    -	this.offset[1] = 0;
    -}
    -
    -
    -//*********************************************************************************
    -// LGraphCanvas: LGraph renderer CLASS
    -//*********************************************************************************
    -
    -/**
    -* This class is in charge of rendering one graph inside a canvas. And provides all the interaction required.
    -* Valid callbacks are: onNodeSelected, onNodeDeselected, onShowNodePanel, onNodeDblClicked
    -*
    -* @class LGraphCanvas
    -* @constructor
    -* @param {HTMLCanvas} canvas the canvas where you want to render (it accepts a selector in string format or the canvas element itself)
    -* @param {LGraph} graph [optional]
    -* @param {Object} options [optional] { skip_rendering, autoresize }
    -*/
    -function LGraphCanvas( canvas, graph, options )
    -{
    -	options = options || {};
    -
    -	//if(graph === undefined)
    -  //	throw ("No graph assigned");
    -	this.background_image = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAIAAAD/gAIDAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAQBJREFUeNrs1rEKwjAUhlETUkj3vP9rdmr1Ysammk2w5wdxuLgcMHyptfawuZX4pJSWZTnfnu/lnIe/jNNxHHGNn//HNbbv+4dr6V+11uF527arU7+u63qfa/bnmh8sWLBgwYJlqRf8MEptXPBXJXa37BSl3ixYsGDBMliwFLyCV/DeLIMFCxYsWLBMwSt4Be/NggXLYMGCBUvBK3iNruC9WbBgwYJlsGApeAWv4L1ZBgsWLFiwYJmCV/AK3psFC5bBggULloJX8BpdwXuzYMGCBctgwVLwCl7Be7MMFixYsGDBsu8FH1FaSmExVfAxBa/gvVmwYMGCZbBg/W4vAQYA5tRF9QYlv/QAAAAASUVORK5CYII='
    -
    -	if(canvas && canvas.constructor === String )
    -		canvas = document.querySelector( canvas );
    -
    -	this.ds = new DragAndScale();
    -	this.zoom_modify_alpha = true; //otherwise it generates ugly patterns when scaling down too much
    -
    -	this.title_text_font = ""+LiteGraph.NODE_TEXT_SIZE+"px Arial";
    -	this.inner_text_font = "normal "+LiteGraph.NODE_SUBTEXT_SIZE+"px Arial";
    -	this.node_title_color = LiteGraph.NODE_TITLE_COLOR;
    -	this.default_link_color = LiteGraph.LINK_COLOR;
    -	this.default_connection_color = {
    -		input_off: "#778",
    -		input_on: "#7F7",
    -		output_off: "#778",
    -		output_on: "#7F7"
    -	};
    -
    -	this.highquality_render = true;
    -	this.use_gradients = false; //set to true to render titlebar with gradients
    -	this.editor_alpha = 1; //used for transition
    -	this.pause_rendering = false;
    -	this.clear_background = true;
    -
    -	this.render_only_selected = true;
    -	this.live_mode = false;
    -	this.show_info = true;
    -	this.allow_dragcanvas = true;
    -	this.allow_dragnodes = true;
    -	this.allow_interaction = true; //allow to control widgets, buttons, collapse, etc
    -	this.allow_searchbox = true;
    -	this.allow_reconnect_links = false; //allows to change a connection with having to redo it again
    -
    -	this.drag_mode = false;
    -	this.dragging_rectangle = null;
    -
    -	this.filter = null; //allows to filter to only accept some type of nodes in a graph
    -
    -	this.always_render_background = false;
    -	this.render_shadows = true;
    -	this.render_canvas_border = true;
    -	this.render_connections_shadows = false; //too much cpu
    -	this.render_connections_border = true;
    -	this.render_curved_connections = false;
    -	this.render_connection_arrows = false;
    -	this.render_collapsed_slots = true;
    -	this.render_execution_order = false;
    -	this.render_title_colored = true;
    -
    -	this.links_render_mode = LiteGraph.SPLINE_LINK;
    -
    -	this.canvas_mouse = [0,0]; //mouse in canvas graph coordinates, where 0,0 is the top-left corner of the blue rectangle
    -
    -	//to personalize the search box
    -	this.onSearchBox = null;
    -	this.onSearchBoxSelection = null;
    -
    -	//callbacks
    -	this.onMouse = null;
    -	this.onDrawBackground = null; //to render background objects (behind nodes and connections) in the canvas affected by transform
    -	this.onDrawForeground = null; //to render foreground objects (above nodes and connections) in the canvas affected by transform
    -	this.onDrawOverlay = null; //to render foreground objects not affected by transform (for GUIs)
    -
    -	this.connections_width = 3;
    -	this.round_radius = 8;
    -
    -	this.current_node = null;
    -	this.node_widget = null; //used for widgets
    -	this.last_mouse_position = [0,0];
    -	this.visible_area = this.ds.visible_area;
    -	this.visible_links = [];
    -
    -	//link canvas and graph
    -	if(graph)
    -		graph.attachCanvas(this);
    -
    -	this.setCanvas( canvas );
    -	this.clear();
    -
    -	if(!options.skip_render)
    -		this.startRendering();
    -
    -	this.autoresize = options.autoresize;
    -}
    -
    -global.LGraphCanvas = LiteGraph.LGraphCanvas = LGraphCanvas;
    -
    -LGraphCanvas.link_type_colors = {"-1": LiteGraph.EVENT_LINK_COLOR,'number':"#AAA","node":"#DCA"};
    -LGraphCanvas.gradients = {}; //cache of gradients
    -
    -/**
    -* clears all the data inside
    -*
    -* @method clear
    -*/
    -LGraphCanvas.prototype.clear = function()
    -{
    -	this.frame = 0;
    -	this.last_draw_time = 0;
    -	this.render_time = 0;
    -	this.fps = 0;
    -
    -	//this.scale = 1;
    -	//this.offset = [0,0];
    -
    -	this.dragging_rectangle = null;
    -
    -	this.selected_nodes = {};
    -	this.selected_group = null;
    -
    -	this.visible_nodes = [];
    -	this.node_dragged = null;
    -	this.node_over = null;
    -	this.node_capturing_input = null;
    -	this.connecting_node = null;
    -	this.highlighted_links = {};
    -
    -	this.dirty_canvas = true;
    -	this.dirty_bgcanvas = true;
    -	this.dirty_area = null;
    -
    -	this.node_in_panel = null;
    -	this.node_widget = null;
    -
    -	this.last_mouse = [0,0];
    -	this.last_mouseclick = 0;
    -	this.visible_area.set([0,0,0,0]);
    -
    -	if(this.onClear)
    -		this.onClear();
    -	//this.UIinit();
    -}
    -
    -/**
    -* assigns a graph, you can reasign graphs to the same canvas
    -*
    -* @method setGraph
    -* @param {LGraph} graph
    -*/
    -LGraphCanvas.prototype.setGraph = function( graph, skip_clear )
    -{
    -	if(this.graph == graph)
    -		return;
    -
    -	if(!skip_clear)
    -		this.clear();
    -
    -	if(!graph && this.graph)
    -	{
    -		this.graph.detachCanvas(this);
    -		return;
    -	}
    -
    -	/*
    -	if(this.graph)
    -		this.graph.canvas = null; //remove old graph link to the canvas
    -	this.graph = graph;
    -	if(this.graph)
    -		this.graph.canvas = this;
    -	*/
    -	graph.attachCanvas(this);
    -	this.setDirty(true,true);
    -}
    -
    -/**
    -* opens a graph contained inside a node in the current graph
    -*
    -* @method openSubgraph
    -* @param {LGraph} graph
    -*/
    -LGraphCanvas.prototype.openSubgraph = function(graph)
    -{
    -	if(!graph)
    -		throw("graph cannot be null");
    -
    -	if(this.graph == graph)
    -		throw("graph cannot be the same");
    -
    -	this.clear();
    -
    -	if(this.graph)
    -	{
    -		if(!this._graph_stack)
    -			this._graph_stack = [];
    -		this._graph_stack.push(this.graph);
    -	}
    -
    -	graph.attachCanvas(this);
    -	this.setDirty(true,true);
    -}
    -
    -/**
    -* closes a subgraph contained inside a node
    -*
    -* @method closeSubgraph
    -* @param {LGraph} assigns a graph
    -*/
    -LGraphCanvas.prototype.closeSubgraph = function()
    -{
    -	if(!this._graph_stack || this._graph_stack.length == 0)
    -		return;
    -	var subraph_node = this.graph._subgraph_node;
    -	var graph = this._graph_stack.pop();
    -	this.selected_nodes = {};
    -	this.highlighted_links = {};
    -	graph.attachCanvas(this);
    -	this.setDirty(true,true);
    -	if( subraph_node )
    -	{
    -		this.centerOnNode( subraph_node );
    -		this.selectNodes( [subraph_node] );
    -	}
    -}
    -
    -/**
    -* assigns a canvas
    -*
    -* @method setCanvas
    -* @param {Canvas} assigns a canvas (also accepts the ID of the element (not a selector)
    -*/
    -LGraphCanvas.prototype.setCanvas = function( canvas, skip_events )
    -{
    -	var that = this;
    -
    -	if(canvas)
    -	{
    -		if( canvas.constructor === String )
    -		{
    -			canvas = document.getElementById(canvas);
    -			if(!canvas)
    -				throw("Error creating LiteGraph canvas: Canvas not found");
    -		}
    -	}
    -
    -	if(canvas === this.canvas)
    -		return;
    -
    -	if(!canvas && this.canvas)
    -	{
    -		//maybe detach events from old_canvas
    -		if(!skip_events)
    -			this.unbindEvents();
    -	}
    -
    -	this.canvas = canvas;
    -	this.ds.element = canvas;
    -
    -	if(!canvas)
    -		return;
    -
    -	//this.canvas.tabindex = "1000";
    -	canvas.className += " lgraphcanvas";
    -	canvas.data = this;
    -	canvas.tabindex = '1'; //to allow key events
    -
    -	//bg canvas: used for non changing stuff
    -	this.bgcanvas = null;
    -	if(!this.bgcanvas)
    -	{
    -		this.bgcanvas = document.createElement("canvas");
    -		this.bgcanvas.width = this.canvas.width;
    -		this.bgcanvas.height = this.canvas.height;
    -	}
    -
    -	if(canvas.getContext == null)
    -	{
    -		if( canvas.localName != "canvas" )
    -			throw("Element supplied for LGraphCanvas must be a <canvas> element, you passed a " + canvas.localName );
    -		throw("This browser doesnt support Canvas");
    -	}
    -
    -	var ctx = this.ctx = canvas.getContext("2d");
    -	if(ctx == null)
    -	{
    -		if(!canvas.webgl_enabled)
    -			console.warn("This canvas seems to be WebGL, enabling WebGL renderer");
    -		this.enableWebGL();
    -	}
    -
    -	//input:  (move and up could be unbinded)
    -	this._mousemove_callback = this.processMouseMove.bind(this);
    -	this._mouseup_callback = this.processMouseUp.bind(this);
    -
    -	if(!skip_events)
    -		this.bindEvents();
    -}
    -
    -//used in some events to capture them
    -LGraphCanvas.prototype._doNothing = function doNothing(e) { e.preventDefault(); return false; };
    -LGraphCanvas.prototype._doReturnTrue = function doNothing(e) { e.preventDefault(); return true; };
    -
    -/**
    -* binds mouse, keyboard, touch and drag events to the canvas
    -* @method bindEvents
    -**/
    -LGraphCanvas.prototype.bindEvents = function()
    -{
    -	if(	this._events_binded )
    -	{
    -		console.warn("LGraphCanvas: events already binded");
    -		return;
    -	}
    -
    -	var canvas = this.canvas;
    -	var ref_window = this.getCanvasWindow();
    -	var document = ref_window.document; //hack used when moving canvas between windows
    -
    -	this._mousedown_callback = this.processMouseDown.bind(this);
    -	this._mousewheel_callback = this.processMouseWheel.bind(this);
    -
    -	canvas.addEventListener("mousedown", this._mousedown_callback, true ); //down do not need to store the binded
    -	canvas.addEventListener("mousemove", this._mousemove_callback );
    -	canvas.addEventListener("mousewheel", this._mousewheel_callback, false);
    -
    -	canvas.addEventListener("contextmenu", this._doNothing );
    -	canvas.addEventListener("DOMMouseScroll", this._mousewheel_callback, false);
    -
    -	//touch events
    -	//if( 'touchstart' in document.documentElement )
    -	{
    -		canvas.addEventListener("touchstart", this.touchHandler, true);
    -		canvas.addEventListener("touchmove", this.touchHandler, true);
    -		canvas.addEventListener("touchend", this.touchHandler, true);
    -		canvas.addEventListener("touchcancel", this.touchHandler, true);
    -	}
    -
    -	//Keyboard ******************
    -	this._key_callback = this.processKey.bind(this);
    -
    -	canvas.addEventListener("keydown", this._key_callback, true );
    -	document.addEventListener("keyup", this._key_callback, true ); //in document, otherwise it doesnt fire keyup
    -
    -	//Droping Stuff over nodes ************************************
    -	this._ondrop_callback = this.processDrop.bind(this);
    -
    -	canvas.addEventListener("dragover", this._doNothing, false );
    -	canvas.addEventListener("dragend", this._doNothing, false );
    -	canvas.addEventListener("drop", this._ondrop_callback, false );
    -	canvas.addEventListener("dragenter", this._doReturnTrue, false );
    -
    -	this._events_binded = true;
    -}
    -
    -/**
    -* unbinds mouse events from the canvas
    -* @method unbindEvents
    -**/
    -LGraphCanvas.prototype.unbindEvents = function()
    -{
    -	if(	!this._events_binded )
    -	{
    -		console.warn("LGraphCanvas: no events binded");
    -		return;
    -	}
    -
    -	var ref_window = this.getCanvasWindow();
    -	var document = ref_window.document;
    -
    -	this.canvas.removeEventListener( "mousedown", this._mousedown_callback );
    -	this.canvas.removeEventListener( "mousewheel", this._mousewheel_callback );
    -	this.canvas.removeEventListener( "DOMMouseScroll", this._mousewheel_callback );
    -	this.canvas.removeEventListener( "keydown", this._key_callback );
    -	document.removeEventListener( "keyup", this._key_callback );
    -	this.canvas.removeEventListener( "contextmenu", this._doNothing );
    -	this.canvas.removeEventListener( "drop", this._ondrop_callback );
    -	this.canvas.removeEventListener( "dragenter", this._doReturnTrue );
    -
    -	this.canvas.removeEventListener("touchstart", this.touchHandler );
    -	this.canvas.removeEventListener("touchmove", this.touchHandler );
    -	this.canvas.removeEventListener("touchend", this.touchHandler );
    -	this.canvas.removeEventListener("touchcancel", this.touchHandler );
    -
    -	this._mousedown_callback = null;
    -	this._mousewheel_callback = null;
    -	this._key_callback = null;
    -	this._ondrop_callback = null;
    -
    -	this._events_binded = false;
    -}
    -
    -LGraphCanvas.getFileExtension = function (url)
    -{
    -	var question = url.indexOf("?");
    -	if(question != -1)
    -		url = url.substr(0,question);
    -	var point = url.lastIndexOf(".");
    -	if(point == -1)
    -		return "";
    -	return url.substr(point+1).toLowerCase();
    -}
    -
    -/**
    -* this function allows to render the canvas using WebGL instead of Canvas2D
    -* this is useful if you plant to render 3D objects inside your nodes, it uses litegl.js for webgl and canvas2DtoWebGL to emulate the Canvas2D calls in webGL
    -* @method enableWebGL
    -**/
    -LGraphCanvas.prototype.enableWebGL = function()
    -{
    -	if(typeof(GL) === undefined)
    -		throw("litegl.js must be included to use a WebGL canvas");
    -	if(typeof(enableWebGLCanvas) === undefined)
    -		throw("webglCanvas.js must be included to use this feature");
    -
    -	this.gl = this.ctx = enableWebGLCanvas(this.canvas);
    -	this.ctx.webgl = true;
    -	this.bgcanvas = this.canvas;
    -	this.bgctx = this.gl;
    -	this.canvas.webgl_enabled = true;
    -
    -	/*
    -	GL.create({ canvas: this.bgcanvas });
    -	this.bgctx = enableWebGLCanvas( this.bgcanvas );
    -	window.gl = this.gl;
    -	*/
    -}
    -
    -
    -/**
    -* marks as dirty the canvas, this way it will be rendered again
    -*
    -* @class LGraphCanvas
    -* @method setDirty
    -* @param {bool} fgcanvas if the foreground canvas is dirty (the one containing the nodes)
    -* @param {bool} bgcanvas if the background canvas is dirty (the one containing the wires)
    -*/
    -LGraphCanvas.prototype.setDirty = function( fgcanvas, bgcanvas )
    -{
    -	if(fgcanvas)
    -		this.dirty_canvas = true;
    -	if(bgcanvas)
    -		this.dirty_bgcanvas = true;
    -}
    -
    -/**
    -* Used to attach the canvas in a popup
    -*
    -* @method getCanvasWindow
    -* @return {window} returns the window where the canvas is attached (the DOM root node)
    -*/
    -LGraphCanvas.prototype.getCanvasWindow = function()
    -{
    -	if(!this.canvas)
    -		return window;
    -	var doc = this.canvas.ownerDocument;
    -	return doc.defaultView || doc.parentWindow;
    -}
    -
    -/**
    -* starts rendering the content of the canvas when needed
    -*
    -* @method startRendering
    -*/
    -LGraphCanvas.prototype.startRendering = function()
    -{
    -	if(this.is_rendering)
    -		return; //already rendering
    -
    -	this.is_rendering = true;
    -	renderFrame.call(this);
    -
    -	function renderFrame()
    -	{
    -		if(!this.pause_rendering)
    -			this.draw();
    -
    -		var window = this.getCanvasWindow();
    -		if(this.is_rendering)
    -			window.requestAnimationFrame( renderFrame.bind(this) );
    -	}
    -}
    -
    -/**
    -* stops rendering the content of the canvas (to save resources)
    -*
    -* @method stopRendering
    -*/
    -LGraphCanvas.prototype.stopRendering = function()
    -{
    -	this.is_rendering = false;
    -	/*
    -	if(this.rendering_timer_id)
    -	{
    -		clearInterval(this.rendering_timer_id);
    -		this.rendering_timer_id = null;
    -	}
    -	*/
    -}
    -
    -/* LiteGraphCanvas input */
    -
    -LGraphCanvas.prototype.processMouseDown = function(e)
    -{
    -	if(!this.graph)
    -		return;
    -
    -	this.adjustMouseEvent(e);
    -
    -	var ref_window = this.getCanvasWindow();
    -	var document = ref_window.document;
    -	LGraphCanvas.active_canvas = this;
    -	var that = this;
    -
    -	//move mouse move event to the window in case it drags outside of the canvas
    -	this.canvas.removeEventListener("mousemove", this._mousemove_callback );
    -	ref_window.document.addEventListener("mousemove", this._mousemove_callback, true ); //catch for the entire window
    -	ref_window.document.addEventListener("mouseup", this._mouseup_callback, true );
    -
    -	var node = this.graph.getNodeOnPos( e.canvasX, e.canvasY, this.visible_nodes, 5 );
    -	var skip_dragging = false;
    -	var skip_action = false;
    -	var now = LiteGraph.getTime();
    -	var is_double_click = (now - this.last_mouseclick) < 300;
    -
    -	this.canvas_mouse[0] = e.canvasX;
    -	this.canvas_mouse[1] = e.canvasY;
    -	this.canvas.focus();
    -
    -    LiteGraph.closeAllContextMenus( ref_window );
    -
    -	if(this.onMouse)
    -	{
    -		if( this.onMouse(e) == true )
    -			return;
    -	}
    -
    -	if(e.which == 1) //left button mouse
    -	{
    -		if( e.ctrlKey )
    -		{
    -			this.dragging_rectangle = new Float32Array(4);
    -			this.dragging_rectangle[0] = e.canvasX;
    -			this.dragging_rectangle[1] = e.canvasY;
    -			this.dragging_rectangle[2] = 1;
    -			this.dragging_rectangle[3] = 1;
    -			skip_action = true;
    -		}
    -
    -		var clicking_canvas_bg = false;
    -
    -		//when clicked on top of a node
    -		//and it is not interactive
    -		if( node && this.allow_interaction && !skip_action )
    -		{
    -			if( !this.live_mode && !node.flags.pinned )
    -				this.bringToFront( node ); //if it wasnt selected?
    -
    -			//not dragging mouse to connect two slots
    -			if(!this.connecting_node && !node.flags.collapsed && !this.live_mode)
    -			{
    -				//Search for corner for resize
    -				if( !skip_action && node.resizable !== false && isInsideRectangle( e.canvasX, e.canvasY, node.pos[0] + node.size[0] - 5, node.pos[1] + node.size[1] - 5 ,10,10 ))
    -				{
    -					this.resizing_node = node;
    -					this.canvas.style.cursor = "se-resize";
    -					skip_action = true;
    -				}
    -				else
    -				{
    -					//search for outputs
    -					if(node.outputs)
    -						for(var i = 0, l = node.outputs.length; i < l; ++i)
    -						{
    -							var output = node.outputs[i];
    -							var link_pos = node.getConnectionPos(false,i);
    -							if( isInsideRectangle( e.canvasX, e.canvasY, link_pos[0] - 15, link_pos[1] - 10, 30,20) )
    -							{
    -								this.connecting_node = node;
    -								this.connecting_output = output;
    -								this.connecting_pos = node.getConnectionPos(false,i);
    -								this.connecting_slot = i;
    -
    -								if( e.shiftKey )
    -									node.disconnectOutput(i);
    -
    -								if (is_double_click) {
    -									if (node.onOutputDblClick)
    -										node.onOutputDblClick(i, e);
    -								} else {
    -									if (node.onOutputClick)
    -										node.onOutputClick(i, e);
    -								}
    -
    -								skip_action = true;
    -								break;
    -							}
    -						}
    -
    -					//search for inputs
    -					if(node.inputs)
    -						for(var i = 0, l = node.inputs.length; i < l; ++i)
    -						{
    -							var input = node.inputs[i];
    -							var link_pos = node.getConnectionPos( true, i );
    -							if( isInsideRectangle(e.canvasX, e.canvasY, link_pos[0] - 15, link_pos[1] - 10, 30,20) )
    -							{
    -								if (is_double_click) {
    -									if (node.onInputDblClick)
    -										node.onInputDblClick(i, e);
    -								} else {
    -									if (node.onInputClick)
    -										node.onInputClick(i, e);
    -								}
    -
    -								if(input.link !== null)
    -								{
    -									var link_info = this.graph.links[ input.link ]; //before disconnecting
    -									node.disconnectInput(i);
    -
    -									if( this.allow_reconnect_links || e.shiftKey )
    -									{
    -										this.connecting_node = this.graph._nodes_by_id[ link_info.origin_id ];
    -										this.connecting_slot = link_info.origin_slot;
    -										this.connecting_output = this.connecting_node.outputs[ this.connecting_slot ];
    -										this.connecting_pos = this.connecting_node.getConnectionPos( false, this.connecting_slot );
    -									}
    -
    -									this.dirty_bgcanvas = true;
    -									skip_action = true;
    -								}
    -							}
    -						}
    -				} //not resizing
    -			}
    -
    -			//Search for corner for collapsing
    -			/*
    -			if( !skip_action && isInsideRectangle( e.canvasX, e.canvasY, node.pos[0], node.pos[1] - LiteGraph.NODE_TITLE_HEIGHT, LiteGraph.NODE_TITLE_HEIGHT, LiteGraph.NODE_TITLE_HEIGHT ))
    -			{
    -				node.collapse();
    -				skip_action = true;
    -			}
    -			*/
    -
    -			//it wasnt clicked on the links boxes
    -			if(!skip_action)
    -			{
    -				var block_drag_node = false;
    -
    -				//widgets
    -				var widget = this.processNodeWidgets( node, this.canvas_mouse, e );
    -				if(widget)
    -				{
    -					block_drag_node = true;
    -					this.node_widget = [node, widget];
    -				}
    -
    -				//double clicking
    -				if (is_double_click && this.selected_nodes[ node.id ])
    -				{
    -					//double click node
    -					if( node.onDblClick)
    -						node.onDblClick(e,[e.canvasX - node.pos[0], e.canvasY - node.pos[1]], this);
    -					this.processNodeDblClicked( node );
    -					block_drag_node = true;
    -				}
    -
    -				//if do not capture mouse
    -				if( node.onMouseDown && node.onMouseDown( e, [e.canvasX - node.pos[0], e.canvasY - node.pos[1]], this ) )
    -				{
    -					block_drag_node = true;
    -				}
    -				else if(this.live_mode)
    -				{
    -					clicking_canvas_bg = true;
    -					block_drag_node = true;
    -				}
    -
    -				if(!block_drag_node)
    -				{
    -					if(this.allow_dragnodes)
    -						this.node_dragged = node;
    -					if(!this.selected_nodes[ node.id ])
    -						this.processNodeSelected( node, e );
    -				}
    -
    -				this.dirty_canvas = true;
    -			}
    -		}
    -		else //clicked outside of nodes
    -		{
    -
    -			//search for link connector
    -			for(var i = 0; i < this.visible_links.length; ++i)
    -			{
    -				var link = this.visible_links[i];
    -				var center = link._pos;
    -				if( !center || e.canvasX < center[0] - 4 || e.canvasX > center[0] + 4 || e.canvasY < center[1] - 4 || e.canvasY > center[1] + 4 )
    -					continue;
    -				//link clicked
    -				this.showLinkMenu( link, e );
    -				break;
    -			}
    -
    -			this.selected_group = this.graph.getGroupOnPos( e.canvasX, e.canvasY );
    -			this.selected_group_resizing = false;
    -			if( this.selected_group )
    -			{
    -				if( e.ctrlKey )
    -					this.dragging_rectangle = null;
    -
    -				var dist = distance( [e.canvasX, e.canvasY], [ this.selected_group.pos[0] + this.selected_group.size[0], this.selected_group.pos[1] + this.selected_group.size[1] ] );
    -				if( (dist * this.ds.scale) < 10 )
    -					this.selected_group_resizing = true;
    -				else
    -					this.selected_group.recomputeInsideNodes();
    -			}
    -
    -			if( is_double_click )
    -				this.showSearchBox( e );
    -			
    -			clicking_canvas_bg = true;
    -		}
    -
    -		if( !skip_action && clicking_canvas_bg && this.allow_dragcanvas )
    -		{
    -			this.dragging_canvas = true;
    -		}
    -	}
    -	else if (e.which == 2) //middle button
    -	{
    -
    -	}
    -	else if (e.which == 3) //right button
    -	{
    -		this.processContextMenu( node, e );
    -	}
    -
    -	//TODO
    -	//if(this.node_selected != prev_selected)
    -	//	this.onNodeSelectionChange(this.node_selected);
    -
    -	this.last_mouse[0] = e.localX;
    -	this.last_mouse[1] = e.localY;
    -	this.last_mouseclick = LiteGraph.getTime();
    -	this.last_mouse_dragging = true;
    -
    -	/*
    -	if( (this.dirty_canvas || this.dirty_bgcanvas) && this.rendering_timer_id == null)
    -		this.draw();
    -	*/
    -
    -	this.graph.change();
    -
    -	//this is to ensure to defocus(blur) if a text input element is on focus
    -	if(!ref_window.document.activeElement || (ref_window.document.activeElement.nodeName.toLowerCase() != "input" && ref_window.document.activeElement.nodeName.toLowerCase() != "textarea"))
    -		e.preventDefault();
    -	e.stopPropagation();
    -
    -	if(this.onMouseDown)
    -		this.onMouseDown(e);
    -
    -	return false;
    -}
    -
    -/**
    -* Called when a mouse move event has to be processed
    -* @method processMouseMove
    -**/
    -LGraphCanvas.prototype.processMouseMove = function(e)
    -{
    -	if(this.autoresize)
    -		this.resize();
    -
    -	if(!this.graph)
    -		return;
    -
    -	LGraphCanvas.active_canvas = this;
    -	this.adjustMouseEvent(e);
    -	var mouse = [e.localX, e.localY];
    -	var delta = [mouse[0] - this.last_mouse[0], mouse[1] - this.last_mouse[1]];
    -	this.last_mouse = mouse;
    -	this.canvas_mouse[0] = e.canvasX;
    -	this.canvas_mouse[1] = e.canvasY;
    -	e.dragging = this.last_mouse_dragging;
    -
    -	if( this.node_widget )
    -	{
    -		this.processNodeWidgets( this.node_widget[0], this.canvas_mouse, e, this.node_widget[1] );
    -		this.dirty_canvas = true;
    -	}
    -
    -	if( this.dragging_rectangle )
    -	{
    -		this.dragging_rectangle[2] = e.canvasX - this.dragging_rectangle[0];
    -		this.dragging_rectangle[3] = e.canvasY - this.dragging_rectangle[1];
    -		this.dirty_canvas = true;
    -	}
    -	else if (this.selected_group) //moving/resizing a group
    -	{
    -		if( this.selected_group_resizing )
    -			this.selected_group.size = [ e.canvasX - this.selected_group.pos[0], e.canvasY - this.selected_group.pos[1] ];
    -		else
    -		{
    -			var deltax = delta[0] / this.ds.scale;
    -			var deltay = delta[1] / this.ds.scale;
    -			this.selected_group.move( deltax, deltay, e.ctrlKey );
    -			if( this.selected_group._nodes.length)
    -				this.dirty_canvas = true;
    -		}
    -		this.dirty_bgcanvas = true;
    -	}
    -	else if(this.dragging_canvas)
    -	{
    -		this.ds.offset[0] += delta[0] / this.ds.scale;
    -		this.ds.offset[1] += delta[1] / this.ds.scale;
    -		this.dirty_canvas = true;
    -		this.dirty_bgcanvas = true;
    -	}
    -	else if(this.allow_interaction)
    -	{
    -		if(this.connecting_node)
    -			this.dirty_canvas = true;
    -
    -		//get node over
    -		var node = this.graph.getNodeOnPos( e.canvasX, e.canvasY, this.visible_nodes );
    -
    -		//remove mouseover flag
    -		for(var i = 0, l = this.graph._nodes.length; i < l; ++i)
    -		{
    -			if(this.graph._nodes[i].mouseOver && node != this.graph._nodes[i])
    -			{
    -				//mouse leave
    -				this.graph._nodes[i].mouseOver = false;
    -				if(this.node_over && this.node_over.onMouseLeave)
    -					this.node_over.onMouseLeave(e);
    -				this.node_over = null;
    -				this.dirty_canvas = true;
    -			}
    -		}
    -
    -		//mouse over a node
    -		if(node)
    -		{
    -			//this.canvas.style.cursor = "move";
    -			if(!node.mouseOver)
    -			{
    -				//mouse enter
    -				node.mouseOver = true;
    -				this.node_over = node;
    -				this.dirty_canvas = true;
    -
    -				if(node.onMouseEnter) node.onMouseEnter(e);
    -			}
    -
    -			//in case the node wants to do something
    -			if(node.onMouseMove)
    -				node.onMouseMove(e, [e.canvasX - node.pos[0], e.canvasY - node.pos[1]], this);
    -
    -			//if dragging a link 
    -			if(this.connecting_node)
    -			{
    -				var pos = this._highlight_input || [0,0]; //to store the output of isOverNodeInput
    -
    -				//on top of input
    -				if( this.isOverNodeBox( node, e.canvasX, e.canvasY ) )
    -				{
    -					//mouse on top of the corner box, dont know what to do
    -				}
    -				else
    -				{
    -					//check if I have a slot below de mouse
    -					var slot = this.isOverNodeInput( node, e.canvasX, e.canvasY, pos );
    -					if(slot != -1 && node.inputs[slot] )
    -					{
    -						var slot_type = node.inputs[slot].type;
    -						if( LiteGraph.isValidConnection( this.connecting_output.type, slot_type ) )
    -							this._highlight_input = pos;
    -					}
    -					else
    -						this._highlight_input = null;
    -				}
    -			}
    -
    -			//Search for corner
    -			if(this.canvas)
    -			{
    -				if( isInsideRectangle(e.canvasX, e.canvasY, node.pos[0] + node.size[0] - 5, node.pos[1] + node.size[1] - 5 ,5,5 ))
    -					this.canvas.style.cursor = "se-resize";
    -				else
    -					this.canvas.style.cursor = "crosshair";
    -			}
    -		}
    -		else if(this.canvas)
    -			this.canvas.style.cursor = "";
    -
    -		if(this.node_capturing_input && this.node_capturing_input != node && this.node_capturing_input.onMouseMove)
    -		{
    -			this.node_capturing_input.onMouseMove(e);
    -		}
    -
    -
    -		if(this.node_dragged && !this.live_mode)
    -		{
    -			for(var i in this.selected_nodes)
    -			{
    -				var n = this.selected_nodes[i];
    -				n.pos[0] += delta[0] / this.ds.scale;
    -				n.pos[1] += delta[1] / this.ds.scale;
    -			}
    -
    -			this.dirty_canvas = true;
    -			this.dirty_bgcanvas = true;
    -		}
    -
    -		if(this.resizing_node && !this.live_mode)
    -		{
    -			//convert mouse to node space
    -			this.resizing_node.size[0] = e.canvasX - this.resizing_node.pos[0];
    -			this.resizing_node.size[1] = e.canvasY - this.resizing_node.pos[1];
    -
    -			//constraint size
    -			var max_slots = Math.max( this.resizing_node.inputs ? this.resizing_node.inputs.length : 0, this.resizing_node.outputs ? this.resizing_node.outputs.length : 0);
    -			var min_height = max_slots * LiteGraph.NODE_SLOT_HEIGHT + ( this.resizing_node.widgets ? this.resizing_node.widgets.length : 0 ) * (LiteGraph.NODE_WIDGET_HEIGHT + 4 ) + 4;
    -			if(this.resizing_node.size[1] < min_height )
    -				this.resizing_node.size[1] = min_height;
    -			if(this.resizing_node.size[0] < LiteGraph.NODE_MIN_WIDTH)
    -				this.resizing_node.size[0] = LiteGraph.NODE_MIN_WIDTH;
    -
    -			this.canvas.style.cursor = "se-resize";
    -			this.dirty_canvas = true;
    -			this.dirty_bgcanvas = true;
    -		}
    -	}
    -
    -	e.preventDefault();
    -	return false;
    -}
    -
    -/**
    -* Called when a mouse up event has to be processed
    -* @method processMouseUp
    -**/
    -LGraphCanvas.prototype.processMouseUp = function(e)
    -{
    -	if(!this.graph)
    -		return;
    -
    -	var window = this.getCanvasWindow();
    -	var document = window.document;
    -	LGraphCanvas.active_canvas = this;
    -
    -	//restore the mousemove event back to the canvas
    -	document.removeEventListener("mousemove", this._mousemove_callback, true );
    -	this.canvas.addEventListener("mousemove", this._mousemove_callback, true);
    -	document.removeEventListener("mouseup", this._mouseup_callback, true );
    -
    -	this.adjustMouseEvent(e);
    -	var now = LiteGraph.getTime();
    -	e.click_time = (now - this.last_mouseclick);
    -	this.last_mouse_dragging = false;
    -
    -	if (e.which == 1) //left button
    -	{
    -		this.node_widget = null;
    -
    -		if( this.selected_group )
    -		{
    -			var diffx = this.selected_group.pos[0] - Math.round( this.selected_group.pos[0] );
    -			var diffy = this.selected_group.pos[1] - Math.round( this.selected_group.pos[1] );
    -			this.selected_group.move( diffx, diffy, e.ctrlKey );
    -			this.selected_group.pos[0] = Math.round( this.selected_group.pos[0] );
    -			this.selected_group.pos[1] = Math.round( this.selected_group.pos[1] );
    -			if( this.selected_group._nodes.length )
    -				this.dirty_canvas = true;
    -			this.selected_group = null;
    -		}
    -		this.selected_group_resizing = false;
    -
    -		if( this.dragging_rectangle )
    -		{
    -			if(this.graph)
    -			{
    -				var nodes = this.graph._nodes;
    -				var node_bounding = new Float32Array(4);
    -				this.deselectAllNodes();
    -				//compute bounding and flip if left to right
    -				var w = Math.abs( this.dragging_rectangle[2] );
    -				var h = Math.abs( this.dragging_rectangle[3] );
    -				var startx = this.dragging_rectangle[2] < 0 ? this.dragging_rectangle[0] - w : this.dragging_rectangle[0];
    -				var starty = this.dragging_rectangle[3] < 0 ? this.dragging_rectangle[1] - h : this.dragging_rectangle[1];
    -				this.dragging_rectangle[0] = startx; this.dragging_rectangle[1] = starty; this.dragging_rectangle[2] = w; this.dragging_rectangle[3] = h;
    -
    -				//test against all nodes (not visible becasue the rectangle maybe start outside
    -				var to_select = [];
    -				for(var i = 0; i < nodes.length; ++i)
    -				{
    -					var node = nodes[i];
    -					node.getBounding( node_bounding );
    -					if(!overlapBounding( this.dragging_rectangle, node_bounding ))
    -						continue; //out of the visible area
    -					to_select.push(node);
    -				}
    -				if(to_select.length)
    -					this.selectNodes(to_select);
    -			}
    -			this.dragging_rectangle = null;
    -		}
    -		else if(this.connecting_node) //dragging a connection
    -		{
    -			this.dirty_canvas = true;
    -			this.dirty_bgcanvas = true;
    -
    -			var node = this.graph.getNodeOnPos( e.canvasX, e.canvasY, this.visible_nodes );
    -
    -			//node below mouse
    -			if(node)
    -			{
    -				if( this.connecting_output.type == LiteGraph.EVENT && this.isOverNodeBox( node, e.canvasX, e.canvasY ) )
    -				{
    -					this.connecting_node.connect( this.connecting_slot, node, LiteGraph.EVENT );
    -				}
    -				else
    -				{
    -					//slot below mouse? connect
    -					var slot = this.isOverNodeInput(node, e.canvasX, e.canvasY);
    -					if(slot != -1)
    -					{
    -						this.connecting_node.connect(this.connecting_slot, node, slot);
    -					}
    -					else
    -					{ //not on top of an input
    -						var input = node.getInputInfo(0);
    -						//auto connect
    -						if(this.connecting_output.type == LiteGraph.EVENT)
    -							this.connecting_node.connect( this.connecting_slot, node, LiteGraph.EVENT );
    -						else
    -							if(input && !input.link && LiteGraph.isValidConnection( input.type && this.connecting_output.type ) )
    -								this.connecting_node.connect( this.connecting_slot, node, 0 );
    -					}
    -				}
    -			}
    -
    -			this.connecting_output = null;
    -			this.connecting_pos = null;
    -			this.connecting_node = null;
    -			this.connecting_slot = -1;
    -
    -		}//not dragging connection
    -		else if(this.resizing_node)
    -		{
    -			this.dirty_canvas = true;
    -			this.dirty_bgcanvas = true;
    -			this.resizing_node = null;
    -		}
    -		else if(this.node_dragged) //node being dragged?
    -		{
    -			var node = this.node_dragged;
    -			if( node && e.click_time < 300 && isInsideRectangle( e.canvasX, e.canvasY, node.pos[0], node.pos[1] - LiteGraph.NODE_TITLE_HEIGHT, LiteGraph.NODE_TITLE_HEIGHT, LiteGraph.NODE_TITLE_HEIGHT ))
    -				node.collapse();
    -
    -			this.dirty_canvas = true;
    -			this.dirty_bgcanvas = true;
    -			this.node_dragged.pos[0] = Math.round(this.node_dragged.pos[0]);
    -			this.node_dragged.pos[1] = Math.round(this.node_dragged.pos[1]);
    -			if(this.graph.config.align_to_grid)
    -				this.node_dragged.alignToGrid();
    -			this.node_dragged = null;
    -		}
    -		else //no node being dragged
    -		{
    -			//get node over
    -			var node = this.graph.getNodeOnPos( e.canvasX, e.canvasY, this.visible_nodes );
    -
    -			if ( !node && e.click_time < 300 )
    -				this.deselectAllNodes();
    -
    -			this.dirty_canvas = true;
    -			this.dragging_canvas = false;
    -
    -			if( this.node_over && this.node_over.onMouseUp )
    -				this.node_over.onMouseUp(e, [e.canvasX - this.node_over.pos[0], e.canvasY - this.node_over.pos[1]], this );
    -			if( this.node_capturing_input && this.node_capturing_input.onMouseUp )
    -				this.node_capturing_input.onMouseUp(e, [e.canvasX - this.node_capturing_input.pos[0], e.canvasY - this.node_capturing_input.pos[1]] );
    -		}
    -	}
    -	else if (e.which == 2) //middle button
    -	{
    -		//trace("middle");
    -		this.dirty_canvas = true;
    -		this.dragging_canvas = false;
    -	}
    -	else if (e.which == 3) //right button
    -	{
    -		//trace("right");
    -		this.dirty_canvas = true;
    -		this.dragging_canvas = false;
    -	}
    -
    -	/*
    -	if((this.dirty_canvas || this.dirty_bgcanvas) && this.rendering_timer_id == null)
    -		this.draw();
    -	*/
    -
    -	this.graph.change();
    -
    -	e.stopPropagation();
    -	e.preventDefault();
    -	return false;
    -}
    -
    -/**
    -* Called when a mouse wheel event has to be processed
    -* @method processMouseWheel
    -**/
    -LGraphCanvas.prototype.processMouseWheel = function(e)
    -{
    -	if(!this.graph || !this.allow_dragcanvas)
    -		return;
    -
    -	var delta = (e.wheelDeltaY != null ? e.wheelDeltaY : e.detail * -60);
    -
    -	this.adjustMouseEvent(e);
    -
    -	var scale = this.ds.scale;
    -
    -	if (delta > 0)
    -		scale *= 1.1;
    -	else if (delta < 0)
    -		scale *= 1/(1.1);
    -
    -	//this.setZoom( scale, [ e.localX, e.localY ] );
    -	this.ds.changeScale( scale, [ e.localX, e.localY ] );
    -
    -	this.graph.change();
    -
    -	e.preventDefault();
    -	return false; // prevent default
    -}
    -
    -/**
    -* retuns true if a position (in graph space) is on top of a node little corner box
    -* @method isOverNodeBox
    -**/
    -LGraphCanvas.prototype.isOverNodeBox = function( node, canvasx, canvasy )
    -{
    -	var title_height = LiteGraph.NODE_TITLE_HEIGHT;
    -	if( isInsideRectangle( canvasx, canvasy, node.pos[0] + 2, node.pos[1] + 2 - title_height, title_height - 4, title_height - 4) )
    -		return true;
    -	return false;
    -}
    -
    -/**
    -* retuns true if a position (in graph space) is on top of a node input slot
    -* @method isOverNodeInput
    -**/
    -LGraphCanvas.prototype.isOverNodeInput = function(node, canvasx, canvasy, slot_pos )
    -{
    -	if(node.inputs)
    -		for(var i = 0, l = node.inputs.length; i < l; ++i)
    -		{
    -			var input = node.inputs[i];
    -			var link_pos = node.getConnectionPos( true, i );
    -			var is_inside = false;
    -			if( node.horizontal )
    -				is_inside = isInsideRectangle(canvasx, canvasy, link_pos[0] - 5, link_pos[1] - 10, 10,20)
    -			else
    -				is_inside = isInsideRectangle(canvasx, canvasy, link_pos[0] - 10, link_pos[1] - 5, 40,10)
    -			if(is_inside)
    -			{
    -				if(slot_pos)
    -				{
    -					slot_pos[0] = link_pos[0];
    -					slot_pos[1] = link_pos[1];
    -				}
    -				return i;
    -			}
    -		}
    -	return -1;
    -}
    -
    -/**
    -* process a key event
    -* @method processKey
    -**/
    -LGraphCanvas.prototype.processKey = function(e)
    -{
    -	if(!this.graph)
    -		return;
    -
    -	var block_default = false;
    -	//console.log(e); //debug
    -
    -	if(e.target.localName == "input")
    -		return;
    -
    -	if(e.type == "keydown")
    -	{
    -		if(e.keyCode == 32) //esc
    -		{
    -			this.dragging_canvas = true;
    -			block_default = true;
    -		}
    -
    -		//select all Control A
    -		if(e.keyCode == 65 && e.ctrlKey)
    -		{
    -			this.selectNodes();
    -			block_default = true;
    -		}
    -
    -		if(e.code == "KeyC" && (e.metaKey || e.ctrlKey) && !e.shiftKey ) //copy
    -		{
    -			if(this.selected_nodes)
    -			{
    -				this.copyToClipboard();
    -				block_default = true;
    -			}
    -		}
    -
    -		if(e.code == "KeyV" && (e.metaKey || e.ctrlKey) && !e.shiftKey ) //paste
    -		{
    -			this.pasteFromClipboard();
    -		}
    -
    -		//delete or backspace
    -		if(e.keyCode == 46 || e.keyCode == 8)
    -		{
    -			if(e.target.localName != "input" && e.target.localName != "textarea")
    -			{
    -				this.deleteSelectedNodes();
    -				block_default = true;
    -			}
    -		}
    -
    -		//collapse
    -		//...
    -
    -		//TODO
    -		if(this.selected_nodes)
    -			for (var i in this.selected_nodes)
    -				if(this.selected_nodes[i].onKeyDown)
    -					this.selected_nodes[i].onKeyDown(e);
    -	}
    -	else if( e.type == "keyup" )
    -	{
    -		if(e.keyCode == 32)
    -			this.dragging_canvas = false;
    -
    -		if(this.selected_nodes)
    -			for (var i in this.selected_nodes)
    -				if(this.selected_nodes[i].onKeyUp)
    -					this.selected_nodes[i].onKeyUp(e);
    -	}
    -
    -	this.graph.change();
    -
    -	if(block_default)
    -	{
    -		e.preventDefault();
    -		e.stopImmediatePropagation();
    -		return false;
    -	}
    -}
    -
    -LGraphCanvas.prototype.copyToClipboard = function()
    -{
    -	var clipboard_info = {
    -		nodes: [],
    -		links: []
    -	};
    -	var index = 0;
    -	var selected_nodes_array = [];
    -	for(var i in this.selected_nodes)
    -	{
    -		var node = this.selected_nodes[i];
    -		node._relative_id = index;
    -		selected_nodes_array.push( node );
    -		index += 1;
    -	}
    -
    -	for(var i = 0; i < selected_nodes_array.length; ++i)
    -	{
    -		var node = selected_nodes_array[i];
    -		clipboard_info.nodes.push( node.clone().serialize() );
    -		if(node.inputs && node.inputs.length)
    -			for(var j = 0; j < node.inputs.length; ++j)
    -			{
    -				var input = node.inputs[j];
    -				if(!input || input.link == null)
    -					continue;
    -				var link_info = this.graph.links[ input.link ];
    -				if(!link_info)
    -					continue;
    -				var target_node = this.graph.getNodeById( link_info.origin_id );
    -				if(!target_node || !this.selected_nodes[ target_node.id ] ) //improve this by allowing connections to non-selected nodes
    -					continue; //not selected
    -				clipboard_info.links.push([ target_node._relative_id, j, node._relative_id, link_info.target_slot ]);
    -			}
    -	}
    -	localStorage.setItem( "litegrapheditor_clipboard", JSON.stringify( clipboard_info ) );
    -}
    -
    -LGraphCanvas.prototype.pasteFromClipboard = function()
    -{
    -	var data = localStorage.getItem( "litegrapheditor_clipboard" );
    -	if(!data)
    -		return;
    -
    -	//create nodes
    -	var clipboard_info = JSON.parse(data);
    -	var nodes = [];
    -	for(var i = 0; i < clipboard_info.nodes.length; ++i)
    -	{
    -		var node_data = clipboard_info.nodes[i];
    -		var node = LiteGraph.createNode( node_data.type );
    -		if(node)
    -		{
    -			node.configure(node_data);
    -			node.pos[0] += 5;
    -			node.pos[1] += 5;
    -			this.graph.add( node );
    -			nodes.push( node );
    -		}
    -	}
    -
    -	//create links
    -	for(var i = 0; i < clipboard_info.links.length; ++i)
    -	{
    -		var link_info = clipboard_info.links[i];
    -		var origin_node = nodes[ link_info[0] ];
    -		var target_node = nodes[ link_info[2] ];
    -		origin_node.connect( link_info[1], target_node, link_info[3] );
    -	}
    -
    -	this.selectNodes( nodes );
    -}
    -
    -/**
    -* process a item drop event on top the canvas
    -* @method processDrop
    -**/
    -LGraphCanvas.prototype.processDrop = function(e)
    -{
    -	e.preventDefault();
    -	this.adjustMouseEvent(e);
    -
    -
    -	var pos = [e.canvasX,e.canvasY];
    -	var node = this.graph.getNodeOnPos(pos[0],pos[1]);
    -
    -	if(!node)
    -	{
    -		var r = null;
    -		if(this.onDropItem)
    -			r = this.onDropItem( event );
    -		if(!r)
    -			this.checkDropItem(e);
    -		return;
    -	}
    -
    -	if( node.onDropFile || node.onDropData )
    -	{
    -		var files = e.dataTransfer.files;
    -		if(files && files.length)
    -		{
    -			for(var i=0; i < files.length; i++)
    -			{
    -				var file = e.dataTransfer.files[0];
    -				var filename = file.name;
    -				var ext = LGraphCanvas.getFileExtension( filename );
    -				//console.log(file);
    -
    -				if(node.onDropFile)
    -					node.onDropFile(file);
    -
    -				if(node.onDropData)
    -				{
    -					//prepare reader
    -					var reader = new FileReader();
    -					reader.onload = function (event) {
    -						//console.log(event.target);
    -						var data = event.target.result;
    -						node.onDropData( data, filename, file );
    -					};
    -
    -					//read data
    -					var type = file.type.split("/")[0];
    -					if(type == "text" || type == "")
    -						reader.readAsText(file);
    -					else if (type == "image")
    -						reader.readAsDataURL(file);
    -					else
    -						reader.readAsArrayBuffer(file);
    -				}
    -			}
    -		}
    -	}
    -
    -	if(node.onDropItem)
    -	{
    -		if( node.onDropItem( event ) )
    -			return true;
    -	}
    -
    -	if(this.onDropItem)
    -		return this.onDropItem( event );
    -
    -	return false;
    -}
    -
    -//called if the graph doesnt have a default drop item behaviour
    -LGraphCanvas.prototype.checkDropItem = function(e)
    -{
    -	if(e.dataTransfer.files.length)
    -	{
    -		var file = e.dataTransfer.files[0];
    -		var ext = LGraphCanvas.getFileExtension( file.name ).toLowerCase();
    -		var nodetype = LiteGraph.node_types_by_file_extension[ext];
    -		if(nodetype)
    -		{
    -			var node = LiteGraph.createNode( nodetype.type );
    -			node.pos = [e.canvasX, e.canvasY];
    -			this.graph.add( node );
    -			if( node.onDropFile )
    -				node.onDropFile( file );
    -		}
    -	}
    -}
    -
    -
    -LGraphCanvas.prototype.processNodeDblClicked = function(n)
    -{
    -	if(this.onShowNodePanel)
    -		this.onShowNodePanel(n);
    -
    -	if(this.onNodeDblClicked)
    -		this.onNodeDblClicked(n);
    -
    -	this.setDirty(true);
    -}
    -
    -LGraphCanvas.prototype.processNodeSelected = function(node,e)
    -{
    -	this.selectNode( node, e && e.shiftKey );
    -	if(this.onNodeSelected)
    -		this.onNodeSelected(node);
    -}
    -
    -LGraphCanvas.prototype.processNodeDeselected = function(node)
    -{
    -	this.deselectNode(node);
    -	if(this.onNodeDeselected)
    -		this.onNodeDeselected(node);
    -}
    -
    -/**
    -* selects a given node (or adds it to the current selection)
    -* @method selectNode
    -**/
    -LGraphCanvas.prototype.selectNode = function( node, add_to_current_selection )
    -{
    -	if(node == null)
    -		this.deselectAllNodes();
    -	else
    -		this.selectNodes([node], add_to_current_selection );
    -}
    -
    -/**
    -* selects several nodes (or adds them to the current selection)
    -* @method selectNodes
    -**/
    -LGraphCanvas.prototype.selectNodes = function( nodes, add_to_current_selection )
    -{
    -	if(!add_to_current_selection)
    -		this.deselectAllNodes();
    -
    -	nodes = nodes || this.graph._nodes;
    -	for(var i = 0; i < nodes.length; ++i)
    -	{
    -		var node = nodes[i];
    -		if(node.is_selected)
    -			continue;
    -
    -		if( !node.is_selected && node.onSelected )
    -			node.onSelected();
    -		node.is_selected = true;
    -		this.selected_nodes[ node.id ] = node;
    -
    -		if(node.inputs)
    -			for(var j = 0; j < node.inputs.length; ++j)
    -				this.highlighted_links[ node.inputs[j].link ] = true;
    -		if(node.outputs)
    -			for(var j = 0; j < node.outputs.length; ++j)
    -			{
    -				var out = node.outputs[j];
    -				if( out.links )
    -					for(var k = 0; k < out.links.length; ++k)
    -						this.highlighted_links[ out.links[k] ] = true;
    -			}
    -
    -	}
    -
    -	this.setDirty(true);
    -}
    -
    -/**
    -* removes a node from the current selection
    -* @method deselectNode
    -**/
    -LGraphCanvas.prototype.deselectNode = function( node )
    -{
    -	if(!node.is_selected)
    -		return;
    -	if(node.onDeselected)
    -		node.onDeselected();
    -	node.is_selected = false;
    -
    -	//remove highlighted
    -	if(node.inputs)
    -		for(var i = 0; i < node.inputs.length; ++i)
    -			delete this.highlighted_links[ node.inputs[i].link ];
    -	if(node.outputs)
    -		for(var i = 0; i < node.outputs.length; ++i)
    -		{
    -			var out = node.outputs[i];
    -			if( out.links )
    -				for(var j = 0; j < out.links.length; ++j)
    -					delete this.highlighted_links[ out.links[j] ];
    -		}
    -}
    -
    -/**
    -* removes all nodes from the current selection
    -* @method deselectAllNodes
    -**/
    -LGraphCanvas.prototype.deselectAllNodes = function()
    -{
    -	if(!this.graph)
    -		return;
    -	var nodes = this.graph._nodes;
    -	for(var i = 0, l = nodes.length; i < l; ++i)
    -	{
    -		var node = nodes[i];
    -		if(!node.is_selected)
    -			continue;
    -		if(node.onDeselected)
    -			node.onDeselected();
    -		node.is_selected = false;
    -	}
    -	this.selected_nodes = {};
    -	this.highlighted_links = {};
    -	this.setDirty(true);
    -}
    -
    -/**
    -* deletes all nodes in the current selection from the graph
    -* @method deleteSelectedNodes
    -**/
    -LGraphCanvas.prototype.deleteSelectedNodes = function()
    -{
    -	for(var i in this.selected_nodes)
    -	{
    -		var m = this.selected_nodes[i];
    -		//if(m == this.node_in_panel) this.showNodePanel(null);
    -		this.graph.remove(m);
    -	}
    -	this.selected_nodes = {};
    -	this.highlighted_links = {};
    -	this.setDirty(true);
    -}
    -
    -/**
    -* centers the camera on a given node
    -* @method centerOnNode
    -**/
    -LGraphCanvas.prototype.centerOnNode = function(node)
    -{
    -	this.ds.offset[0] = -node.pos[0] - node.size[0] * 0.5 + (this.canvas.width * 0.5 / this.ds.scale);
    -	this.ds.offset[1] = -node.pos[1] - node.size[1] * 0.5 + (this.canvas.height * 0.5 / this.ds.scale);
    -	this.setDirty(true,true);
    -}
    -
    -/**
    -* adds some useful properties to a mouse event, like the position in graph coordinates
    -* @method adjustMouseEvent
    -**/
    -LGraphCanvas.prototype.adjustMouseEvent = function(e)
    -{
    -	if(this.canvas)
    -	{
    -		var b = this.canvas.getBoundingClientRect();
    -		e.localX = e.clientX - b.left;
    -		e.localY = e.clientY - b.top;
    -	}
    -	else
    -	{
    -		e.localX = e.clientX;
    -		e.localY = e.clientY;
    -	}
    -
    -	e.deltaX = e.localX - this.last_mouse_position[0];
    -	e.deltaY = e.localY - this.last_mouse_position[1];
    -
    -	this.last_mouse_position[0] = e.localX;
    -	this.last_mouse_position[1] = e.localY;
    -
    -	e.canvasX = e.localX / this.ds.scale - this.ds.offset[0];
    -	e.canvasY = e.localY / this.ds.scale - this.ds.offset[1];
    -}
    -
    -/**
    -* changes the zoom level of the graph (default is 1), you can pass also a place used to pivot the zoom
    -* @method setZoom
    -**/
    -LGraphCanvas.prototype.setZoom = function(value, zooming_center)
    -{
    -	this.ds.changeScale( value, zooming_center);
    -	/*
    -	if(!zooming_center && this.canvas)
    -		zooming_center = [this.canvas.width * 0.5,this.canvas.height * 0.5];
    -
    -	var center = this.convertOffsetToCanvas( zooming_center );
    -
    -	this.ds.scale = value;
    -
    -	if(this.scale > this.max_zoom)
    -		this.scale = this.max_zoom;
    -	else if(this.scale < this.min_zoom)
    -		this.scale = this.min_zoom;
    -
    -	var new_center = this.convertOffsetToCanvas( zooming_center );
    -	var delta_offset = [new_center[0] - center[0], new_center[1] - center[1]];
    -
    -	this.offset[0] += delta_offset[0];
    -	this.offset[1] += delta_offset[1];
    -	*/
    -
    -	this.dirty_canvas = true;
    -	this.dirty_bgcanvas = true;
    -}
    -
    -/**
    -* converts a coordinate from graph coordinates to canvas2D coordinates
    -* @method convertOffsetToCanvas
    -**/
    -LGraphCanvas.prototype.convertOffsetToCanvas = function( pos, out )
    -{
    -	return this.ds.convertOffsetToCanvas( pos, out );
    -}
    -
    -/**
    -* converts a coordinate from Canvas2D coordinates to graph space
    -* @method convertCanvasToOffset
    -**/
    -LGraphCanvas.prototype.convertCanvasToOffset = function( pos, out )
    -{
    -	return this.ds.convertCanvasToOffset( pos, out );
    -}
    -
    -//converts event coordinates from canvas2D to graph coordinates
    -LGraphCanvas.prototype.convertEventToCanvasOffset = function(e)
    -{
    -	var rect = this.canvas.getBoundingClientRect();
    -	return this.convertCanvasToOffset([e.clientX - rect.left,e.clientY - rect.top]);
    -}
    -
    -/**
    -* brings a node to front (above all other nodes)
    -* @method bringToFront
    -**/
    -LGraphCanvas.prototype.bringToFront = function(node)
    -{
    -	var i = this.graph._nodes.indexOf(node);
    -	if(i == -1) return;
    -
    -	this.graph._nodes.splice(i,1);
    -	this.graph._nodes.push(node);
    -}
    -
    -/**
    -* sends a node to the back (below all other nodes)
    -* @method sendToBack
    -**/
    -LGraphCanvas.prototype.sendToBack = function(node)
    -{
    -	var i = this.graph._nodes.indexOf(node);
    -	if(i == -1) return;
    -
    -	this.graph._nodes.splice(i,1);
    -	this.graph._nodes.unshift(node);
    -}
    -
    -/* Interaction */
    -
    -
    -
    -/* LGraphCanvas render */
    -var temp = new Float32Array(4);
    -
    -/**
    -* checks which nodes are visible (inside the camera area)
    -* @method computeVisibleNodes
    -**/
    -LGraphCanvas.prototype.computeVisibleNodes = function( nodes, out )
    -{
    -	var visible_nodes = out || [];
    -	visible_nodes.length = 0;
    -	nodes = nodes || this.graph._nodes;
    -	for(var i = 0, l = nodes.length; i < l; ++i)
    -	{
    -		var n = nodes[i];
    -
    -		//skip rendering nodes in live mode
    -		if( this.live_mode && !n.onDrawBackground && !n.onDrawForeground )
    -			continue;
    -
    -		if(!overlapBounding( this.visible_area, n.getBounding( temp ) ))
    -			continue; //out of the visible area
    -
    -		visible_nodes.push(n);
    -	}
    -	return visible_nodes;
    -}
    -
    -/**
    -* renders the whole canvas content, by rendering in two separated canvas, one containing the background grid and the connections, and one containing the nodes)
    -* @method draw
    -**/
    -LGraphCanvas.prototype.draw = function(force_canvas, force_bgcanvas)
    -{
    -	if(!this.canvas)
    -		return;
    -
    -	//fps counting
    -	var now = LiteGraph.getTime();
    -	this.render_time = (now - this.last_draw_time)*0.001;
    -	this.last_draw_time = now;
    -
    -	if(this.graph)
    -		this.ds.computeVisibleArea();
    -
    -	if(this.dirty_bgcanvas || force_bgcanvas || this.always_render_background || (this.graph && this.graph._last_trigger_time && (now - this.graph._last_trigger_time) < 1000) )
    -		this.drawBackCanvas();
    -
    -	if(this.dirty_canvas || force_canvas)
    -		this.drawFrontCanvas();
    -
    -	this.fps = this.render_time ? (1.0 / this.render_time) : 0;
    -	this.frame += 1;
    -}
    -
    -/**
    -* draws the front canvas (the one containing all the nodes)
    -* @method drawFrontCanvas
    -**/
    -LGraphCanvas.prototype.drawFrontCanvas = function()
    -{
    -	this.dirty_canvas = false;
    -
    -	if(!this.ctx)
    -		this.ctx = this.bgcanvas.getContext("2d");
    -	var ctx = this.ctx;
    -	if(!ctx) //maybe is using webgl...
    -		return;
    -
    -	if(ctx.start2D)
    -		ctx.start2D();
    -
    -	var canvas = this.canvas;
    -
    -	//reset in case of error
    -	ctx.restore();
    -	ctx.setTransform(1, 0, 0, 1, 0, 0);
    -
    -	//clip dirty area if there is one, otherwise work in full canvas
    -	if(this.dirty_area)
    -	{
    -		ctx.save();
    -		ctx.beginPath();
    -		ctx.rect(this.dirty_area[0],this.dirty_area[1],this.dirty_area[2],this.dirty_area[3]);
    -		ctx.clip();
    -	}
    -
    -	//clear
    -	//canvas.width = canvas.width;
    -	if(this.clear_background)
    -		ctx.clearRect(0,0,canvas.width, canvas.height);
    -
    -	//draw bg canvas
    -	if(this.bgcanvas == this.canvas)
    -		this.drawBackCanvas();
    -	else
    -		ctx.drawImage(this.bgcanvas,0,0);
    -
    -	//rendering
    -	if(this.onRender)
    -		this.onRender(canvas, ctx);
    -
    -	//info widget
    -	if(this.show_info)
    -		this.renderInfo(ctx);
    -
    -	if(this.graph)
    -	{
    -		//apply transformations
    -		ctx.save();
    -		this.ds.toCanvasContext( ctx );
    -
    -		//draw nodes
    -		var drawn_nodes = 0;
    -		var visible_nodes = this.computeVisibleNodes( null, this.visible_nodes );
    -
    -		for (var i = 0; i < visible_nodes.length; ++i)
    -		{
    -			var node = visible_nodes[i];
    -
    -			//transform coords system
    -			ctx.save();
    -			ctx.translate( node.pos[0], node.pos[1] );
    -
    -			//Draw
    -			this.drawNode( node, ctx );
    -			drawn_nodes += 1;
    -
    -			//Restore
    -			ctx.restore();
    -		}
    -
    -		//on top (debug)
    -		if( this.render_execution_order)
    -			this.drawExecutionOrder(ctx);
    -
    -
    -		//connections ontop?
    -		if(this.graph.config.links_ontop)
    -			if(!this.live_mode)
    -				this.drawConnections(ctx);
    -
    -		//current connection (the one being dragged by the mouse)
    -		if(this.connecting_pos != null)
    -		{
    -			ctx.lineWidth = this.connections_width;
    -			var link_color = null;
    -
    -			switch( this.connecting_output.type )
    -			{
    -				case LiteGraph.EVENT: link_color = LiteGraph.EVENT_LINK_COLOR; break;
    -				default:
    -					link_color = LiteGraph.CONNECTING_LINK_COLOR;
    -			}
    -			
    -			//the connection being dragged by the mouse
    -			this.renderLink( ctx, this.connecting_pos, [ this.canvas_mouse[0], this.canvas_mouse[1] ], null, false, null, link_color, this.connecting_output.dir || (this.connecting_node.horizontal ? LiteGraph.DOWN : LiteGraph.RIGHT), LiteGraph.CENTER );
    -
    -			ctx.beginPath();
    -				if( this.connecting_output.type === LiteGraph.EVENT || this.connecting_output.shape === LiteGraph.BOX_SHAPE )
    -					ctx.rect( (this.connecting_pos[0] - 6) + 0.5, (this.connecting_pos[1] - 5) + 0.5,14,10);
    -				else
    -					ctx.arc( this.connecting_pos[0], this.connecting_pos[1],4,0,Math.PI*2);
    -			ctx.fill();
    -
    -			ctx.fillStyle = "#ffcc00";
    -			if(this._highlight_input)
    -			{
    -				ctx.beginPath();
    -					ctx.arc( this._highlight_input[0], this._highlight_input[1],6,0,Math.PI*2);
    -				ctx.fill();
    -			}
    -		}
    -
    -		if( this.dragging_rectangle )
    -		{
    -			ctx.strokeStyle = "#FFF";
    -			ctx.strokeRect( this.dragging_rectangle[0], this.dragging_rectangle[1], this.dragging_rectangle[2], this.dragging_rectangle[3] );
    -		}
    -
    -		if( this.onDrawForeground )
    -			this.onDrawForeground( ctx, this.visible_rect );
    -
    -		ctx.restore();
    -	}
    -
    -	if( this.onDrawOverlay )
    -		this.onDrawOverlay( ctx );
    -
    -	if(this.dirty_area)
    -	{
    -		ctx.restore();
    -		//this.dirty_area = null;
    -	}
    -
    -	if(ctx.finish2D) //this is a function I use in webgl renderer
    -		ctx.finish2D();
    -}
    -
    -/**
    -* draws some useful stats in the corner of the canvas
    -* @method renderInfo
    -**/
    -LGraphCanvas.prototype.renderInfo = function( ctx, x, y )
    -{
    -	x = x || 0;
    -	y = y || 0;
    -
    -	ctx.save();
    -	ctx.translate( x, y );
    -
    -	ctx.font = "10px Arial";
    -	ctx.fillStyle = "#888";
    -	if(this.graph)
    -	{
    -		ctx.fillText( "T: " + this.graph.globaltime.toFixed(2)+"s",5,13*1 );
    -		ctx.fillText( "I: " + this.graph.iteration,5,13*2 );
    -		ctx.fillText( "N: " + this.graph._nodes.length + " [" + this.visible_nodes.length + "]",5,13*3  );
    -		ctx.fillText( "V: " + this.graph._version,5,13*4 );
    -		ctx.fillText( "FPS:" + this.fps.toFixed(2),5,13*5 );
    -	}
    -	else
    -		ctx.fillText( "No graph selected",5,13*1 );
    -	ctx.restore();
    -}
    -
    -/**
    -* draws the back canvas (the one containing the background and the connections)
    -* @method drawBackCanvas
    -**/
    -LGraphCanvas.prototype.drawBackCanvas = function()
    -{
    -	var canvas = this.bgcanvas;
    -	if(canvas.width != this.canvas.width ||
    -		canvas.height != this.canvas.height)
    -	{
    -		canvas.width = this.canvas.width;
    -		canvas.height = this.canvas.height;
    -	}
    -
    -	if(!this.bgctx)
    -		this.bgctx = this.bgcanvas.getContext("2d");
    -	var ctx = this.bgctx;
    -	if(ctx.start)
    -		ctx.start();
    -
    -	//clear
    -	if(this.clear_background)
    -		ctx.clearRect(0,0,canvas.width, canvas.height);
    -
    -	if(this._graph_stack && this._graph_stack.length)
    -	{
    -		ctx.save();
    -		var parent_graph = this._graph_stack[ this._graph_stack.length - 1];
    -		var subgraph_node = this.graph._subgraph_node;
    -		ctx.strokeStyle = subgraph_node.bgcolor;
    -		ctx.lineWidth = 10;
    -		ctx.strokeRect(1,1,canvas.width-2,canvas.height-2);
    -		ctx.lineWidth = 1;
    -		ctx.font = "40px Arial"
    -		ctx.textAlign = "center";
    -		ctx.fillStyle = subgraph_node.bgcolor;
    -		var title = "";
    -		for(var i = 1; i < this._graph_stack.length; ++i)
    -			title += this._graph_stack[i]._subgraph_node.getTitle() + " >> ";
    -		ctx.fillText( title + subgraph_node.getTitle(), canvas.width * 0.5, 40 );
    -		ctx.restore();
    -	}
    -
    -	var bg_already_painted = false;
    -	if(this.onRenderBackground)
    -		bg_already_painted = this.onRenderBackground( canvas, ctx );
    -
    -	//reset in case of error
    -	ctx.restore();
    -	ctx.setTransform(1, 0, 0, 1, 0, 0);
    -	this.visible_links.length = 0;
    -
    -	if(this.graph)
    -	{
    -		//apply transformations
    -		ctx.save();
    -		this.ds.toCanvasContext(ctx);
    -
    -		//render BG
    -		if(this.background_image && this.ds.scale > 0.5 && !bg_already_painted)
    -		{
    -			if (this.zoom_modify_alpha)
    -				ctx.globalAlpha = (1.0 - 0.5 / this.ds.scale) * this.editor_alpha;
    -			else
    -				ctx.globalAlpha = this.editor_alpha;
    -			ctx.imageSmoothingEnabled = ctx.mozImageSmoothingEnabled = ctx.imageSmoothingEnabled = false;
    -			if(!this._bg_img || this._bg_img.name != this.background_image)
    -			{
    -				this._bg_img = new Image();
    -				this._bg_img.name = this.background_image;
    -				this._bg_img.src = this.background_image;
    -				var that = this;
    -				this._bg_img.onload = function() {
    -					that.draw(true,true);
    -				}
    -			}
    -
    -			var pattern = null;
    -			if(this._pattern == null && this._bg_img.width > 0)
    -			{
    -				pattern = ctx.createPattern( this._bg_img, 'repeat' );
    -				this._pattern_img = this._bg_img;
    -				this._pattern = pattern;
    -			}
    -			else
    -				pattern = this._pattern;
    -			if(pattern)
    -			{
    -				ctx.fillStyle = pattern;
    -				ctx.fillRect(this.visible_area[0],this.visible_area[1],this.visible_area[2],this.visible_area[3]);
    -				ctx.fillStyle = "transparent";
    -			}
    -
    -			ctx.globalAlpha = 1.0;
    -			ctx.imageSmoothingEnabled = ctx.mozImageSmoothingEnabled = ctx.imageSmoothingEnabled = true;
    -		}
    -
    -		//groups
    -		if(this.graph._groups.length && !this.live_mode)
    -			this.drawGroups(canvas, ctx);
    -
    -		if( this.onDrawBackground )
    -			this.onDrawBackground( ctx, this.visible_area );
    -		if( this.onBackgroundRender ) //LEGACY
    -		{
    -			console.error("WARNING! onBackgroundRender deprecated, now is named onDrawBackground ");
    -			this.onBackgroundRender = null;
    -		}
    -
    -		//DEBUG: show clipping area
    -		//ctx.fillStyle = "red";
    -		//ctx.fillRect( this.visible_area[0] + 10, this.visible_area[1] + 10, this.visible_area[2] - 20, this.visible_area[3] - 20);
    -
    -		//bg
    -		if (this.render_canvas_border) {
    -			ctx.strokeStyle = "#235";
    -			ctx.strokeRect(0,0,canvas.width,canvas.height);
    -		}
    -
    -		if(this.render_connections_shadows)
    -		{
    -			ctx.shadowColor = "#000";
    -			ctx.shadowOffsetX = 0;
    -			ctx.shadowOffsetY = 0;
    -			ctx.shadowBlur = 6;
    -		}
    -		else
    -			ctx.shadowColor = "rgba(0,0,0,0)";
    -
    -		//draw connections
    -		if(!this.live_mode)
    -			this.drawConnections(ctx);
    -
    -		ctx.shadowColor = "rgba(0,0,0,0)";
    -
    -		//restore state
    -		ctx.restore();
    -	}
    -
    -	if(ctx.finish)
    -		ctx.finish();
    -
    -	this.dirty_bgcanvas = false;
    -	this.dirty_canvas = true; //to force to repaint the front canvas with the bgcanvas
    -}
    -
    -var temp_vec2 = new Float32Array(2);
    -
    -/**
    -* draws the given node inside the canvas
    -* @method drawNode
    -**/
    -LGraphCanvas.prototype.drawNode = function(node, ctx )
    -{
    -	var glow = false;
    -	this.current_node = node;
    -
    -	var color = node.color || node.constructor.color || LiteGraph.NODE_DEFAULT_COLOR;
    -	var bgcolor = node.bgcolor || node.constructor.bgcolor || LiteGraph.NODE_DEFAULT_BGCOLOR;
    -
    -	//shadow and glow
    -	if (node.mouseOver)
    -		glow = true;
    -
    -	//only render if it forces it to do it
    -	if(this.live_mode)
    -	{
    -		if(!node.flags.collapsed)
    -		{
    -			ctx.shadowColor = "transparent";
    -			if(node.onDrawForeground)
    -				node.onDrawForeground(ctx, this, this.canvas );
    -		}
    -		return;
    -	}
    -
    -	var editor_alpha = this.editor_alpha;
    -	ctx.globalAlpha = editor_alpha;
    -
    -	if(this.render_shadows)
    -	{
    -		ctx.shadowColor = LiteGraph.DEFAULT_SHADOW_COLOR;
    -		ctx.shadowOffsetX = 2 * this.ds.scale;
    -		ctx.shadowOffsetY = 2 * this.ds.scale;
    -		ctx.shadowBlur = 3 * this.ds.scale;
    -	}
    -	else
    -		ctx.shadowColor = "transparent";
    -
    -	//custom draw collapsed method (draw after shadows because they are affected)
    -	if(node.flags.collapsed && node.onDrawCollaped && node.onDrawCollapsed(ctx, this) == true)
    -		return;
    -
    -	//clip if required (mask)
    -	var shape = node._shape || LiteGraph.BOX_SHAPE;
    -	var size = temp_vec2;
    -	temp_vec2.set( node.size );
    -	var horizontal = node.horizontal;// || node.flags.horizontal;
    -
    -	if( node.flags.collapsed )
    -	{
    -		ctx.font = this.inner_text_font;
    -		var title = node.getTitle ? node.getTitle() : node.title;
    -		if(title != null)
    -		{
    -			node._collapsed_width = Math.min( node.size[0], ctx.measureText(title).width + LiteGraph.NODE_TITLE_HEIGHT * 2 );//LiteGraph.NODE_COLLAPSED_WIDTH;
    -			size[0] = node._collapsed_width;
    -			size[1] = 0;
    -		}
    -	}
    -	
    -	if( node.clip_area ) //Start clipping
    -	{
    -		ctx.save();
    -		ctx.beginPath();
    -		if(shape == LiteGraph.BOX_SHAPE)
    -			ctx.rect(0,0,size[0], size[1]);
    -		else if (shape == LiteGraph.ROUND_SHAPE)
    -			ctx.roundRect(0,0,size[0], size[1],10);
    -		else if (shape == LiteGraph.CIRCLE_SHAPE)
    -			ctx.arc(size[0] * 0.5, size[1] * 0.5, size[0] * 0.5, 0, Math.PI*2);
    -		ctx.clip();
    -	}
    -
    -	//draw shape
    -	if( node.has_errors )
    -		bgcolor = "red";
    -	this.drawNodeShape( node, ctx, size, color, bgcolor, node.is_selected, node.mouseOver );
    -	ctx.shadowColor = "transparent";
    -
    -	//draw foreground
    -	if(node.onDrawForeground)
    -		node.onDrawForeground( ctx, this, this.canvas );
    -
    -	//connection slots
    -	ctx.textAlign = horizontal ? "center" : "left";
    -	ctx.font = this.inner_text_font;
    -
    -	var render_text = this.ds.scale > 0.6;
    -
    -	var out_slot = this.connecting_output;
    -	ctx.lineWidth = 1;
    -
    -	var max_y = 0;
    -	var slot_pos = new Float32Array(2); //to reuse
    -
    -	//render inputs and outputs
    -	if(!node.flags.collapsed)
    -	{
    -		//input connection slots
    -		if(node.inputs)
    -			for(var i = 0; i < node.inputs.length; i++)
    -			{
    -				var slot = node.inputs[i];
    -
    -				ctx.globalAlpha = editor_alpha;
    -				//change opacity of incompatible slots when dragging a connection
    -				if ( this.connecting_node && LiteGraph.isValidConnection( slot.type && out_slot.type ) )
    -					ctx.globalAlpha = 0.4 * editor_alpha;
    -
    -				ctx.fillStyle = slot.link != null ? (slot.color_on || this.default_connection_color.input_on) : (slot.color_off || this.default_connection_color.input_off);
    -
    -				var pos = node.getConnectionPos( true, i, slot_pos );
    -				pos[0] -= node.pos[0];
    -				pos[1] -= node.pos[1];
    -				if( max_y < pos[1] + LiteGraph.NODE_SLOT_HEIGHT*0.5 )
    -					max_y = pos[1] + LiteGraph.NODE_SLOT_HEIGHT*0.5;
    -
    -				ctx.beginPath();
    -
    -				if (slot.type === LiteGraph.EVENT || slot.shape === LiteGraph.BOX_SHAPE)
    -				{
    -					if (horizontal)
    -	                    ctx.rect((pos[0] - 5) + 0.5, (pos[1] - 8) + 0.5, 10, 14);
    -					else
    -	                    ctx.rect((pos[0] - 6) + 0.5, (pos[1] - 5) + 0.5, 14, 10);
    -                } else if (slot.shape === LiteGraph.ARROW_SHAPE) {
    -                    ctx.moveTo(pos[0] + 8, pos[1] + 0.5);
    -                    ctx.lineTo(pos[0] - 4, (pos[1] + 6) + 0.5);
    -                    ctx.lineTo(pos[0] - 4, (pos[1] - 6) + 0.5);
    -                    ctx.closePath();
    -                } else {
    -                    ctx.arc(pos[0], pos[1], 4, 0, Math.PI * 2);
    -                }
    -
    -				ctx.fill();
    -
    -				//render name
    -				if(render_text)
    -				{
    -					var text = slot.label != null ? slot.label : slot.name;
    -					if(text)
    -					{
    -						ctx.fillStyle = LiteGraph.NODE_TEXT_COLOR;
    -						if( horizontal || slot.dir == LiteGraph.UP )
    -							ctx.fillText(text,pos[0],pos[1] - 10);
    -						else
    -							ctx.fillText(text,pos[0] + 10,pos[1] + 5);
    -					}
    -				}
    -			}
    -
    -		//output connection slots
    -		if(this.connecting_node)
    -			ctx.globalAlpha = 0.4 * editor_alpha;
    -
    -		ctx.textAlign = horizontal ? "center" : "right";
    -		ctx.strokeStyle = "black";
    -		if(node.outputs)
    -			for(var i = 0; i < node.outputs.length; i++)
    -			{
    -				var slot = node.outputs[i];
    -
    -				var pos = node.getConnectionPos(false,i, slot_pos );
    -				pos[0] -= node.pos[0];
    -				pos[1] -= node.pos[1];
    -				if( max_y < pos[1] + LiteGraph.NODE_SLOT_HEIGHT*0.5)
    -					max_y = pos[1] + LiteGraph.NODE_SLOT_HEIGHT*0.5;
    -
    -				ctx.fillStyle = slot.links && slot.links.length ? (slot.color_on || this.default_connection_color.output_on) : (slot.color_off || this.default_connection_color.output_off);
    -				ctx.beginPath();
    -				//ctx.rect( node.size[0] - 14,i*14,10,10);
    -
    -				if (slot.type === LiteGraph.EVENT || slot.shape === LiteGraph.BOX_SHAPE)
    -				{
    -					if( horizontal )
    -						ctx.rect((pos[0] - 5) + 0.5,(pos[1] - 8) + 0.5,10,14);
    -					else
    -						ctx.rect((pos[0] - 6) + 0.5,(pos[1] - 5) + 0.5,14,10);
    -                } else if (slot.shape === LiteGraph.ARROW_SHAPE) {
    -                    ctx.moveTo(pos[0] + 8, pos[1] + 0.5);
    -                    ctx.lineTo(pos[0] - 4, (pos[1] + 6) + 0.5);
    -                    ctx.lineTo(pos[0] - 4, (pos[1] - 6) + 0.5);
    -                    ctx.closePath();
    -                } else {
    -                    ctx.arc(pos[0], pos[1], 4, 0, Math.PI * 2);
    -                }
    -
    -				//trigger
    -				//if(slot.node_id != null && slot.slot == -1)
    -				//	ctx.fillStyle = "#F85";
    -
    -				//if(slot.links != null && slot.links.length)
    -				ctx.fill();
    -				ctx.stroke();
    -
    -				//render output name
    -				if(render_text)
    -				{
    -					var text = slot.label != null ? slot.label : slot.name;
    -					if(text)
    -					{
    -						ctx.fillStyle = LiteGraph.NODE_TEXT_COLOR;
    -						if( horizontal || slot.dir == LiteGraph.DOWN )
    -							ctx.fillText(text,pos[0],pos[1] - 8);
    -						else
    -							ctx.fillText(text, pos[0] - 10,pos[1] + 5);
    -					}
    -				}
    -			}
    -
    -		ctx.textAlign = "left";
    -		ctx.globalAlpha = 1;
    -
    -		if(node.widgets)
    -		{
    -			if( horizontal || node.widgets_up  )
    -				max_y = 2;
    -			this.drawNodeWidgets( node, max_y, ctx, (this.node_widget && this.node_widget[0] == node) ? this.node_widget[1] : null );
    -		}
    -	}
    -	else if(this.render_collapsed_slots)//if collapsed
    -	{
    -		var input_slot = null;
    -		var output_slot = null;
    -
    -		//get first connected slot to render
    -		if(node.inputs)
    -		{
    -			for(var i = 0; i < node.inputs.length; i++)
    -			{
    -				var slot = node.inputs[i];
    -				if( slot.link == null )
    -					continue;
    -				input_slot = slot;
    -				break;
    -			}
    -		}
    -		if(node.outputs)
    -		{
    -			for(var i = 0; i < node.outputs.length; i++)
    -			{
    -				var slot = node.outputs[i];
    -				if(!slot.links || !slot.links.length)
    -					continue;
    -				output_slot = slot;
    -			}
    -		}
    -
    -		if(input_slot)
    -		{
    -			var x = 0;
    -			var y = LiteGraph.NODE_TITLE_HEIGHT * -0.5; //center
    -			if( horizontal )
    -			{
    -				x = node._collapsed_width * 0.5;
    -				y = -LiteGraph.NODE_TITLE_HEIGHT;		
    -			}
    -			ctx.fillStyle = "#686";
    -			ctx.beginPath();
    -			if ( slot.type === LiteGraph.EVENT || slot.shape === LiteGraph.BOX_SHAPE) {
    -				ctx.rect(x - 7 + 0.5, y-4,14,8);
    -			} else if (slot.shape === LiteGraph.ARROW_SHAPE) {
    -				ctx.moveTo(x + 8, y);
    -				ctx.lineTo(x + -4, y - 4);
    -				ctx.lineTo(x + -4, y + 4);
    -				ctx.closePath();
    -			} else {
    -				ctx.arc(x, y, 4, 0, Math.PI * 2);
    -			}
    -			ctx.fill();
    -		}
    -
    -		if(output_slot)
    -		{
    -			var x = node._collapsed_width;
    -			var y = LiteGraph.NODE_TITLE_HEIGHT * -0.5; //center
    -			if( horizontal )
    -			{
    -				x = node._collapsed_width * 0.5;
    -				y = 0;
    -			}
    -			ctx.fillStyle = "#686";
    -			ctx.strokeStyle = "black";
    -			ctx.beginPath();
    -			if (slot.type === LiteGraph.EVENT || slot.shape === LiteGraph.BOX_SHAPE) {
    -				ctx.rect( x - 7 + 0.5, y - 4,14,8);
    -			} else if (slot.shape === LiteGraph.ARROW_SHAPE) {
    -				ctx.moveTo(x + 6, y);
    -				ctx.lineTo(x - 6, y - 4);
    -				ctx.lineTo(x - 6, y + 4);
    -				ctx.closePath();
    -			} else {
    -				ctx.arc(x, y, 4, 0, Math.PI * 2);
    -			}
    -			ctx.fill();
    -			//ctx.stroke();
    -		}
    -	}
    -
    -	if(node.clip_area)
    -		ctx.restore();
    -
    -	ctx.globalAlpha = 1.0;
    -}
    -
    -/**
    -* draws the shape of the given node in the canvas
    -* @method drawNodeShape
    -**/
    -var tmp_area = new Float32Array(4);
    -
    -LGraphCanvas.prototype.drawNodeShape = function( node, ctx, size, fgcolor, bgcolor, selected, mouse_over )
    -{
    -	//bg rect
    -	ctx.strokeStyle = fgcolor;
    -	ctx.fillStyle = bgcolor;
    -
    -	var title_height = LiteGraph.NODE_TITLE_HEIGHT;
    -	var low_quality = this.ds.scale < 0.5;
    -
    -	//render node area depending on shape
    -	var shape = node._shape || node.constructor.shape || LiteGraph.ROUND_SHAPE;
    -
    -	var title_mode = node.constructor.title_mode;
    -
    -	var render_title = true;
    -	if( title_mode == LiteGraph.TRANSPARENT_TITLE )
    -		render_title = false;
    -	else if( title_mode == LiteGraph.AUTOHIDE_TITLE && mouse_over)
    -		render_title = true;
    -
    -	var area = tmp_area;
    -	area[0] = 0; //x
    -	area[1] = render_title ? -title_height : 0; //y
    -	area[2] = size[0]+1; //w
    -	area[3] = render_title ? size[1] + title_height : size[1]; //h
    -
    -	var old_alpha = ctx.globalAlpha;
    -
    -	//full node shape
    -	//if(node.flags.collapsed)
    -	{
    -		ctx.beginPath();
    -		if(shape == LiteGraph.BOX_SHAPE || low_quality )
    -			ctx.fillRect( area[0], area[1], area[2], area[3] );
    -		else if (shape == LiteGraph.ROUND_SHAPE || shape == LiteGraph.CARD_SHAPE)
    -			ctx.roundRect( area[0], area[1], area[2], area[3], this.round_radius, shape == LiteGraph.CARD_SHAPE ? 0 : this.round_radius);
    -		else if (shape == LiteGraph.CIRCLE_SHAPE)
    -			ctx.arc(size[0] * 0.5, size[1] * 0.5, size[0] * 0.5, 0, Math.PI*2);
    -		ctx.fill();
    -
    -		ctx.shadowColor = "transparent";
    -		ctx.fillStyle = "rgba(0,0,0,0.2)";
    -		ctx.fillRect(0,-1, area[2],2);
    -	}
    -	ctx.shadowColor = "transparent";
    -
    -	if( node.onDrawBackground )
    -		node.onDrawBackground( ctx, this, this.canvas );
    -
    -	//title bg (remember, it is rendered ABOVE the node)
    -	if( render_title || title_mode == LiteGraph.TRANSPARENT_TITLE )
    -	{
    -		//title bar
    -		if(node.onDrawTitleBar)
    -		{
    -			node.onDrawTitleBar(ctx, title_height, size, this.ds.scale, fgcolor);
    -		}
    -		else if(title_mode != LiteGraph.TRANSPARENT_TITLE && (node.constructor.title_color || this.render_title_colored ))
    -		{
    -			var title_color = node.constructor.title_color || fgcolor;
    -
    -			if(node.flags.collapsed)
    -				ctx.shadowColor = LiteGraph.DEFAULT_SHADOW_COLOR;
    -	
    -			//* gradient test
    -			if(this.use_gradients)
    -			{
    -				var grad = LGraphCanvas.gradients[ title_color ];
    -				if(!grad)
    -				{
    -					grad = LGraphCanvas.gradients[ title_color ] = ctx.createLinearGradient(0,0,400,0);
    -					grad.addColorStop(0, title_color);
    -					grad.addColorStop(1, "#000");
    -				}
    -				ctx.fillStyle = grad;
    -			}
    -			else
    -				ctx.fillStyle = title_color;
    -
    -			//ctx.globalAlpha = 0.5 * old_alpha;
    -			ctx.beginPath();
    -			if( shape == LiteGraph.BOX_SHAPE || low_quality )
    -				ctx.rect(0, -title_height, size[0]+1, title_height);
    -			else if ( shape == LiteGraph.ROUND_SHAPE || shape == LiteGraph.CARD_SHAPE )
    -				ctx.roundRect(0,-title_height,size[0]+1, title_height, this.round_radius, node.flags.collapsed ? this.round_radius : 0);
    -			ctx.fill();
    -			ctx.shadowColor = "transparent";
    -		}
    -
    -		//title box
    -		var box_size = 10;
    -		if(node.onDrawTitleBox)
    -		{
    -			node.onDrawTitleBox( ctx, title_height, size, this.ds.scale );
    -		}
    -		else if ( shape == LiteGraph.ROUND_SHAPE || shape == LiteGraph.CIRCLE_SHAPE || shape == LiteGraph.CARD_SHAPE )
    -		{
    -			if( low_quality )
    -			{
    -				ctx.fillStyle = "black";
    -				ctx.beginPath();
    -				ctx.arc(title_height * 0.5, title_height * -0.5, box_size*0.5+1,0,Math.PI*2);
    -				ctx.fill();
    -			}
    -
    -			ctx.fillStyle = node.boxcolor || LiteGraph.NODE_DEFAULT_BOXCOLOR;
    -			ctx.beginPath();
    -			ctx.arc(title_height * 0.5, title_height * -0.5, box_size*0.5,0,Math.PI*2);
    -			ctx.fill();
    -		}
    -		else
    -		{
    -			if( low_quality )
    -			{
    -				ctx.fillStyle = "black";
    -				ctx.fillRect( (title_height - box_size) * 0.5 - 1, (title_height + box_size ) * -0.5 - 1, box_size + 2, box_size + 2);
    -			}
    -			ctx.fillStyle = node.boxcolor || LiteGraph.NODE_DEFAULT_BOXCOLOR;
    -			ctx.fillRect( (title_height - box_size) * 0.5, (title_height + box_size ) * -0.5, box_size, box_size );
    -		}
    -		ctx.globalAlpha = old_alpha;
    -
    -		//title text
    -		if(node.onDrawTitleText)
    -		{
    -			node.onDrawTitleText( ctx, title_height, size, this.ds.scale, this.title_text_font, selected);
    -		}
    -		if( !low_quality )
    -		{
    -			ctx.font = this.title_text_font;
    -			var title = node.getTitle();
    -			if(title)
    -			{
    -				if(selected)
    -					ctx.fillStyle = "white";
    -				else
    -					ctx.fillStyle = node.constructor.title_text_color || this.node_title_color;
    -				if( node.flags.collapsed )
    -				{
    -					ctx.textAlign =  "center";
    -					var measure = ctx.measureText(title);
    -					ctx.fillText( title, title_height + measure.width * 0.5, LiteGraph.NODE_TITLE_TEXT_Y - title_height );
    -					ctx.textAlign =  "left";
    -				}
    -				else
    -				{
    -					ctx.textAlign =  "left";
    -					ctx.fillText( title, title_height, LiteGraph.NODE_TITLE_TEXT_Y - title_height );
    -				}
    -			}
    -		}
    -
    -		if(node.onDrawTitle)
    -			node.onDrawTitle(ctx);
    -	}
    -
    -	//render selection marker
    -	if(selected)
    -	{
    -		if( node.onBounding )
    -			node.onBounding( area );
    -
    -		if( title_mode == LiteGraph.TRANSPARENT_TITLE )
    -		{
    -			area[1] -= title_height;
    -			area[3] += title_height;
    -		}
    -		ctx.lineWidth = 1;
    -		ctx.globalAlpha = 0.8;
    -		ctx.beginPath();
    -		if( shape == LiteGraph.BOX_SHAPE )
    -			ctx.rect(-6 + area[0],-6 + area[1], 12 + area[2], 12 + area[3] );
    -		else if (shape == LiteGraph.ROUND_SHAPE || (shape == LiteGraph.CARD_SHAPE && node.flags.collapsed) )
    -			ctx.roundRect(-6 + area[0],-6 + area[1], 12 + area[2], 12 + area[3] , this.round_radius * 2);
    -		else if (shape == LiteGraph.CARD_SHAPE)
    -			ctx.roundRect(-6 + area[0],-6 + area[1], 12 + area[2], 12 + area[3] , this.round_radius * 2, 2);
    -		else if (shape == LiteGraph.CIRCLE_SHAPE)
    -			ctx.arc(size[0] * 0.5, size[1] * 0.5, size[0] * 0.5 + 6, 0, Math.PI*2);
    -		ctx.strokeStyle = "#FFF";
    -		ctx.stroke();
    -		ctx.strokeStyle = fgcolor;
    -		ctx.globalAlpha = 1;
    -	}
    -}
    -
    -var margin_area = new Float32Array(4);
    -var link_bounding = new Float32Array(4);
    -var tempA = new Float32Array(2);
    -var tempB = new Float32Array(2);
    -
    -/**
    -* draws every connection visible in the canvas
    -* OPTIMIZE THIS: precatch connections position instead of recomputing them every time
    -* @method drawConnections
    -**/
    -LGraphCanvas.prototype.drawConnections = function(ctx)
    -{
    -	var now = LiteGraph.getTime();
    -	var visible_area = this.visible_area;
    -	margin_area[0] = visible_area[0] - 20; margin_area[1] = visible_area[1] - 20; margin_area[2] = visible_area[2] + 40; margin_area[3] = visible_area[3] + 40;
    -
    -	//draw connections
    -	ctx.lineWidth = this.connections_width;
    -
    -	ctx.fillStyle = "#AAA";
    -	ctx.strokeStyle = "#AAA";
    -	ctx.globalAlpha = this.editor_alpha;
    -	//for every node
    -	var nodes = this.graph._nodes;
    -	for (var n = 0, l = nodes.length; n < l; ++n)
    -	{
    -		var node = nodes[n];
    -		//for every input (we render just inputs because it is easier as every slot can only have one input)
    -		if(!node.inputs || !node.inputs.length)
    -			continue;
    -	
    -		for(var i = 0; i < node.inputs.length; ++i)
    -		{
    -			var input = node.inputs[i];
    -			if(!input || input.link == null)
    -				continue;
    -			var link_id = input.link;
    -			var link = this.graph.links[ link_id ];
    -			if(!link)
    -				continue;
    -
    -			//find link info
    -			var start_node = this.graph.getNodeById( link.origin_id );
    -			if(start_node == null) continue;
    -			var start_node_slot = link.origin_slot;
    -			var start_node_slotpos = null;
    -			if(start_node_slot == -1)
    -				start_node_slotpos = [start_node.pos[0] + 10, start_node.pos[1] + 10];
    -			else
    -				start_node_slotpos = start_node.getConnectionPos( false, start_node_slot, tempA );
    -			var end_node_slotpos = node.getConnectionPos( true, i, tempB );
    -
    -			//compute link bounding
    -			link_bounding[0] = start_node_slotpos[0];
    -			link_bounding[1] = start_node_slotpos[1];
    -			link_bounding[2] = end_node_slotpos[0] - start_node_slotpos[0];
    -			link_bounding[3] = end_node_slotpos[1] - start_node_slotpos[1];
    -			if( link_bounding[2] < 0 ){
    -				link_bounding[0] += link_bounding[2];
    -				link_bounding[2] = Math.abs( link_bounding[2] );
    -			}
    -			if( link_bounding[3] < 0 ){
    -				link_bounding[1] += link_bounding[3];
    -				link_bounding[3] = Math.abs( link_bounding[3] );
    -			}
    -
    -			//skip links outside of the visible area of the canvas
    -			if( !overlapBounding( link_bounding, margin_area ) )
    -				continue;
    -
    -			var start_slot = start_node.outputs[ start_node_slot ];
    -			var end_slot = node.inputs[i];
    -			if(!start_slot || !end_slot) continue;
    -			var start_dir = start_slot.dir || (start_node.horizontal ? LiteGraph.DOWN : LiteGraph.RIGHT);
    -			var end_dir = end_slot.dir || (node.horizontal ? LiteGraph.UP : LiteGraph.LEFT);
    -
    -			this.renderLink( ctx, start_node_slotpos, end_node_slotpos, link, false, 0, null, start_dir, end_dir );
    -
    -			//event triggered rendered on top
    -			if(link && link._last_time && (now - link._last_time) < 1000 )
    -			{
    -				var f = 2.0 - (now - link._last_time) * 0.002;
    -				var tmp = ctx.globalAlpha;
    -				ctx.globalAlpha = tmp * f;
    -				this.renderLink( ctx, start_node_slotpos, end_node_slotpos, link, true, f, "white", start_dir, end_dir );
    -				ctx.globalAlpha = tmp;
    -			}
    -		}
    -	}
    -	ctx.globalAlpha = 1;
    -}
    -
    -/**
    -* draws a link between two points
    -* @method renderLink
    -* @param {vec2} a start pos
    -* @param {vec2} b end pos
    -* @param {Object} link the link object with all the link info
    -* @param {boolean} skip_border ignore the shadow of the link
    -* @param {boolean} flow show flow animation (for events)
    -* @param {string} color the color for the link
    -* @param {number} start_dir the direction enum 
    -* @param {number} end_dir the direction enum 
    -* @param {number} num_sublines number of sublines (useful to represent vec3 or rgb)
    -**/
    -LGraphCanvas.prototype.renderLink = function( ctx, a, b, link, skip_border, flow, color, start_dir, end_dir, num_sublines )
    -{
    -	if(link)
    -		this.visible_links.push( link );
    -
    -	//choose color
    -	if( !color && link )
    -		color = link.color || LGraphCanvas.link_type_colors[ link.type ];
    -	if( !color )
    -		color = this.default_link_color;
    -	if( link != null && this.highlighted_links[ link.id ] )
    -		color = "#FFF";
    -
    -	start_dir = start_dir || LiteGraph.RIGHT;
    -	end_dir = end_dir || LiteGraph.LEFT;
    -
    -	var dist = distance(a,b);
    -
    -	if(this.render_connections_border && this.ds.scale > 0.6)
    -		ctx.lineWidth = this.connections_width + 4;
    -	ctx.lineJoin = "round";
    -	num_sublines = num_sublines || 1;
    -	if(num_sublines > 1)
    -		ctx.lineWidth = 0.5;
    -
    -	//begin line shape
    -	ctx.beginPath();
    -	for(var i = 0; i < num_sublines; i += 1)
    -	{
    -		var offsety = (i - (num_sublines-1)*0.5)*5;
    -
    -		if(this.links_render_mode == LiteGraph.SPLINE_LINK)
    -		{
    -			ctx.moveTo(a[0],a[1] + offsety);
    -			var start_offset_x = 0;
    -			var start_offset_y = 0;
    -			var end_offset_x = 0;
    -			var end_offset_y = 0;
    -			switch(start_dir)
    -			{
    -				case LiteGraph.LEFT: start_offset_x = dist*-0.25; break;
    -				case LiteGraph.RIGHT: start_offset_x = dist*0.25; break;
    -				case LiteGraph.UP: start_offset_y = dist*-0.25; break;
    -				case LiteGraph.DOWN: start_offset_y = dist*0.25; break;
    -			}
    -			switch(end_dir)
    -			{
    -				case LiteGraph.LEFT: end_offset_x = dist*-0.25; break;
    -				case LiteGraph.RIGHT: end_offset_x = dist*0.25; break;
    -				case LiteGraph.UP: end_offset_y = dist*-0.25; break;
    -				case LiteGraph.DOWN: end_offset_y = dist*0.25; break;
    -			}
    -			ctx.bezierCurveTo(a[0] + start_offset_x, a[1] + start_offset_y + offsety,
    -								b[0] + end_offset_x , b[1] + end_offset_y + offsety,
    -								b[0], b[1] + offsety);
    -		}
    -		else if(this.links_render_mode == LiteGraph.LINEAR_LINK)
    -		{
    -			ctx.moveTo(a[0],a[1] + offsety);
    -			var start_offset_x = 0;
    -			var start_offset_y = 0;
    -			var end_offset_x = 0;
    -			var end_offset_y = 0;
    -			switch(start_dir)
    -			{
    -				case LiteGraph.LEFT: start_offset_x = -1; break;
    -				case LiteGraph.RIGHT: start_offset_x = 1; break;
    -				case LiteGraph.UP: start_offset_y = -1; break;
    -				case LiteGraph.DOWN: start_offset_y = 1; break;
    -			}
    -			switch(end_dir)
    -			{
    -				case LiteGraph.LEFT: end_offset_x = -1; break;
    -				case LiteGraph.RIGHT: end_offset_x = 1; break;
    -				case LiteGraph.UP: end_offset_y = -1; break;
    -				case LiteGraph.DOWN: end_offset_y = 1; break;
    -			}
    -			var l = 15;
    -			ctx.lineTo(a[0] + start_offset_x * l, a[1] + start_offset_y * l + offsety);
    -			ctx.lineTo(b[0] + end_offset_x * l, b[1] + end_offset_y * l + offsety);
    -			ctx.lineTo(b[0],b[1] + offsety);
    -		}
    -		else if(this.links_render_mode == LiteGraph.STRAIGHT_LINK)
    -		{
    -			ctx.moveTo(a[0], a[1]);
    -			var start_x = a[0];
    -			var start_y = a[1];
    -			var end_x = b[0];
    -			var end_y = b[1];
    -			if( start_dir == LiteGraph.RIGHT )
    -				start_x += 10;
    -			else
    -				start_y += 10;
    -			if( end_dir == LiteGraph.LEFT )
    -				end_x -= 10;
    -			else
    -				end_y -= 10;
    -			ctx.lineTo(start_x, start_y);
    -			ctx.lineTo((start_x + end_x)*0.5,start_y);
    -			ctx.lineTo((start_x + end_x)*0.5,end_y);
    -			ctx.lineTo(end_x, end_y);
    -			ctx.lineTo(b[0],b[1]);
    -		}
    -		else
    -			return; //unknown
    -	}
    -
    -	//rendering the outline of the connection can be a little bit slow
    -	if(this.render_connections_border && this.ds.scale > 0.6 && !skip_border)
    -	{
    -		ctx.strokeStyle = "rgba(0,0,0,0.5)";
    -		ctx.stroke();
    -	}
    -
    -	ctx.lineWidth = this.connections_width;
    -	ctx.fillStyle = ctx.strokeStyle = color;
    -	ctx.stroke();
    -	//end line shape
    -
    -	var pos = this.computeConnectionPoint( a, b, 0.5, start_dir, end_dir );
    -	if(link && link._pos)
    -	{
    -		link._pos[0] = pos[0];
    -		link._pos[1] = pos[1];
    -	}
    -
    -	//render arrow in the middle
    -	if( this.ds.scale >= 0.6 && this.highquality_render && end_dir != LiteGraph.CENTER )
    -	{
    -		//render arrow
    -		if( this.render_connection_arrows )
    -		{
    -			//compute two points in the connection
    -			var posA = this.computeConnectionPoint( a, b, 0.25, start_dir, end_dir );
    -			var posB = this.computeConnectionPoint( a, b, 0.26, start_dir, end_dir );
    -			var posC = this.computeConnectionPoint( a, b, 0.75, start_dir, end_dir );
    -			var posD = this.computeConnectionPoint( a, b, 0.76, start_dir, end_dir );
    -
    -			//compute the angle between them so the arrow points in the right direction
    -			var angleA = 0;
    -			var angleB = 0;
    -			if(this.render_curved_connections)
    -			{
    -				angleA = -Math.atan2( posB[0] - posA[0], posB[1] - posA[1]);
    -				angleB = -Math.atan2( posD[0] - posC[0], posD[1] - posC[1]);
    -			}
    -			else
    -				angleB = angleA = b[1] > a[1] ? 0 : Math.PI;
    -
    -			//render arrow
    -			ctx.save();
    -			ctx.translate(posA[0],posA[1]);
    -			ctx.rotate(angleA);
    -			ctx.beginPath();
    -			ctx.moveTo(-5,-3);
    -			ctx.lineTo(0,+7);
    -			ctx.lineTo(+5,-3);
    -			ctx.fill();
    -			ctx.restore();
    -			ctx.save();
    -			ctx.translate(posC[0],posC[1]);
    -			ctx.rotate(angleB);
    -			ctx.beginPath();
    -			ctx.moveTo(-5,-3);
    -			ctx.lineTo(0,+7);
    -			ctx.lineTo(+5,-3);
    -			ctx.fill();
    -			ctx.restore();
    -		}
    -
    -		//circle
    -		ctx.beginPath();
    -		ctx.arc(pos[0],pos[1],5,0,Math.PI*2);
    -		ctx.fill();
    -	}
    -
    -	//render flowing points
    -	if(flow)
    -	{
    -		ctx.fillStyle = color;
    -		for(var i = 0; i < 5; ++i)
    -		{
    -			var f = (LiteGraph.getTime() * 0.001 + (i * 0.2)) % 1;
    -			var pos = this.computeConnectionPoint(a,b,f, start_dir, end_dir);
    -			ctx.beginPath();
    -			ctx.arc(pos[0],pos[1],5,0,2*Math.PI);
    -			ctx.fill();
    -		}
    -	}
    -}
    -
    -//returns the link center point based on curvature
    -LGraphCanvas.prototype.computeConnectionPoint = function(a,b,t,start_dir,end_dir)
    -{
    -	start_dir = start_dir || LiteGraph.RIGHT;
    -	end_dir = end_dir || LiteGraph.LEFT;
    -
    -	var dist = distance(a,b);
    -	var p0 = a;
    -	var p1 = [ a[0], a[1] ];
    -	var p2 = [ b[0], b[1] ];
    -	var p3 = b;
    -
    -	switch(start_dir)
    -	{
    -		case LiteGraph.LEFT: p1[0] += dist*-0.25; break;
    -		case LiteGraph.RIGHT: p1[0] += dist*0.25; break;
    -		case LiteGraph.UP: p1[1] += dist*-0.25; break;
    -		case LiteGraph.DOWN: p1[1] += dist*0.25; break;
    -	}
    -	switch(end_dir)
    -	{
    -		case LiteGraph.LEFT: p2[0] += dist*-0.25; break;
    -		case LiteGraph.RIGHT: p2[0] += dist*0.25; break;
    -		case LiteGraph.UP: p2[1] += dist*-0.25; break;
    -		case LiteGraph.DOWN: p2[1] += dist*0.25; break;
    -	}
    -
    -	var c1 = (1-t)*(1-t)*(1-t);
    -	var c2 = 3*((1-t)*(1-t))*t;
    -	var c3 = 3*(1-t)*(t*t);
    -	var c4 = t*t*t;
    -
    -	var x = c1*p0[0] + c2*p1[0] + c3*p2[0] + c4*p3[0];
    -	var y = c1*p0[1] + c2*p1[1] + c3*p2[1] + c4*p3[1];
    -	return [x,y];
    -}
    -
    -LGraphCanvas.prototype.drawExecutionOrder = function(ctx)
    -{
    -	ctx.shadowColor = "transparent";
    -	ctx.globalAlpha = 0.25;
    -
    -	ctx.textAlign = "center";
    -	ctx.strokeStyle = "white";
    -	ctx.globalAlpha = 0.75;
    -
    -	var visible_nodes = this.visible_nodes;
    -	for (var i = 0; i < visible_nodes.length; ++i)
    -	{
    -		var node = visible_nodes[i];
    -		ctx.fillStyle = "black";
    -		ctx.fillRect( node.pos[0] - LiteGraph.NODE_TITLE_HEIGHT, node.pos[1] - LiteGraph.NODE_TITLE_HEIGHT, LiteGraph.NODE_TITLE_HEIGHT, LiteGraph.NODE_TITLE_HEIGHT );
    -		if(node.order == 0)
    -			ctx.strokeRect( node.pos[0] - LiteGraph.NODE_TITLE_HEIGHT + 0.5, node.pos[1] - LiteGraph.NODE_TITLE_HEIGHT + 0.5, LiteGraph.NODE_TITLE_HEIGHT, LiteGraph.NODE_TITLE_HEIGHT );
    -		ctx.fillStyle = "#FFF";
    -		ctx.fillText( node.order, node.pos[0] + LiteGraph.NODE_TITLE_HEIGHT * -0.5, node.pos[1] - 6 );
    -	}
    -	ctx.globalAlpha = 1;
    -}
    -
    -
    -/**
    -* draws the widgets stored inside a node
    -* @method drawNodeWidgets
    -**/
    -LGraphCanvas.prototype.drawNodeWidgets = function( node, posY, ctx, active_widget )
    -{
    -	if(!node.widgets || !node.widgets.length)
    -		return 0;
    -	var width = node.size[0];
    -	var widgets = node.widgets;
    -	posY += 2;
    -	var H = LiteGraph.NODE_WIDGET_HEIGHT;
    -	var show_text = this.ds.scale > 0.5;
    -	ctx.save();
    -	ctx.globalAlpha = this.editor_alpha;
    -	var outline_color = "#666";
    -	var background_color = "#222";
    -	var margin = 15;
    -
    -	for(var i = 0; i < widgets.length; ++i)
    -	{
    -		var w = widgets[i];
    -		var y = posY;
    -		if(w.y)
    -			y = w.y;
    -		w.last_y = y;
    -		ctx.strokeStyle = outline_color;
    -		ctx.fillStyle = "#222";
    -		ctx.textAlign = "left";
    -
    -		switch( w.type )
    -		{
    -			case "button": 
    -				if(w.clicked)
    -				{
    -					ctx.fillStyle = "#AAA";
    -					w.clicked = false;
    -					this.dirty_canvas = true;
    -				}
    -				ctx.fillRect(margin,y,width-margin*2,H);
    -				ctx.strokeRect(margin,y,width-margin*2,H);
    -				if(show_text)
    -				{
    -					ctx.textAlign = "center";
    -					ctx.fillStyle = "#AAA";
    -					ctx.fillText( w.name, width*0.5, y + H*0.7 );
    -				}
    -				break;
    -			case "toggle":
    -				ctx.textAlign = "left";
    -				ctx.strokeStyle = outline_color;
    -				ctx.fillStyle = background_color;
    -				ctx.beginPath();
    -				ctx.roundRect( margin, posY, width - margin*2, H,H*0.5 );
    -				ctx.fill();
    -				ctx.stroke();
    -				ctx.fillStyle = w.value ? "#89A" : "#333";
    -				ctx.beginPath();
    -				ctx.arc( width - margin*2, y + H*0.5, H * 0.36, 0, Math.PI * 2 );
    -				ctx.fill();
    -				if(show_text)
    -				{
    -					ctx.fillStyle = "#999";
    -					if(w.name != null)
    -						ctx.fillText( w.name, margin*2, y + H*0.7 );
    -					ctx.fillStyle = w.value ? "#DDD" : "#888";
    -					ctx.textAlign = "right";
    -					ctx.fillText( w.value ? (w.options.on || "true") : (w.options.off || "false"), width - 40, y + H*0.7 );
    -				}
    -				break;
    -			case "slider": 
    -				ctx.fillStyle = background_color;
    -				ctx.fillRect(margin,y,width-margin*2,H);
    -				var range = w.options.max - w.options.min;
    -				var nvalue = (w.value - w.options.min) / range;
    -				ctx.fillStyle = active_widget == w ? "#89A" : "#678";
    -				ctx.fillRect(margin,y,nvalue*(width-margin*2),H);
    -				ctx.strokeRect(margin,y,width-margin*2,H);
    -				if( w.marker )
    -				{
    -					var marker_nvalue = (w.marker - w.options.min) / range;
    -					ctx.fillStyle = "#AA9";
    -					ctx.fillRect(margin + marker_nvalue*(width-margin*2),y,2,H);
    -				}
    -				if(show_text)
    -				{
    -					ctx.textAlign = "center";
    -					ctx.fillStyle = "#DDD";
    -					ctx.fillText( w.name + "  " + Number(w.value).toFixed(3), width*0.5, y + H*0.7 );
    -				}
    -				break;
    -			case "number":
    -			case "combo":
    -				ctx.textAlign = "left";
    -				ctx.strokeStyle = outline_color;
    -				ctx.fillStyle = background_color;
    -				ctx.beginPath();
    -				ctx.roundRect( margin, posY, width - margin*2, H,H*0.5 );
    -				ctx.fill();
    -				ctx.stroke();
    -				if(show_text)
    -				{
    -					ctx.fillStyle = "#AAA";
    -					ctx.beginPath();
    -					ctx.moveTo( margin + 16, posY + 5 );
    -					ctx.lineTo( margin + 6, posY + H*0.5 );
    -					ctx.lineTo( margin + 16, posY + H - 5 );
    -					ctx.moveTo( width - margin - 16, posY + 5 );
    -					ctx.lineTo( width - margin - 6, posY + H*0.5 );
    -					ctx.lineTo( width - margin - 16, posY + H - 5 );
    -					ctx.fill();
    -					ctx.fillStyle = "#999";
    -					ctx.fillText( w.name,  margin*2 + 5, y + H*0.7 );
    -					ctx.fillStyle = "#DDD";
    -					ctx.textAlign = "right";
    -					if(w.type == "number")
    -						ctx.fillText( Number(w.value).toFixed( w.options.precision !== undefined ? w.options.precision : 3), width - margin*2 - 20, y + H*0.7 );
    -					else
    -						ctx.fillText( w.value, width - margin*2 - 20, y + H*0.7 );
    -				}
    -				break;
    -			case "string":
    -			case "text":
    -				ctx.textAlign = "left";
    -				ctx.strokeStyle = outline_color;
    -				ctx.fillStyle = background_color;
    -				ctx.beginPath();
    -				ctx.roundRect( margin, posY, width - margin*2, H,H*0.5 );
    -				ctx.fill();
    -				ctx.stroke();
    -				if(show_text)
    -				{
    -					ctx.fillStyle = "#999";
    -					if(w.name != null)
    -						ctx.fillText( w.name, margin*2, y + H*0.7 );
    -					ctx.fillStyle = "#DDD";
    -					ctx.textAlign = "right";
    -					ctx.fillText( w.value, width - margin*2, y + H*0.7 );
    -				}
    -				break;
    -			default:
    -				if(w.draw)
    -					w.draw(ctx,node,w,y,H);
    -				break;
    -		}
    -		posY += H + 4;
    -	}
    -	ctx.restore();
    -}
    -
    -/**
    -* process an event on widgets 
    -* @method processNodeWidgets
    -**/
    -LGraphCanvas.prototype.processNodeWidgets = function( node, pos, event, active_widget )
    -{
    -	if(!node.widgets || !node.widgets.length)
    -		return null;
    -
    -	var x = pos[0] - node.pos[0];
    -	var y = pos[1] - node.pos[1];
    -	var width = node.size[0];
    -	var that = this;
    -	var ref_window = this.getCanvasWindow();
    -
    -	for(var i = 0; i < node.widgets.length; ++i)
    -	{
    -		var w = node.widgets[i];
    -		if( w == active_widget || (x > 6 && x < (width - 12) && y > w.last_y && y < (w.last_y + LiteGraph.NODE_WIDGET_HEIGHT)) )
    -		{
    -			//inside widget
    -			switch( w.type )
    -			{
    -				case "button": 
    -					if(w.callback)
    -						setTimeout( function(){	w.callback( w, that, node, pos ); }, 20 );
    -					w.clicked = true;
    -					this.dirty_canvas = true;
    -					break;
    -				case "slider": 
    -					var range = w.options.max - w.options.min;
    -					var nvalue = Math.clamp( (x - 10) / (width - 20), 0, 1);
    -					w.value = w.options.min + (w.options.max - w.options.min) * nvalue;
    -					if(w.callback)
    -						setTimeout( function(){	w.callback( w.value, that, node, pos ); }, 20 );
    -					this.dirty_canvas = true;
    -					break;
    -				case "number": 
    -				case "combo": 
    -					if(event.type == "mousemove" && w.type == "number")
    -					{
    -						w.value += (event.deltaX * 0.1) * (w.options.step || 1);
    -						if(w.options.min != null && w.value < w.options.min)
    -							w.value = w.options.min;
    -						if(w.options.max != null && w.value > w.options.max)
    -							w.value = w.options.max;
    -					}
    -					else if( event.type == "mousedown" )
    -					{
    -						var values = w.options.values;
    -						if(values && values.constructor === Function)
    -							values = w.options.values( w, node );
    -
    -						var delta = ( x < 40 ? -1 : ( x > width - 40 ? 1 : 0) );
    -						if (w.type == "number")
    -						{
    -							w.value += delta * 0.1 * (w.options.step || 1);
    -							if(w.options.min != null && w.value < w.options.min)
    -								w.value = w.options.min;
    -							if(w.options.max != null && w.value > w.options.max)
    -								w.value = w.options.max;
    -						}
    -						else if(delta)
    -						{
    -							var index = values.indexOf( w.value ) + delta;
    -							if( index >= values.length )
    -								index = 0;
    -							if( index < 0 )
    -								index = values.length - 1;
    -							w.value = values[ index ];
    -						}
    -						else
    -						{
    -							var menu = new LiteGraph.ContextMenu( values, { scale: Math.max(1,this.ds.scale), event: event, className: "dark", callback: inner_clicked.bind(w) }, ref_window );
    -							function inner_clicked( v, option, event )
    -							{
    -								this.value = v;
    -								that.dirty_canvas = true;
    -								return false;
    -							}
    -						}
    -					}
    -					if(w.callback)
    -						setTimeout( (function(){ this.callback( this.value, that, node, pos ); }).bind(w), 20 );
    -					this.dirty_canvas = true;
    -					break;
    -				case "toggle":
    -					if( event.type == "mousedown" )
    -					{
    -						w.value = !w.value;
    -						if(w.callback)
    -							setTimeout( function(){	w.callback( w.value, that, node, pos ); }, 20 );
    -					}
    -					break;
    -				case "string":
    -				case "text":
    -					if( event.type == "mousedown" )
    -						this.prompt( "Value", w.value, (function(v){ this.value = v; if(w.callback) w.callback(v, that, node ); }).bind(w), event );
    -					break;
    -				default: 
    -					if( w.mouse )
    -						w.mouse( ctx, event, [x,y], node );
    -					break;
    -			}
    -
    -			return w;
    -		}
    -	}
    -	return null;
    -}
    -
    -/**
    -* draws every group area in the background
    -* @method drawGroups
    -**/
    -LGraphCanvas.prototype.drawGroups = function(canvas, ctx)
    -{
    -	if(!this.graph)
    -		return;
    -
    -	var groups = this.graph._groups;
    -
    -	ctx.save();
    -	ctx.globalAlpha = 0.5 * this.editor_alpha;
    -
    -	for(var i = 0; i < groups.length; ++i)
    -	{
    -		var group = groups[i];
    -
    -		if(!overlapBounding( this.visible_area, group._bounding ))
    -			continue; //out of the visible area
    -
    -		ctx.fillStyle = group.color || "#335";
    -		ctx.strokeStyle = group.color || "#335";
    -		var pos = group._pos;
    -		var size = group._size;
    -		ctx.globalAlpha = 0.25 * this.editor_alpha;
    -		ctx.beginPath();
    -		ctx.rect( pos[0] + 0.5, pos[1] + 0.5, size[0], size[1] );
    -		ctx.fill();
    -		ctx.globalAlpha = this.editor_alpha;;
    -		ctx.stroke();
    -
    -		ctx.beginPath();
    -		ctx.moveTo( pos[0] + size[0], pos[1] + size[1] );
    -		ctx.lineTo( pos[0] + size[0] - 10, pos[1] + size[1] );
    -		ctx.lineTo( pos[0] + size[0], pos[1] + size[1] - 10 );
    -		ctx.fill();
    -
    -		var font_size = (group.font_size || LiteGraph.DEFAULT_GROUP_FONT_SIZE);
    -		ctx.font = font_size + "px Arial";
    -		ctx.fillText( group.title, pos[0] + 4, pos[1] + font_size );
    -	}
    -
    -	ctx.restore();
    -}
    -
    -LGraphCanvas.prototype.adjustNodesSize = function()
    -{
    -	var nodes = this.graph._nodes;
    -	for(var i = 0; i < nodes.length; ++i)
    -		nodes[i].size = nodes[i].computeSize();
    -	this.setDirty(true,true);
    -}
    -
    -
    -/**
    -* resizes the canvas to a given size, if no size is passed, then it tries to fill the parentNode
    -* @method resize
    -**/
    -LGraphCanvas.prototype.resize = function(width, height)
    -{
    -	if(!width && !height)
    -	{
    -		var parent = this.canvas.parentNode;
    -		width = parent.offsetWidth;
    -		height = parent.offsetHeight;
    -	}
    -
    -	if(this.canvas.width == width && this.canvas.height == height)
    -		return;
    -
    -	this.canvas.width = width;
    -	this.canvas.height = height;
    -	this.bgcanvas.width = this.canvas.width;
    -	this.bgcanvas.height = this.canvas.height;
    -	this.setDirty(true,true);
    -}
    -
    -/**
    -* switches to live mode (node shapes are not rendered, only the content)
    -* this feature was designed when graphs where meant to create user interfaces
    -* @method switchLiveMode
    -**/
    -LGraphCanvas.prototype.switchLiveMode = function(transition)
    -{
    -	if(!transition)
    -	{
    -		this.live_mode = !this.live_mode;
    -		this.dirty_canvas = true;
    -		this.dirty_bgcanvas = true;
    -		return;
    -	}
    -
    -	var self = this;
    -	var delta = this.live_mode ? 1.1 : 0.9;
    -	if(this.live_mode)
    -	{
    -		this.live_mode = false;
    -		this.editor_alpha = 0.1;
    -	}
    -
    -	var t = setInterval(function() {
    -		self.editor_alpha *= delta;
    -		self.dirty_canvas = true;
    -		self.dirty_bgcanvas = true;
    -
    -		if(delta < 1  && self.editor_alpha < 0.01)
    -		{
    -			clearInterval(t);
    -			if(delta < 1)
    -				self.live_mode = true;
    -		}
    -		if(delta > 1 && self.editor_alpha > 0.99)
    -		{
    -			clearInterval(t);
    -			self.editor_alpha = 1;
    -		}
    -	},1);
    -}
    -
    -LGraphCanvas.prototype.onNodeSelectionChange = function(node)
    -{
    -	return; //disabled
    -}
    -
    -LGraphCanvas.prototype.touchHandler = function(event)
    -{
    -	//alert("foo");
    -    var touches = event.changedTouches,
    -        first = touches[0],
    -        type = "";
    -
    -         switch(event.type)
    -    {
    -        case "touchstart": type = "mousedown"; break;
    -        case "touchmove":  type = "mousemove"; break;
    -        case "touchend":   type = "mouseup"; break;
    -        default: return;
    -    }
    -
    -             //initMouseEvent(type, canBubble, cancelable, view, clickCount,
    -    //           screenX, screenY, clientX, clientY, ctrlKey,
    -    //           altKey, shiftKey, metaKey, button, relatedTarget);
    -
    -	var window = this.getCanvasWindow();
    -	var document = window.document;
    -
    -    var simulatedEvent = document.createEvent("MouseEvent");
    -    simulatedEvent.initMouseEvent(type, true, true, window, 1,
    -                              first.screenX, first.screenY,
    -                              first.clientX, first.clientY, false,
    -                              false, false, false, 0/*left*/, null);
    -	first.target.dispatchEvent(simulatedEvent);
    -    event.preventDefault();
    -}
    -
    -/* CONTEXT MENU ********************/
    -
    -LGraphCanvas.onGroupAdd = function(info,entry,mouse_event)
    -{
    -	var canvas = LGraphCanvas.active_canvas;
    -	var ref_window = canvas.getCanvasWindow();
    -		
    -	var group = new LiteGraph.LGraphGroup();
    -	group.pos = canvas.convertEventToCanvasOffset( mouse_event );
    -	canvas.graph.add( group );
    -}
    -
    -LGraphCanvas.onMenuAdd = function( node, options, e, prev_menu )
    -{
    -	var canvas = LGraphCanvas.active_canvas;
    -	var ref_window = canvas.getCanvasWindow();
    -
    -	var values = LiteGraph.getNodeTypesCategories();
    -	var entries = [];
    -	for(var i in values)
    -		if(values[i])
    -			entries.push({ value: values[i], content: values[i], has_submenu: true });
    -
    -	//show categories
    -	var menu = new LiteGraph.ContextMenu( entries, { event: e, callback: inner_clicked, parentMenu: prev_menu }, ref_window);
    -
    -	function inner_clicked( v, option, e )
    -	{
    -		var category = v.value;
    -		var node_types = LiteGraph.getNodeTypesInCategory( category, canvas.filter );
    -		var values = [];
    -		for(var i in node_types)
    -			if (!node_types[i].skip_list)
    -				values.push( { content: node_types[i].title, value: node_types[i].type });
    -
    -		new LiteGraph.ContextMenu( values, {event: e, callback: inner_create, parentMenu: menu }, ref_window);
    -		return false;
    -	}
    -
    -	function inner_create( v, e )
    -	{
    -		var first_event = prev_menu.getFirstEvent();
    -		var node = LiteGraph.createNode( v.value );
    -		if(node)
    -		{
    -			node.pos = canvas.convertEventToCanvasOffset( first_event );
    -			canvas.graph.add( node );
    -		}
    -	}
    -
    -	return false;
    -}
    -
    -LGraphCanvas.onMenuCollapseAll = function()
    -{
    -
    -}
    -
    -
    -LGraphCanvas.onMenuNodeEdit = function()
    -{
    -
    -}
    -
    -LGraphCanvas.showMenuNodeOptionalInputs = function( v, options, e, prev_menu, node )
    -{
    -	if(!node)
    -		return;
    -
    -	var that = this;
    -	var canvas = LGraphCanvas.active_canvas;
    -	var ref_window = canvas.getCanvasWindow();
    -
    -	var options = node.optional_inputs;
    -	if(node.onGetInputs)
    -		options = node.onGetInputs();
    -
    -	var entries = [];
    -	if(options)
    -		for (var i in options)
    -		{
    -			var entry = options[i];
    -			if(!entry)
    -			{
    -				entries.push(null);
    -				continue;
    -			}
    -			var label = entry[0];
    -			if(entry[2] && entry[2].label)
    -				label = entry[2].label;
    -			var data = {content: label, value: entry};
    -			if(entry[1] == LiteGraph.ACTION)
    -				data.className = "event";
    -			entries.push(data);
    -		}
    -
    -	if(this.onMenuNodeInputs)
    -		entries = this.onMenuNodeInputs( entries );
    -
    -	if(!entries.length)
    -		return;
    -
    -	var menu = new LiteGraph.ContextMenu(entries, { event: e, callback: inner_clicked, parentMenu: prev_menu, node: node }, ref_window);
    -
    -	function inner_clicked(v, e, prev)
    -	{
    -		if(!node)
    -			return;
    -
    -		if(v.callback)
    -			v.callback.call( that, node, v, e, prev );
    -
    -		if(v.value)
    -		{
    -			node.addInput(v.value[0],v.value[1], v.value[2]);
    -			node.setDirtyCanvas(true,true);
    -		}
    -	}
    -
    -	return false;
    -}
    -
    -LGraphCanvas.showMenuNodeOptionalOutputs = function( v, options, e, prev_menu, node )
    -{
    -	if(!node)
    -		return;
    -
    -	var that = this;
    -	var canvas = LGraphCanvas.active_canvas;
    -	var ref_window = canvas.getCanvasWindow();
    -
    -	var options = node.optional_outputs;
    -	if(node.onGetOutputs)
    -		options = node.onGetOutputs();
    -
    -	var entries = [];
    -	if(options)
    -		for (var i in options)
    -		{
    -			var entry = options[i];
    -			if(!entry) //separator?
    -			{
    -				entries.push(null);
    -				continue;
    -			}
    -
    -			if(node.flags && node.flags.skip_repeated_outputs && node.findOutputSlot(entry[0]) != -1)
    -				continue; //skip the ones already on
    -			var label = entry[0];
    -			if(entry[2] && entry[2].label)
    -				label = entry[2].label;
    -			var data = {content: label, value: entry};
    -			if(entry[1] == LiteGraph.EVENT)
    -				data.className = "event";
    -			entries.push(data);
    -		}
    -
    -	if(this.onMenuNodeOutputs)
    -		entries = this.onMenuNodeOutputs( entries );
    -
    -	if(!entries.length)
    -		return;
    -
    -	var menu = new LiteGraph.ContextMenu(entries, {event: e, callback: inner_clicked, parentMenu: prev_menu, node: node }, ref_window);
    -
    -	function inner_clicked( v, e, prev )
    -	{
    -		if(!node)
    -			return;
    -
    -		if(v.callback)
    -			v.callback.call( that, node, v, e, prev );
    -
    -		if(!v.value)
    -			return;
    -
    -		var value = v.value[1];
    -
    -		if(value && (value.constructor === Object || value.constructor === Array)) //submenu why?
    -		{
    -			var entries = [];
    -			for(var i in value)
    -				entries.push({ content: i, value: value[i]});
    -			new LiteGraph.ContextMenu( entries, { event: e, callback: inner_clicked, parentMenu: prev_menu, node: node });
    -			return false;
    -		}
    -		else
    -		{
    -			node.addOutput( v.value[0], v.value[1], v.value[2]);
    -			node.setDirtyCanvas(true,true);
    -		}
    -
    -	}
    -
    -	return false;
    -}
    -
    -LGraphCanvas.onShowMenuNodeProperties = function( value, options, e, prev_menu, node )
    -{
    -	if(!node || !node.properties)
    -		return;
    -
    -	var that = this;
    -	var canvas = LGraphCanvas.active_canvas;
    -	var ref_window = canvas.getCanvasWindow();
    -
    -	var entries = [];
    -		for (var i in node.properties)
    -		{
    -			var value = node.properties[i] !== undefined ? node.properties[i] : " ";
    -			//value could contain invalid html characters, clean that
    -			value = LGraphCanvas.decodeHTML(value);
    -			entries.push({content: "<span class='property_name'>" + i + "</span>" + "<span class='property_value'>" + value + "</span>", value: i});
    -		}
    -	if(!entries.length)
    -		return;
    -
    -	var menu = new LiteGraph.ContextMenu(entries, {event: e, callback: inner_clicked, parentMenu: prev_menu, allow_html: true, node: node },ref_window);
    -
    -	function inner_clicked( v, options, e, prev )
    -	{
    -		if(!node)
    -			return;
    -		var rect = this.getBoundingClientRect();
    -		canvas.showEditPropertyValue( node, v.value, { position: [rect.left, rect.top] });
    -	}
    -
    -	return false;
    -}
    -
    -LGraphCanvas.decodeHTML = function( str )
    -{
    -	var e = document.createElement("div");
    -	e.innerText = str;
    -	return e.innerHTML;
    -}
    -
    -LGraphCanvas.onResizeNode = function( value, options, e, menu, node )
    -{
    -	if(!node)
    -		return;
    -	node.size = node.computeSize();
    -	node.setDirtyCanvas(true,true);
    -}
    -
    -LGraphCanvas.prototype.showLinkMenu = function( link, e )
    -{
    -	var that = this;
    -
    -	new LiteGraph.ContextMenu(["Delete"], { event: e, callback: inner_clicked });
    -
    -	function inner_clicked(v)
    -	{
    -		switch(v)
    -		{
    -			case "Delete": that.graph.removeLink( link.id ); break;
    -			default:
    -		}
    -	}
    -
    -	return false;
    -}
    -
    -LGraphCanvas.onShowPropertyEditor = function( item, options, e, menu, node )
    -{
    -	var input_html = "";
    -	var property = item.property || "title";
    -	var value = node[ property ];
    -
    -	var dialog = document.createElement("div");
    -	dialog.className = "graphdialog";
    -	dialog.innerHTML = "<span class='name'></span><input autofocus type='text' class='value'/><button>OK</button>";
    -	var title = dialog.querySelector(".name");
    -	title.innerText = property;
    -	var input = dialog.querySelector("input");
    -	if(input)
    -	{
    -		input.value = value;
    -        input.addEventListener("blur", function(e){
    -            this.focus();
    -        });
    -		input.addEventListener("keydown", function(e){
    -			if(e.keyCode != 13)
    -				return;
    -			inner();
    -			e.preventDefault();
    -			e.stopPropagation();
    -		});
    -	}
    -
    -	var graphcanvas = LGraphCanvas.active_canvas;
    -	var canvas = graphcanvas.canvas;
    -
    -	var rect = canvas.getBoundingClientRect();
    -	var offsetx = -20;
    -	var offsety = -20;
    -	if(rect)
    -	{
    -		offsetx -= rect.left;
    -		offsety -= rect.top;
    -	}
    -
    -	if( event )
    -	{
    -		dialog.style.left = (event.clientX + offsetx) + "px";
    -		dialog.style.top = (event.clientY + offsety)+ "px";
    -	}
    -	else
    -	{
    -		dialog.style.left = (canvas.width * 0.5 + offsetx) + "px";
    -		dialog.style.top = (canvas.height * 0.5 + offsety) + "px";
    -	}
    -
    -	var button = dialog.querySelector("button");
    -	button.addEventListener("click", inner );
    -	canvas.parentNode.appendChild( dialog );
    -
    -	function inner()
    -	{
    -		setValue( input.value );
    -	}
    -
    -	function setValue(value)
    -	{
    -		if( item.type == "Number" )
    -			value = Number(value);
    -		else if( item.type == "Boolean" )
    -			value = Boolean(value);
    -		node[ property ] = value;
    -		if(dialog.parentNode)
    -			dialog.parentNode.removeChild( dialog );
    -		node.setDirtyCanvas(true,true);
    -	}
    -}
    -
    -LGraphCanvas.prototype.prompt = function( title, value, callback, event )
    -{
    -	var that = this;
    -	var input_html = "";
    -	title = title || "";
    -
    -	var modified = false;
    -
    -	var dialog = document.createElement("div");
    -	dialog.className = "graphdialog rounded";
    -	dialog.innerHTML = "<span class='name'></span> <input autofocus type='text' class='value'/><button class='rounded'>OK</button>";
    -	dialog.close = function()
    -	{
    -		that.prompt_box = null;
    -		if(dialog.parentNode)
    -			dialog.parentNode.removeChild( dialog );
    -	}
    -
    -	if(this.ds.scale > 1)
    -		dialog.style.transform = "scale("+this.ds.scale+")";
    -
    -	dialog.addEventListener("mouseleave",function(e){
    -		if(!modified)
    -			 dialog.close();
    -	});
    -
    -	if(that.prompt_box)
    -		that.prompt_box.close();
    -	that.prompt_box = dialog;
    -
    -	var first = null;
    -	var timeout = null;
    -	var selected = null;
    -
    -	var name_element = dialog.querySelector(".name");
    -	name_element.innerText = title;
    -	var value_element = dialog.querySelector(".value");
    -	value_element.value = value;
    -
    -	var input = dialog.querySelector("input");
    -	input.addEventListener("keydown", function(e){
    -		modified = true;
    -		if(e.keyCode == 27) //ESC
    -			dialog.close();
    -		else if(e.keyCode == 13)
    -		{
    -			if( callback )
    -				callback( this.value );
    -			dialog.close();
    -		}
    -		else
    -			return;
    -		e.preventDefault();
    -		e.stopPropagation();
    -	});
    -
    -	var button = dialog.querySelector("button");
    -	button.addEventListener("click", function(e){
    -		if( callback )
    -			callback( input.value );
    -		that.setDirty(true);
    -		dialog.close();		
    -	});
    -
    -	var graphcanvas = LGraphCanvas.active_canvas;
    -	var canvas = graphcanvas.canvas;
    -
    -	var rect = canvas.getBoundingClientRect();
    -	var offsetx = -20;
    -	var offsety = -20;
    -	if(rect)
    -	{
    -		offsetx -= rect.left;
    -		offsety -= rect.top;
    -	}
    -
    -	if( event )
    -	{
    -		dialog.style.left = (event.clientX + offsetx) + "px";
    -		dialog.style.top = (event.clientY + offsety)+ "px";
    -	}
    -	else
    -	{
    -		dialog.style.left = (canvas.width * 0.5 + offsetx) + "px";
    -		dialog.style.top = (canvas.height * 0.5 + offsety) + "px";
    -	}
    -
    -	canvas.parentNode.appendChild( dialog );
    -	setTimeout( function(){	input.focus(); },10 );
    -
    -	return dialog;
    -}
    -
    -
    -LGraphCanvas.search_limit = -1;
    -LGraphCanvas.prototype.showSearchBox = function(event)
    -{
    -	var that = this;
    -	var input_html = "";
    -
    -	var dialog = document.createElement("div");
    -	dialog.className = "litegraph litesearchbox graphdialog rounded";
    -	dialog.innerHTML = "<span class='name'>Search</span> <input autofocus type='text' class='value rounded'/><div class='helper'></div>";
    -	dialog.close = function()
    -	{
    -		that.search_box = null;
    -		document.body.focus();
    -		setTimeout( function(){ that.canvas.focus(); },20 ); //important, if canvas loses focus keys wont be captured
    -		if(dialog.parentNode)
    -			dialog.parentNode.removeChild( dialog );
    -	}
    -
    -	var timeout_close = null;
    -
    -	if(this.ds.scale > 1)
    -		dialog.style.transform = "scale("+this.ds.scale+")";
    -
    -	dialog.addEventListener("mouseenter",function(e){
    -		if(timeout_close)
    -		{
    -			clearTimeout(timeout_close);
    -			timeout_close = null;
    -		}
    -	});
    -
    -	dialog.addEventListener("mouseleave",function(e){
    -		 //dialog.close();
    -		timeout_close = setTimeout(function(){
    -			dialog.close();
    -		},500);
    -	});
    -
    -	if(that.search_box)
    -		that.search_box.close();
    -	that.search_box = dialog;
    -
    -	var helper = dialog.querySelector(".helper");
    -
    -	var first = null;
    -	var timeout = null;
    -	var selected = null;
    -
    -	var input = dialog.querySelector("input");
    -	if(input)
    -	{
    -        input.addEventListener("blur", function(e){
    -            this.focus();
    -        });
    -		input.addEventListener("keydown", function(e){
    -
    -			if(e.keyCode == 38) //UP
    -				changeSelection(false);
    -			else if(e.keyCode == 40) //DOWN
    -				changeSelection(true);
    -			else if(e.keyCode == 27) //ESC
    -				dialog.close();
    -			else if(e.keyCode == 13)
    -			{
    -				if(selected)
    -					select( selected.innerHTML )
    -				else if(first)
    -					select( first );
    -				else
    -					dialog.close();
    -			}
    -			else
    -			{
    -				if(timeout)
    -					clearInterval(timeout);
    -				timeout = setTimeout( refreshHelper, 10 );
    -				return;
    -			}
    -			e.preventDefault();
    -			e.stopPropagation();
    -		});
    -	}
    -
    -	var graphcanvas = LGraphCanvas.active_canvas;
    -	var canvas = graphcanvas.canvas;
    -
    -	var rect = canvas.getBoundingClientRect();
    -	var offsetx = -20;
    -	var offsety = -20;
    -	if(rect)
    -	{
    -		offsetx -= rect.left;
    -		offsety -= rect.top;
    -	}
    -
    -	if( event )
    -	{
    -		dialog.style.left = (event.clientX + offsetx) + "px";
    -		dialog.style.top = (event.clientY + offsety)+ "px";
    -	}
    -	else
    -	{
    -		dialog.style.left = (canvas.width * 0.5 + offsetx) + "px";
    -		dialog.style.top = (canvas.height * 0.5 + offsety) + "px";
    -	}
    -
    -	canvas.parentNode.appendChild( dialog );
    -	input.focus();
    -
    -	function select( name )
    -	{
    -		if(name)
    -		{
    -			if( that.onSearchBoxSelection )
    -				that.onSearchBoxSelection( name, event, graphcanvas );
    -			else
    -			{
    -				var extra = LiteGraph.searchbox_extras[ name ];
    -				if( extra )
    -					name = extra.type;
    -
    -				var node = LiteGraph.createNode( name );
    -				if(node)
    -				{
    -					node.pos = graphcanvas.convertEventToCanvasOffset( event );
    -					graphcanvas.graph.add( node );
    -				}
    -
    -				if( extra && extra.data )
    -				{
    -					if(extra.data.properties)
    -						for(var i in extra.data.properties)
    -							node.addProperty( extra.data.properties[i][0], extra.data.properties[i][0] );
    -					if(extra.data.inputs)
    -					{
    -						node.inputs = [];
    -						for(var i in extra.data.inputs)
    -							node.addOutput( extra.data.inputs[i][0],extra.data.inputs[i][1] );
    -					}
    -					if(extra.data.outputs)
    -					{
    -						node.outputs = [];
    -						for(var i in extra.data.outputs)
    -							node.addOutput( extra.data.outputs[i][0],extra.data.outputs[i][1] );
    -					}
    -					if(extra.data.title)
    -						node.title = extra.data.title;
    -					if(extra.data.json)
    -						node.configure( extra.data.json );
    -				}
    -			}
    -		}
    -
    -		dialog.close();
    -	}
    -
    -	function changeSelection( forward )
    -	{
    -		var prev = selected;
    -		if(selected)
    -			selected.classList.remove("selected");
    -		if(!selected)
    -			selected = forward ? helper.childNodes[0] : helper.childNodes[ helper.childNodes.length ];
    -		else
    -		{
    -			selected = forward ? selected.nextSibling : selected.previousSibling;
    -			if(!selected)
    -				selected = prev;
    -		}
    -		if(!selected)
    -			return;
    -		selected.classList.add("selected");
    -		selected.scrollIntoView();
    -	}
    -
    -	function refreshHelper() {
    -        timeout = null;
    -        var str = input.value;
    -        first = null;
    -        helper.innerHTML = "";
    -        if (!str)
    -            return;
    -
    -        if (that.onSearchBox) {
    -            var list = that.onSearchBox( help, str, graphcanvas );
    -			if(list)
    -				for( var i = 0; i < list.length; ++i )
    -					addResult( list[i] );
    -    	} else {
    -            var c = 0;
    -       		str = str.toLowerCase();
    -			//extras
    -			for(var i in LiteGraph.searchbox_extras)
    -			{
    -				var extra = LiteGraph.searchbox_extras[i];
    -				if( extra.desc.toLowerCase().indexOf(str) === -1 )
    -					continue;
    -				addResult( extra.desc, "searchbox_extra" );
    -				if(LGraphCanvas.search_limit !== -1 && c++ > LGraphCanvas.search_limit )
    -					break;
    -			}
    -
    -        	if(Array.prototype.filter)//filter supported
    -			{
    -				//types
    -        		var keys = Object.keys( LiteGraph.registered_node_types );
    -        		var filtered = keys.filter(function (item) {
    -					return item.toLowerCase().indexOf(str) !== -1;
    -                });
    -        		for(var i = 0; i < filtered.length; i++)
    -				{
    -                    addResult(filtered[i]);
    -                    if(LGraphCanvas.search_limit !== -1 && c++ > LGraphCanvas.search_limit)
    -						break;
    -				}
    -			} else {
    -                for (var i in LiteGraph.registered_node_types)
    -				{
    -                    if (i.indexOf(str) != -1) {
    -                        addResult(i);
    -                        if(LGraphCanvas.search_limit !== -1 && c++ > LGraphCanvas.search_limit)
    -							break;
    -                    }
    -                }
    -            }
    -        }
    -
    -		function addResult( type, className )
    -		{
    -			var help = document.createElement("div");
    -			if (!first)
    -				first = type;
    -			help.innerText = type;
    -			help.dataset["type"] = escape(type);
    -			help.className = "litegraph lite-search-item";
    -			if( className )
    -				help.className +=  " " + className;
    -			help.addEventListener("click", function (e) {
    -				select( unescape( this.dataset["type"] ) );
    -			});
    -			helper.appendChild(help);
    -		}
    -	}
    -
    -	return dialog;
    -}
    -
    -LGraphCanvas.prototype.showEditPropertyValue = function( node, property, options )
    -{
    -	if(!node || node.properties[ property ] === undefined )
    -		return;
    -
    -	options = options || {};
    -	var that = this;
    -
    -	var type = "string";
    -
    -	if(node.properties[ property ] !== null)
    -		type = typeof(node.properties[ property ]);
    -
    -	//for arrays
    -	if(type == "object")
    -	{
    -		if( node.properties[ property ].length )
    -			type = "array";
    -	}
    -
    -	var info = null;
    -	if(node.getPropertyInfo)
    -		info = node.getPropertyInfo(property);
    -	if(node.properties_info)
    -	{
    -		for(var i = 0; i < node.properties_info.length; ++i)
    -		{
    -			if( node.properties_info[i].name == property )
    -			{
    -				info = node.properties_info[i];
    -				break;
    -			}
    -		}
    -	}
    -
    -	if(info !== undefined && info !== null && info.type )
    -		type = info.type;
    -
    -	var input_html = "";
    -
    -	if(type == "string" || type == "number" || type == "array")
    -		input_html = "<input autofocus type='text' class='value'/>";
    -	else if(type == "enum" && info.values)
    -	{
    -		input_html = "<select autofocus type='text' class='value'>";
    -		for(var i in info.values)
    -		{
    -			var v = info.values.constructor === Array ? info.values[i] : i;
    -			input_html += "<option value='"+v+"' "+(v == node.properties[property] ? "selected" : "")+">"+info.values[i]+"</option>";
    -		}
    -		input_html += "</select>";
    -	}
    -	else if(type == "boolean")
    -	{
    -		input_html = "<input autofocus type='checkbox' class='value' "+(node.properties[property] ? "checked" : "")+"/>";
    -	}
    -	else
    -	{
    -		console.warn("unknown type: " + type );
    -		return;
    -	}
    -
    -	var dialog = this.createDialog( "<span class='name'>" + property + "</span>"+input_html+"<button>OK</button>" , options );
    -
    -	if(type == "enum" && info.values)
    -	{
    -		var input = dialog.querySelector("select");
    -		input.addEventListener("change", function(e){
    -			setValue( e.target.value );
    -			//var index = e.target.value;
    -			//setValue( e.options[e.selectedIndex].value );
    -		});
    -	}
    -	else if(type == "boolean")
    -	{
    -		var input = dialog.querySelector("input");
    -		if(input)
    -		{
    -			input.addEventListener("click", function(e){
    -				setValue( !!input.checked );
    -			});
    -		}
    -	}
    -	else
    -	{
    -		var input = dialog.querySelector("input");
    -		if(input)
    -		{
    -            input.addEventListener("blur", function(e){
    -                this.focus();
    -            });
    -			input.value = node.properties[ property ] !== undefined ? node.properties[ property ] : "";
    -			input.addEventListener("keydown", function(e){
    -				if(e.keyCode != 13)
    -					return;
    -				inner();
    -				e.preventDefault();
    -				e.stopPropagation();
    -			});
    -		}
    -	}
    -
    -	var button = dialog.querySelector("button");
    -	button.addEventListener("click", inner );
    -
    -	function inner()
    -	{
    -		setValue( input.value );
    -	}
    -
    -	function setValue(value)
    -	{
    -		if(typeof( node.properties[ property ] ) == "number")
    -			value = Number(value);
    -		if(type == "array")
    -			value = value.split(",").map(Number);
    -		node.properties[ property ] = value;
    -		if(node._graph)
    -			node._graph._version++;
    -		if(node.onPropertyChanged)
    -			node.onPropertyChanged( property, value );
    -		dialog.close();
    -		node.setDirtyCanvas(true,true);
    -	}
    -}
    -
    -LGraphCanvas.prototype.createDialog = function( html, options )
    -{
    -	options = options || {};
    -
    -	var dialog = document.createElement("div");
    -	dialog.className = "graphdialog";
    -	dialog.innerHTML = html;
    -
    -	var rect = this.canvas.getBoundingClientRect();
    -	var offsetx = -20;
    -	var offsety = -20;
    -	if(rect)
    -	{
    -		offsetx -= rect.left;
    -		offsety -= rect.top;
    -	}
    -
    -	if( options.position )
    -	{
    -		offsetx += options.position[0];
    -		offsety += options.position[1];
    -	}
    -	else if( options.event )
    -	{
    -		offsetx += options.event.clientX;
    -		offsety += options.event.clientY;
    -	}
    -	else //centered
    -	{
    -		offsetx += this.canvas.width * 0.5;
    -		offsety += this.canvas.height * 0.5;
    -	}
    -
    -	dialog.style.left = offsetx + "px";
    -	dialog.style.top = offsety + "px";
    -
    -	this.canvas.parentNode.appendChild( dialog );
    -
    -	dialog.close = function()
    -	{
    -		if(this.parentNode)
    -			this.parentNode.removeChild( this );
    -	}
    -
    -	return dialog;
    -}
    -
    -LGraphCanvas.onMenuNodeCollapse = function( value, options, e, menu, node )
    -{
    -	node.collapse();
    -}
    -
    -LGraphCanvas.onMenuNodePin = function( value, options, e, menu, node )
    -{
    -	node.pin();
    -}
    -
    -LGraphCanvas.onMenuNodeMode = function( value, options, e, menu, node )
    -{
    -	new LiteGraph.ContextMenu(["Always","On Event","On Trigger","Never"], {event: e, callback: inner_clicked, parentMenu: menu, node: node });
    -
    -	function inner_clicked(v)
    -	{
    -		if(!node)
    -			return;
    -		switch(v)
    -		{
    -			case "On Event": node.mode = LiteGraph.ON_EVENT; break;
    -			case "On Trigger": node.mode = LiteGraph.ON_TRIGGER; break;
    -			case "Never": node.mode = LiteGraph.NEVER; break;
    -			case "Always":
    -			default:
    -				node.mode = LiteGraph.ALWAYS; break;
    -		}
    -	}
    -
    -	return false;
    -}
    -
    -LGraphCanvas.onMenuNodeColors = function( value, options, e, menu, node )
    -{
    -	if(!node)
    -		throw("no node for color");
    -
    -	var values = [];
    -	values.push({ value:null, content:"<span style='display: block; padding-left: 4px;'>No color</span>" });
    -
    -	for(var i in LGraphCanvas.node_colors)
    -	{
    -		var color = LGraphCanvas.node_colors[i];
    -		var value = { value:i, content:"<span style='display: block; color: #999; padding-left: 4px; border-left: 8px solid "+color.color+"; background-color:"+color.bgcolor+"'>"+i+"</span>" };
    -		values.push(value);
    -	}
    -	new LiteGraph.ContextMenu( values, { event: e, callback: inner_clicked, parentMenu: menu, node: node });
    -
    -	function inner_clicked(v)
    -	{
    -		if(!node)
    -			return;
    -
    -		var color = v.value ? LGraphCanvas.node_colors[ v.value ] : null;
    -		if(color)
    -		{
    -			if(node.constructor === LiteGraph.LGraphGroup)
    -				node.color = color.groupcolor;
    -			else
    -			{
    -				node.color = color.color;
    -				node.bgcolor = color.bgcolor;
    -			}
    -		}
    -		else
    -		{
    -			delete node.color;
    -			delete node.bgcolor;
    -		}
    -		node.setDirtyCanvas(true,true);
    -	}
    -
    -	return false;
    -}
    -
    -LGraphCanvas.onMenuNodeShapes = function( value, options, e, menu, node )
    -{
    -	if(!node)
    -		throw("no node passed");
    -
    -	new LiteGraph.ContextMenu( LiteGraph.VALID_SHAPES, { event: e, callback: inner_clicked, parentMenu: menu, node: node });
    -
    -	function inner_clicked(v)
    -	{
    -		if(!node)
    -			return;
    -		node.shape = v;
    -		node.setDirtyCanvas(true);
    -	}
    -
    -	return false;
    -}
    -
    -LGraphCanvas.onMenuNodeRemove = function( value, options, e, menu, node )
    -{
    -	if(!node)
    -		throw("no node passed");
    -
    -	if(node.removable === false)
    -		return;
    -
    -	node.graph.remove(node);
    -	node.setDirtyCanvas(true,true);
    -}
    -
    -LGraphCanvas.onMenuNodeClone = function( value, options, e, menu, node )
    -{
    -	if(node.clonable == false) return;
    -	var newnode = node.clone();
    -	if(!newnode)
    -		return;
    -	newnode.pos = [node.pos[0]+5,node.pos[1]+5];
    -	node.graph.add(newnode);
    -	node.setDirtyCanvas(true,true);
    -}
    -
    -LGraphCanvas.node_colors = {
    -	"red": { color:"#322", bgcolor:"#533", groupcolor: "#A88" },
    -	"brown": { color:"#332922", bgcolor:"#593930", groupcolor: "#b06634" },
    -	"green": { color:"#232", bgcolor:"#353", groupcolor: "#8A8" },
    -	"blue": { color:"#223", bgcolor:"#335", groupcolor: "#88A" },
    -	"pale_blue": { color:"#2a363b", bgcolor:"#3f5159", groupcolor: "#3f789e" },
    -	"cyan": { color:"#233", bgcolor:"#355", groupcolor: "#8AA" },
    -	"purple": { color:"#323", bgcolor:"#535", groupcolor: "#a1309b" },
    -	"yellow": { color:"#432", bgcolor:"#653", groupcolor: "#b58b2a" },
    -	"black": { color:"#222", bgcolor:"#000", groupcolor: "#444" }
    -};
    -
    -LGraphCanvas.prototype.getCanvasMenuOptions = function()
    -{
    -	var options = null;
    -	if(this.getMenuOptions)
    -		options = this.getMenuOptions();
    -	else
    -	{
    -		options = [
    -			{ content:"Add Node", has_submenu: true, callback: LGraphCanvas.onMenuAdd },
    -			{ content:"Add Group", callback: LGraphCanvas.onGroupAdd }
    -			//{content:"Collapse All", callback: LGraphCanvas.onMenuCollapseAll }
    -		];
    -
    -		if(this._graph_stack && this._graph_stack.length > 0)
    -			options.push(null,{content:"Close subgraph", callback: this.closeSubgraph.bind(this) });
    -	}
    -
    -	if(this.getExtraMenuOptions)
    -	{
    -		var extra = this.getExtraMenuOptions(this,options);
    -		if(extra)
    -			options = options.concat( extra );
    -	}
    -
    -	return options;
    -}
    -
    -//called by processContextMenu to extract the menu list
    -LGraphCanvas.prototype.getNodeMenuOptions = function( node )
    -{
    -	var options = null;
    -
    -	if(node.getMenuOptions)
    -		options = node.getMenuOptions(this);
    -	else
    -		options = [
    -			{content:"Inputs", has_submenu: true, disabled:true, callback: LGraphCanvas.showMenuNodeOptionalInputs },
    -			{content:"Outputs", has_submenu: true, disabled:true, callback: LGraphCanvas.showMenuNodeOptionalOutputs },
    -			null,
    -			{content:"Properties", has_submenu: true, callback: LGraphCanvas.onShowMenuNodeProperties },
    -			null,
    -			{content:"Title", callback: LGraphCanvas.onShowPropertyEditor },
    -			{content:"Mode", has_submenu: true, callback: LGraphCanvas.onMenuNodeMode },
    -			{content:"Resize", callback: LGraphCanvas.onResizeNode },
    -			{content:"Collapse", callback: LGraphCanvas.onMenuNodeCollapse },
    -			{content:"Pin", callback: LGraphCanvas.onMenuNodePin },
    -			{content:"Colors", has_submenu: true, callback: LGraphCanvas.onMenuNodeColors },
    -			{content:"Shapes", has_submenu: true, callback: LGraphCanvas.onMenuNodeShapes },
    -			null
    -		];
    -
    -	if(node.onGetInputs)
    -	{
    -		var inputs = node.onGetInputs();
    -		if(inputs && inputs.length)
    -			options[0].disabled = false;
    -	}
    -
    -	if(node.onGetOutputs)
    -	{
    -		var outputs = node.onGetOutputs();
    -		if(outputs && outputs.length )
    -			options[1].disabled = false;
    -	}
    -
    -	if(node.getExtraMenuOptions)
    -	{
    -		var extra = node.getExtraMenuOptions(this);
    -		if(extra)
    -		{
    -			extra.push(null);
    -			options = extra.concat( options );
    -		}
    -	}
    -
    -	if( node.clonable !== false )
    -			options.push({content:"Clone", callback: LGraphCanvas.onMenuNodeClone });
    -	if( node.removable !== false )
    -			options.push(null,{content:"Remove", callback: LGraphCanvas.onMenuNodeRemove });
    -
    -	if(node.graph && node.graph.onGetNodeMenuOptions )
    -		node.graph.onGetNodeMenuOptions( options, node );
    -
    -	return options;
    -}
    -
    -LGraphCanvas.prototype.getGroupMenuOptions = function( node )
    -{
    -	var o = [
    -		{content:"Title", callback: LGraphCanvas.onShowPropertyEditor },
    -		{content:"Color", has_submenu: true, callback: LGraphCanvas.onMenuNodeColors },
    -		{content:"Font size", property: "font_size", type:"Number", callback: LGraphCanvas.onShowPropertyEditor },
    -		null,
    -		{content:"Remove", callback: LGraphCanvas.onMenuNodeRemove }
    -	];
    -
    -	return o;
    -}
    -
    -LGraphCanvas.prototype.processContextMenu = function( node, event )
    -{
    -	var that = this;
    -	var canvas = LGraphCanvas.active_canvas;
    -	var ref_window = canvas.getCanvasWindow();
    -
    -	var menu_info = null;
    -	var options = { event: event, callback: inner_option_clicked, extra: node };
    -
    -	//check if mouse is in input
    -	var slot = null;
    -	if(node)
    -	{
    -		slot = node.getSlotInPosition( event.canvasX, event.canvasY );
    -		LGraphCanvas.active_node = node;
    -	}
    -
    -	if(slot) //on slot
    -	{
    -		menu_info = [];
    -		if(slot && slot.output && slot.output.links && slot.output.links.length)
    -			menu_info.push( { content: "Disconnect Links", slot: slot } );
    -		menu_info.push( slot.locked ? "Cannot remove"  : { content: "Remove Slot", slot: slot } );
    -		menu_info.push( slot.nameLocked ? "Cannot rename" : { content: "Rename Slot", slot: slot } );
    -		options.title = (slot.input ? slot.input.type : slot.output.type) || "*";
    -		if(slot.input && slot.input.type == LiteGraph.ACTION)
    -			options.title = "Action";
    -		if(slot.output && slot.output.type == LiteGraph.EVENT)
    -			options.title = "Event";
    -	}
    -	else
    -	{
    -		if( node ) //on node
    -			menu_info = this.getNodeMenuOptions(node);
    -		else 
    -		{
    -			menu_info = this.getCanvasMenuOptions();
    -			var group = this.graph.getGroupOnPos( event.canvasX, event.canvasY );
    -			if( group ) //on group
    -				menu_info.push(null,{content:"Edit Group", has_submenu: true, submenu: { title:"Group", extra: group, options: this.getGroupMenuOptions( group ) }});
    -		}
    -	}
    -
    -	//show menu
    -	if(!menu_info)
    -		return;
    -
    -	var menu = new LiteGraph.ContextMenu( menu_info, options, ref_window );
    -
    -	function inner_option_clicked( v, options, e )
    -	{
    -		if(!v)
    -			return;
    -
    -		if(v.content == "Remove Slot")
    -		{
    -			var info = v.slot;
    -			if(info.input)
    -				node.removeInput( info.slot );
    -			else if(info.output)
    -				node.removeOutput( info.slot );
    -			return;
    -		}
    -		else if(v.content == "Disconnect Links")
    -		{
    -			var info = v.slot;
    -			if(info.output)
    -				node.disconnectOutput( info.slot );
    -			else if(info.input)
    -				node.disconnectInput( info.slot );
    -			return;
    -		}
    -		else if( v.content == "Rename Slot")
    -		{
    -			var info = v.slot;
    -            var slot_info = info.input ? node.getInputInfo( info.slot ) : node.getOutputInfo( info.slot );
    -			var dialog = that.createDialog( "<span class='name'>Name</span><input autofocus type='text'/><button>OK</button>" , options );
    -			var input = dialog.querySelector("input");
    -			if(input && slot_info){
    -				input.value = slot_info.label || "";
    -			}
    -			dialog.querySelector("button").addEventListener("click",function(e){
    -				if(input.value)
    -				{
    -					if( slot_info )
    -						slot_info.label = input.value;
    -					that.setDirty(true);
    -				}
    -				dialog.close();
    -			});
    -		}
    -
    -		//if(v.callback)
    -		//	return v.callback.call(that, node, options, e, menu, that, event );
    -	}
    -}
    -
    -
    -
    -
    -
    -
    -//API *************************************************
    -//like rect but rounded corners
    -if(this.CanvasRenderingContext2D)
    -CanvasRenderingContext2D.prototype.roundRect = function (x, y, width, height, radius, radius_low) {
    -  if ( radius === undefined ) {
    -    radius = 5;
    -  }
    -
    -  if(radius_low === undefined)
    -	 radius_low  = radius;
    -
    -  this.moveTo(x + radius, y);
    -  this.lineTo(x + width - radius, y);
    -  this.quadraticCurveTo(x + width, y, x + width, y + radius);
    -
    -  this.lineTo(x + width, y + height - radius_low);
    -  this.quadraticCurveTo(x + width, y + height, x + width - radius_low, y + height);
    -  this.lineTo(x + radius_low, y + height);
    -  this.quadraticCurveTo(x, y + height, x, y + height - radius_low);
    -  this.lineTo(x, y + radius);
    -  this.quadraticCurveTo(x, y, x + radius, y);
    -}
    -
    -function compareObjects(a,b)
    -{
    -	for(var i in a)
    -		if(a[i] != b[i])
    -			return false;
    -	return true;
    -}
    -LiteGraph.compareObjects = compareObjects;
    -
    -function distance(a,b)
    -{
    -	return Math.sqrt( (b[0] - a[0]) * (b[0] - a[0]) + (b[1] - a[1]) * (b[1] - a[1]) );
    -}
    -LiteGraph.distance = distance;
    -
    -function colorToString(c)
    -{
    -	return "rgba(" + Math.round(c[0] * 255).toFixed() + "," + Math.round(c[1] * 255).toFixed() + "," + Math.round(c[2] * 255).toFixed() + "," + (c.length == 4 ? c[3].toFixed(2) : "1.0") + ")";
    -}
    -LiteGraph.colorToString = colorToString;
    -
    -function isInsideRectangle( x,y, left, top, width, height)
    -{
    -	if (left < x && (left + width) > x &&
    -		top < y && (top + height) > y)
    -		return true;
    -	return false;
    -}
    -LiteGraph.isInsideRectangle = isInsideRectangle;
    -
    -//[minx,miny,maxx,maxy]
    -function growBounding( bounding, x,y)
    -{
    -	if(x < bounding[0])
    -		bounding[0] = x;
    -	else if(x > bounding[2])
    -		bounding[2] = x;
    -
    -	if(y < bounding[1])
    -		bounding[1] = y;
    -	else if(y > bounding[3])
    -		bounding[3] = y;
    -}
    -LiteGraph.growBounding = growBounding;
    -
    -//point inside boundin box
    -function isInsideBounding(p,bb)
    -{
    -	if (p[0] < bb[0][0] ||
    -		p[1] < bb[0][1] ||
    -		p[0] > bb[1][0] ||
    -		p[1] > bb[1][1])
    -		return false;
    -	return true;
    -}
    -LiteGraph.isInsideBounding = isInsideBounding;
    -
    -//boundings overlap, format: [ startx, starty, width, height ]
    -function overlapBounding(a,b)
    -{
    -	var A_end_x = a[0] + a[2];
    -	var A_end_y = a[1] + a[3];
    -	var B_end_x = b[0] + b[2];
    -	var B_end_y = b[1] + b[3];
    -
    -	if ( a[0] > B_end_x ||
    -		a[1] > B_end_y ||
    -		A_end_x < b[0] ||
    -		A_end_y < b[1])
    -		return false;
    -	return true;
    -}
    -LiteGraph.overlapBounding = overlapBounding;
    -
    -//Convert a hex value to its decimal value - the inputted hex must be in the
    -//	format of a hex triplet - the kind we use for HTML colours. The function
    -//	will return an array with three values.
    -function hex2num(hex) {
    -	if(hex.charAt(0) == "#") hex = hex.slice(1); //Remove the '#' char - if there is one.
    -	hex = hex.toUpperCase();
    -	var hex_alphabets = "0123456789ABCDEF";
    -	var value = new Array(3);
    -	var k = 0;
    -	var int1,int2;
    -	for(var i=0;i<6;i+=2) {
    -		int1 = hex_alphabets.indexOf(hex.charAt(i));
    -		int2 = hex_alphabets.indexOf(hex.charAt(i+1));
    -		value[k] = (int1 * 16) + int2;
    -		k++;
    -	}
    -	return(value);
    -}
    -
    -LiteGraph.hex2num = hex2num;
    -
    -//Give a array with three values as the argument and the function will return
    -//	the corresponding hex triplet.
    -function num2hex(triplet) {
    -	var hex_alphabets = "0123456789ABCDEF";
    -	var hex = "#";
    -	var int1,int2;
    -	for(var i=0;i<3;i++) {
    -		int1 = triplet[i] / 16;
    -		int2 = triplet[i] % 16;
    -
    -		hex += hex_alphabets.charAt(int1) + hex_alphabets.charAt(int2);
    -	}
    -	return(hex);
    -}
    -
    -LiteGraph.num2hex = num2hex;
    -
    -/* LiteGraph GUI elements used for canvas editing *************************************/
    -
    -/**
    -* ContextMenu from LiteGUI
    -*
    -* @class ContextMenu
    -* @constructor
    -* @param {Array} values (allows object { title: "Nice text", callback: function ... })
    -* @param {Object} options [optional] Some options:\
    -* - title: title to show on top of the menu
    -* - callback: function to call when an option is clicked, it receives the item information
    -* - ignore_item_callbacks: ignores the callback inside the item, it just calls the options.callback
    -* - event: you can pass a MouseEvent, this way the ContextMenu appears in that position
    -*/
    -function ContextMenu( values, options )
    -{
    -	options = options || {};
    -	this.options = options;
    -	var that = this;
    -
    -	//to link a menu with its parent
    -	if(options.parentMenu)
    -	{
    -		if( options.parentMenu.constructor !== this.constructor )
    -		{
    -			console.error("parentMenu must be of class ContextMenu, ignoring it");
    -			options.parentMenu = null;
    -		}
    -		else
    -		{
    -			this.parentMenu = options.parentMenu;
    -			this.parentMenu.lock = true;
    -			this.parentMenu.current_submenu = this;
    -		}
    -	}
    -
    -	if(options.event && options.event.constructor !== MouseEvent && options.event.constructor !== CustomEvent)
    -	{
    -		console.error("Event passed to ContextMenu is not of type MouseEvent or CustomEvent. Ignoring it.");
    -		options.event = null;
    -	}
    -
    -	var root = document.createElement("div");
    -	root.className = "litegraph litecontextmenu litemenubar-panel";
    -	if( options.className) 
    -		root.className += " " + options.className;
    -	root.style.minWidth = 100;
    -	root.style.minHeight = 100;
    -	root.style.pointerEvents = "none";
    -	setTimeout( function() { root.style.pointerEvents = "auto"; },100); //delay so the mouse up event is not caugh by this element
    -
    -	//this prevents the default context browser menu to open in case this menu was created when pressing right button
    -	root.addEventListener("mouseup", function(e){
    -		e.preventDefault(); return true;
    -	}, true);
    -	root.addEventListener("contextmenu", function(e) {
    -		if(e.button != 2) //right button
    -			return false;
    -		e.preventDefault();
    -		return false;
    -	},true);
    -
    -	root.addEventListener("mousedown", function(e){
    -		if(e.button == 2)
    -		{
    -			that.close();
    -			e.preventDefault(); return true;
    -		}
    -	}, true);
    -
    -	function on_mouse_wheel(e)
    -	{
    -		var pos = parseInt( root.style.top );
    -		root.style.top = (pos + e.deltaY * options.scroll_speed).toFixed() + "px";
    -		e.preventDefault();
    -		return true;
    -	}
    -
    -	if(!options.scroll_speed)
    -		options.scroll_speed = 0.1;
    -
    -	root.addEventListener("wheel", on_mouse_wheel, true);
    -	root.addEventListener("mousewheel", on_mouse_wheel, true);
    -
    -
    -	this.root = root;
    -
    -	//title
    -	if(options.title)
    -	{
    -		var element = document.createElement("div");
    -		element.className = "litemenu-title";
    -		element.innerHTML = options.title;
    -		root.appendChild(element);
    -	}
    -
    -	//entries
    -	var num = 0;
    -	for(var i in values)
    -	{
    -		var name = values.constructor == Array ? values[i] : i;
    -		if( name != null && name.constructor !== String )
    -			name = name.content === undefined ? String(name) : name.content;
    -		var value = values[i];
    -		this.addItem( name, value, options );
    -		num++;
    -	}
    -
    -	//close on leave
    -	root.addEventListener("mouseleave", function(e) {
    -		if(that.lock)
    -			return;
    -		if(root.closing_timer)
    -			clearTimeout( root.closing_timer );
    -		root.closing_timer = setTimeout( that.close.bind(that, e), 500 );
    -		//that.close(e);
    -	});
    -
    -	root.addEventListener("mouseenter", function(e) {
    -		if(root.closing_timer)
    -			clearTimeout( root.closing_timer );
    -	});
    -
    -	//insert before checking position
    -	var root_document = document;
    -	if(options.event)
    -		root_document = options.event.target.ownerDocument;
    -
    -	if(!root_document)
    -		root_document = document;
    -	root_document.body.appendChild(root);
    -
    -	//compute best position
    -	var left = options.left || 0;
    -	var top = options.top || 0;
    -	if(options.event)
    -	{
    -		left = (options.event.clientX - 10);
    -		top = (options.event.clientY - 10);
    -		if(options.title)
    -			top -= 20;
    -
    -		if(options.parentMenu)
    -		{
    -			var rect = options.parentMenu.root.getBoundingClientRect();
    -			left = rect.left + rect.width;
    -		}
    -
    -		var body_rect = document.body.getBoundingClientRect();
    -		var root_rect = root.getBoundingClientRect();
    -
    -		if(left > (body_rect.width - root_rect.width - 10))
    -			left = (body_rect.width - root_rect.width - 10);
    -		if(top > (body_rect.height - root_rect.height - 10))
    -			top = (body_rect.height - root_rect.height - 10);
    -	}
    -
    -	root.style.left = left + "px";
    -	root.style.top = top  + "px";
    -
    -	if(options.scale)
    -		root.style.transform = "scale("+options.scale+")";
    -}
    -
    -ContextMenu.prototype.addItem = function( name, value, options )
    -{
    -	var that = this;
    -	options = options || {};
    -
    -	var element = document.createElement("div");
    -	element.className = "litemenu-entry submenu";
    -
    -	var disabled = false;
    -
    -	if(value === null)
    -	{
    -		element.classList.add("separator");
    -		//element.innerHTML = "<hr/>"
    -		//continue;
    -	}
    -	else
    -	{
    -		element.innerHTML = value && value.title ? value.title : name;
    -		element.value = value;
    -
    -		if(value)
    -		{
    -			if(value.disabled)
    -			{
    -				disabled = true;
    -				element.classList.add("disabled");
    -			}
    -			if(value.submenu || value.has_submenu)
    -				element.classList.add("has_submenu");
    -		}
    -
    -		if(typeof(value) == "function")
    -		{
    -			element.dataset["value"] = name;
    -			element.onclick_callback = value;
    -		}
    -		else
    -			element.dataset["value"] = value;
    -
    -		if(value.className)
    -			element.className += " " + value.className;
    -	}
    -
    -	this.root.appendChild(element);
    -	if(!disabled)
    -		element.addEventListener("click", inner_onclick);
    -	if(options.autoopen)
    -		element.addEventListener("mouseenter", inner_over);
    -
    -	function inner_over(e)
    -	{
    -		var value = this.value;
    -		if(!value || !value.has_submenu)
    -			return;
    -		//if it is a submenu, autoopen like the item was clicked
    -		inner_onclick.call(this,e);
    -	}
    -
    -	//menu option clicked
    -	function inner_onclick(e) {
    -		var value = this.value;
    -		var close_parent = true;
    -
    -		if(that.current_submenu)
    -			that.current_submenu.close(e);
    -
    -		//global callback
    -		if(options.callback)
    -		{
    -			var r = options.callback.call( this, value, options, e, that, options.node );
    -			if(r === true)
    -				close_parent = false;
    -		}
    -
    -		//special cases
    -		if(value)
    -		{
    -			if (value.callback && !options.ignore_item_callbacks && value.disabled !== true )  //item callback
    -			{
    -				var r = value.callback.call( this, value, options, e, that, options.extra );
    -				if(r === true)
    -					close_parent = false;
    -			}
    -			if(value.submenu)
    -			{
    -				if(!value.submenu.options)
    -					throw("ContextMenu submenu needs options");
    -				var submenu = new that.constructor( value.submenu.options, {
    -					callback: value.submenu.callback,
    -					event: e,
    -					parentMenu: that,
    -					ignore_item_callbacks: value.submenu.ignore_item_callbacks,
    -					title: value.submenu.title,
    -					extra: value.submenu.extra,
    -					autoopen: options.autoopen
    -				});
    -				close_parent = false;
    -			}
    -		}
    -
    -		if(close_parent && !that.lock)
    -			that.close();
    -	}
    -
    -	return element;
    -}
    -
    -ContextMenu.prototype.close = function(e, ignore_parent_menu)
    -{
    -	if(this.root.parentNode)
    -		this.root.parentNode.removeChild( this.root );
    -	if(this.parentMenu && !ignore_parent_menu)
    -	{
    -		this.parentMenu.lock = false;
    -		this.parentMenu.current_submenu = null;
    -		if( e === undefined )
    -			this.parentMenu.close();
    -		else if( e && !ContextMenu.isCursorOverElement( e, this.parentMenu.root) )
    -		{
    -			ContextMenu.trigger( this.parentMenu.root, "mouseleave", e );
    -		}
    -	}
    -	if(this.current_submenu)
    -		this.current_submenu.close(e, true);
    -
    -	if(this.root.closing_timer)
    -		clearTimeout( this.root.closing_timer );
    -}
    -
    -//this code is used to trigger events easily (used in the context menu mouseleave
    -ContextMenu.trigger = function( element, event_name, params, origin )
    -{
    -	var evt = document.createEvent( 'CustomEvent' );
    -	evt.initCustomEvent( event_name, true,true, params ); //canBubble, cancelable, detail
    -	evt.srcElement = origin;
    -	if( element.dispatchEvent )
    -		element.dispatchEvent( evt );
    -	else if( element.__events )
    -		element.__events.dispatchEvent( evt );
    -	//else nothing seems binded here so nothing to do
    -	return evt;
    -}
    -
    -//returns the top most menu
    -ContextMenu.prototype.getTopMenu = function()
    -{
    -	if( this.options.parentMenu )
    -		return this.options.parentMenu.getTopMenu();
    -	return this;
    -}
    -
    -ContextMenu.prototype.getFirstEvent = function()
    -{
    -	if( this.options.parentMenu )
    -		return this.options.parentMenu.getFirstEvent();
    -	return this.options.event;
    -}
    -
    -
    -
    -ContextMenu.isCursorOverElement = function( event, element )
    -{
    -	var left = event.clientX;
    -	var top = event.clientY;
    -	var rect = element.getBoundingClientRect();
    -	if(!rect)
    -		return false;
    -	if(top > rect.top && top < (rect.top + rect.height) &&
    -		left > rect.left && left < (rect.left + rect.width) )
    -		return true;
    -	return false;
    -}
    -
    -
    -
    -LiteGraph.ContextMenu = ContextMenu;
    -
    -LiteGraph.closeAllContextMenus = function( ref_window )
    -{
    -	ref_window = ref_window || window;
    -
    -	var elements = ref_window.document.querySelectorAll(".litecontextmenu");
    -	if(!elements.length)
    -		return;
    -
    -	var result = [];
    -	for(var i = 0; i < elements.length; i++)
    -		result.push(elements[i]);
    -
    -	for(var i in result)
    -	{
    -		if(result[i].close)
    -			result[i].close();
    -		else if(result[i].parentNode)
    -			result[i].parentNode.removeChild( result[i] );
    -	}
    -}
    -
    -LiteGraph.extendClass = function ( target, origin )
    -{
    -	for(var i in origin) //copy class properties
    -	{
    -		if(target.hasOwnProperty(i))
    -			continue;
    -		target[i] = origin[i];
    -	}
    -
    -	if(origin.prototype) //copy prototype properties
    -		for(var i in origin.prototype) //only enumerables
    -		{
    -			if(!origin.prototype.hasOwnProperty(i))
    -				continue;
    -
    -			if(target.prototype.hasOwnProperty(i)) //avoid overwritting existing ones
    -				continue;
    -
    -			//copy getters
    -			if(origin.prototype.__lookupGetter__(i))
    -				target.prototype.__defineGetter__(i, origin.prototype.__lookupGetter__(i));
    -			else
    -				target.prototype[i] = origin.prototype[i];
    -
    -			//and setters
    -			if(origin.prototype.__lookupSetter__(i))
    -				target.prototype.__defineSetter__(i, origin.prototype.__lookupSetter__(i));
    -		}
    -}
    -
    -//used to create nodes from wrapping functions
    -LiteGraph.getParameterNames = function(func) {
    -    return (func + '')
    -      .replace(/[/][/].*$/mg,'') // strip single-line comments
    -      .replace(/\s+/g, '') // strip white space
    -      .replace(/[/][*][^/*]*[*][/]/g, '') // strip multi-line comments  /**/
    -      .split('){', 1)[0].replace(/^[^(]*[(]/, '') // extract the parameters
    -      .replace(/=[^,]+/g, '') // strip any ES6 defaults
    -      .split(',').filter(Boolean); // split & filter [""]
    -}
    -
    -Math.clamp = function(v,a,b) { return (a > v ? a : (b < v ? b : v)); }
    -
    -if( typeof(window) != "undefined" && !window["requestAnimationFrame"] )
    -{
    -	window.requestAnimationFrame = window.webkitRequestAnimationFrame ||
    -		  window.mozRequestAnimationFrame    ||
    -		  (function( callback ){
    -			window.setTimeout(callback, 1000 / 60);
    -		  });
    -}
    -
    -})(this);
    -
    -if(typeof(exports) != "undefined")
    -	exports.LiteGraph = this.LiteGraph;
    -
    -    
    -
    -
    -
    -
    -
    -
    -
    - - - - - - - - - - diff --git a/doc/files/index.html b/doc/files/index.html deleted file mode 100755 index 487fe15b2..000000000 --- a/doc/files/index.html +++ /dev/null @@ -1,10 +0,0 @@ - - - - Redirector - - - - Click here to redirect - - diff --git a/doc/index.html b/doc/index.html deleted file mode 100755 index 83dc8558b..000000000 --- a/doc/index.html +++ /dev/null @@ -1,119 +0,0 @@ - - - - - - - - - - - - - -
    -
    -
    -

    -
    -
    - API Docs for: -
    -
    -
    - -
    - -
    -
    -
    - Show: - - - - - - - -
    - -
    -
    -
    -
    -
    -

    - Browse to a module or class using the sidebar to view its API documentation. -

    - -

    Keyboard Shortcuts

    - -
      -
    • Press s to focus the API search box.

    • - -
    • Use Up and Down to select classes, modules, and search results.

    • - -
    • With the API search box or sidebar focused, use -Left or -Right to switch sidebar tabs.

    • - -
    • With the API search box or sidebar focused, use Ctrl+Left and Ctrl+Right to switch sidebar tabs.

    • -
    -
    -
    - - -
    -
    -
    -
    -
    -
    - - - - - - - - - - diff --git a/doc/modules/index.html b/doc/modules/index.html deleted file mode 100755 index 487fe15b2..000000000 --- a/doc/modules/index.html +++ /dev/null @@ -1,10 +0,0 @@ - - - - Redirector - - - - Click here to redirect - - diff --git a/editor/demodata/audio.wav b/editor/demodata/audio.wav deleted file mode 100755 index 1cccb5a35..000000000 Binary files a/editor/demodata/audio.wav and /dev/null differ diff --git a/editor/demodata/impulse.wav b/editor/demodata/impulse.wav deleted file mode 100755 index 3ce6a1b04..000000000 Binary files a/editor/demodata/impulse.wav and /dev/null differ diff --git a/editor/demodata/video.webm b/editor/demodata/video.webm deleted file mode 100755 index 0c8d15d40..000000000 Binary files a/editor/demodata/video.webm and /dev/null differ diff --git a/editor/editor_mobile.html b/editor/editor_mobile.html deleted file mode 100755 index da5b1e104..000000000 --- a/editor/editor_mobile.html +++ /dev/null @@ -1,144 +0,0 @@ - - - - - LiteGraph - - - - - - - - - -
    -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/editor/examples/audio.json b/editor/examples/audio.json deleted file mode 100755 index 6945e8a36..000000000 --- a/editor/examples/audio.json +++ /dev/null @@ -1 +0,0 @@ -{"last_node_id":16,"last_link_id":16,"nodes":[{"id":9,"type":"widget/knob","pos":[440,81],"size":[80,100],"flags":{},"mode":0,"outputs":[{"name":"","type":"number","links":[8,9]}],"properties":{"min":0,"max":4,"value":0.24000000000000002,"wcolor":"#7AF","size":50},"boxcolor":"rgba(128,128,128,1.0)"},{"id":10,"type":"basic/watch","pos":[537,81],"size":{"0":140,"1":26},"flags":{},"mode":0,"inputs":[{"name":"value","type":0,"link":9,"label":"0.000"}],"outputs":[{"name":"value","type":0,"links":null,"label":""}],"properties":{"value":0.24000000000000002}},{"id":1,"type":"audio/destination","pos":[699,83],"size":{"0":140,"1":26},"flags":{},"mode":0,"inputs":[{"name":"in","type":"audio","link":1}],"properties":{}},{"id":6,"type":"widget/knob","pos":[116,179],"size":[80,100],"flags":{},"mode":0,"outputs":[{"name":"","type":"number","links":[5]}],"properties":{"min":0,"max":1,"value":0.5099999999999996,"wcolor":"#7AF","size":50},"boxcolor":"rgba(128,128,128,1.0)"},{"id":0,"type":"audio/source","pos":[272,192],"size":{"0":140,"1":86},"flags":{},"mode":0,"inputs":[{"name":"gain","type":"number","link":5},{"name":"Play","type":-1,"link":6},{"name":"Stop","type":-1,"link":7},{"name":"playbackRate","type":"number","link":8}],"outputs":[{"name":"out","type":"audio","links":[0]}],"properties":{"src":"demodata/audio.wav","gain":0.5,"loop":true,"autoplay":true,"playbackRate":0.24000000000000002},"boxcolor":"#AA4"},{"id":2,"type":"audio/biquadfilter","pos":[442,228],"size":{"0":140,"1":46},"flags":{},"mode":0,"inputs":[{"name":"in","type":"audio","link":0},{"name":"frequency","type":"number","link":4}],"outputs":[{"name":"out","type":"audio","links":[1,2]}],"properties":{"frequency":350,"detune":0,"Q":1,"type":"lowpass"}},{"id":3,"type":"audio/analyser","pos":[704,231],"size":{"0":140,"1":46},"flags":{},"mode":0,"inputs":[{"name":"in","type":"audio","link":2}],"outputs":[{"name":"freqs","type":"array","links":[3,10]},{"name":"samples","type":"array","links":null}],"properties":{"fftSize":2048,"minDecibels":-100,"maxDecibels":-10,"smoothingTimeConstant":0.5}},{"id":11,"type":"audio/signal","pos":[882,395],"size":{"0":140,"1":46},"flags":{},"mode":0,"inputs":[{"name":"freqs","type":"array","link":10},{"name":"band","type":"number","link":11}],"outputs":[{"name":"signal","type":"number","links":[12]}],"properties":{"band":440,"amplitude":1,"samplerate":44100}},{"id":4,"type":"audio/visualization","pos":[885,503],"size":{"0":140,"1":46},"flags":{},"mode":0,"inputs":[{"name":"freqs","type":"array","link":3},{"name":"mark","type":"number","link":13}],"properties":{"continuous":true,"mark":12000.000000000005,"samplerate":44100}},{"id":5,"type":"widget/knob","pos":[112,314],"size":[80,100],"flags":{},"mode":0,"outputs":[{"name":"","type":"number","links":[4]}],"properties":{"min":0,"max":20000,"value":14800.00000000001,"wcolor":"#7AF","size":50},"boxcolor":"rgba(128,128,128,1.0)"},{"id":13,"type":"basic/watch","pos":[110,458],"size":{"0":140,"1":26},"flags":{},"mode":0,"inputs":[{"name":"value","type":0,"link":12,"label":"0.000"}],"outputs":[{"name":"value","type":0,"links":[14],"label":""}],"title":"Max. Signal","properties":{"value":0.3843137254901945}},{"id":14,"type":"widget/progress","pos":[300,460],"size":{"0":140,"1":26},"flags":{},"mode":0,"inputs":[{"name":"","type":"number","link":14}],"properties":{"min":0,"max":1,"value":0.3843137254901945,"wcolor":"#AAF"}},{"id":12,"type":"widget/knob","pos":[460,458],"size":[80,100],"flags":{},"mode":0,"outputs":[{"name":"","type":"number","links":[11,13,15]}],"properties":{"min":0,"max":24000,"value":12000.000000000005,"wcolor":"#7AF","size":50},"boxcolor":"rgba(128,128,128,1.0)"},{"id":15,"type":"basic/watch","pos":[888,598],"size":{"0":140,"1":26},"flags":{},"mode":0,"inputs":[{"name":"value","type":0,"link":15,"label":"0.000"}],"outputs":[{"name":"value","type":0,"links":null,"label":""}],"properties":{"value":12000.000000000005}},{"id":7,"type":"widget/button","pos":[113,83],"size":{"0":141,"1":60},"flags":{},"mode":0,"outputs":[{"name":"clicked","type":-1,"links":[6]}],"properties":{"text":"Play","font_size":30,"message":"","font":"40px Arial"}},{"id":8,"type":"widget/button","pos":[273,83],"size":{"0":143,"1":62},"flags":{},"mode":0,"outputs":[{"name":"clicked","type":-1,"links":[7]}],"properties":{"text":"Stop","font_size":30,"message":"","font":"40px Arial"}}],"links":[[0,0,0,2,0,null],[1,2,0,1,0,null],[2,2,0,3,0,null],[3,3,0,4,0,null],[4,5,0,2,1,null],[5,6,0,0,0,null],[6,7,0,0,1,null],[7,8,0,0,2,null],[8,9,0,0,3,null],[9,9,0,10,0,null],[10,3,0,11,0,null],[11,12,0,11,1,null],[12,11,0,13,0,null],[13,12,0,4,1,null],[14,13,0,14,0,null],[15,12,0,15,0,null]],"groups":[],"config":{}} \ No newline at end of file diff --git a/editor/examples/audio_delay.json b/editor/examples/audio_delay.json deleted file mode 100755 index 74ca05f23..000000000 --- a/editor/examples/audio_delay.json +++ /dev/null @@ -1 +0,0 @@ -{"last_node_id":7,"last_link_id":7,"nodes":[{"id":6,"type":"widget/knob","pos":[199,296],"size":[64,84],"flags":{},"order":3,"mode":0,"outputs":[{"name":"","type":"number","links":[6]}],"properties":{"min":0,"max":2,"value":0.8799999999999999,"color":"#7AF","precision":2,"wcolor":"#7AF","size":50},"boxcolor":"rgba(112,112,112,1.0)"},{"id":0,"type":"audio/source","pos":[195,187],"size":{"0":137,"1":33},"flags":{},"order":0,"mode":0,"inputs":[{"name":"gain","type":"number","link":null}],"outputs":[{"name":"out","type":"audio","links":[0,1]}],"properties":{"src":"demodata/audio.wav","gain":0.5,"loop":true,"autoplay":true,"playbackRate":1},"boxcolor":"#AA4"},{"id":4,"type":"widget/knob","pos":[408,59],"size":[82,75],"flags":{},"order":1,"mode":0,"outputs":[{"name":"","type":"number","links":[4]}],"properties":{"min":0,"max":1,"value":0.24000000000000007,"color":"#7AF","precision":2,"wcolor":"#7AF","size":50},"boxcolor":"rgba(128,128,128,1.0)"},{"id":2,"type":"audio/delay","pos":[385,255],"size":{"0":143,"1":49},"flags":{},"order":4,"mode":0,"inputs":[{"name":"in","type":"audio","link":1},{"name":"time","type":"number","link":6}],"outputs":[{"name":"out","type":"audio","links":[2]}],"properties":{"delayTime":0.5,"time":1}},{"id":5,"type":"widget/knob","pos":[433,371],"size":[79,79],"flags":{},"order":2,"mode":0,"outputs":[{"name":"","type":"number","links":[5]}],"properties":{"min":0,"max":1,"value":0.5199999999999996,"color":"#7AF","precision":2,"wcolor":"#7AF","size":50},"boxcolor":"rgba(128,128,128,1.0)"},{"id":1,"type":"audio/mixer","pos":[657,180],"size":{"0":147,"1":91},"flags":{},"order":5,"mode":0,"inputs":[{"name":"in1","type":"audio","link":0},{"name":"in1 gain","type":"number","link":4},{"name":"in2","type":"audio","link":2},{"name":"in2 gain","type":"number","link":5}],"outputs":[{"name":"out","type":"audio","links":[3]}],"properties":{"gain1":0.5,"gain2":0.8}},{"id":3,"type":"audio/destination","pos":[911,180],"size":{"0":145,"1":30},"flags":{},"order":6,"mode":0,"inputs":[{"name":"in","type":"audio","link":3}],"properties":{}}],"links":[[0,0,0,1,0,null],[1,0,0,2,0,null],[2,2,0,1,2,null],[3,1,0,3,0,null],[4,4,0,1,1,null],[5,5,0,1,3,null],[6,6,0,2,1,null]],"groups":[],"config":{},"version":0.4} \ No newline at end of file diff --git a/editor/examples/audio_reverb.json b/editor/examples/audio_reverb.json deleted file mode 100755 index 65f23c481..000000000 --- a/editor/examples/audio_reverb.json +++ /dev/null @@ -1 +0,0 @@ -{"last_node_id":8,"last_link_id":9,"nodes":[{"id":4,"type":"widget/knob","pos":[408,59],"size":[81,93],"flags":{},"order":2,"mode":0,"outputs":[{"name":"","type":"number","links":[4]}],"title":"Main","properties":{"min":0,"max":1,"value":0.21000000000000002,"color":"#7AF","precision":2,"wcolor":"#7AF","size":50},"boxcolor":"rgba(128,128,128,1.0)"},{"id":5,"type":"widget/knob","pos":[398,350],"size":[84,100],"flags":{},"order":1,"mode":0,"outputs":[{"name":"","type":"number","links":[5]}],"title":"Reverb","properties":{"min":0,"max":1,"value":0.4099999999999999,"color":"#7AF","precision":2,"wcolor":"#7AF","size":50},"boxcolor":"rgba(128,128,128,1.0)"},{"id":7,"type":"audio/convolver","pos":[421,253],"size":{"0":140,"1":31},"flags":{},"order":3,"mode":0,"inputs":[{"name":"in","type":"audio","link":7}],"outputs":[{"name":"out","type":"audio","links":[8]}],"properties":{"impulse_src":"demodata/impulse.wav","normalize":true}},{"id":1,"type":"audio/mixer","pos":[655,183],"size":{"0":145,"1":94},"flags":{},"order":4,"mode":0,"inputs":[{"name":"in1","type":"audio","link":0},{"name":"in1 gain","type":"number","link":4},{"name":"in2","type":"audio","link":8},{"name":"in2 gain","type":"number","link":5}],"outputs":[{"name":"out","type":"audio","links":[3]}],"properties":{"gain1":0.5,"gain2":0.8}},{"id":3,"type":"audio/destination","pos":[911,180],"size":{"0":141,"1":34},"flags":{},"order":5,"mode":0,"inputs":[{"name":"in","type":"audio","link":3}],"properties":{}},{"id":0,"type":"audio/source","pos":[195,187],"size":{"0":143,"1":30},"flags":{},"order":0,"mode":0,"inputs":[{"name":"gain","type":"number","link":null}],"outputs":[{"name":"out","type":"audio","links":[0,7]}],"properties":{"src":"demodata/audio.wav","gain":0.5,"loop":true,"autoplay":true,"playbackRate":1},"boxcolor":"#AA4"}],"links":[[0,0,0,1,0,null],[1,0,0,2,0,null],[3,1,0,3,0,null],[4,4,0,1,1,null],[5,5,0,1,3,null],[6,6,0,2,1,null],[7,0,0,7,0,null],[8,7,0,1,2,null]],"groups":[],"config":{},"version":0.4} \ No newline at end of file diff --git a/editor/examples/benchmark.json b/editor/examples/benchmark.json deleted file mode 100755 index ad2094e74..000000000 --- a/editor/examples/benchmark.json +++ /dev/null @@ -1 +0,0 @@ -{"last_node_id":60,"last_link_id":50,"nodes":[{"id":9,"type":"features/slots","pos":[846,473],"size":[100,40],"flags":{"horizontal":true},"mode":0,"inputs":[{"name":"C","type":"number","link":6}],"outputs":[{"name":"A","type":"number","links":null},{"name":"B","type":"number","links":null}],"properties":{}},{"id":8,"type":"features/slots","pos":[671,475],"size":[100,40],"flags":{"horizontal":true},"mode":0,"inputs":[{"name":"C","type":"number","link":10}],"outputs":[{"name":"A","type":"number","links":null},{"name":"B","type":"number","links":null}],"properties":{}},{"id":18,"type":"features/slots","pos":[751.5619834710739,854.9586776859505],"size":[100,40],"flags":{"horizontal":true},"mode":0,"inputs":[{"name":"C","type":"number","link":16}],"outputs":[{"name":"A","type":"number","links":[17,18]},{"name":"B","type":"number","links":[]}],"properties":{}},{"id":28,"type":"features/slots","pos":[1386.361032999997,389.62936599999995],"size":[100,40],"flags":{"horizontal":true},"mode":0,"inputs":[{"name":"C","type":"number","link":23}],"outputs":[{"name":"A","type":"number","links":[24,25]},{"name":"B","type":"number","links":[]}],"properties":{}},{"id":35,"type":"features/shape","pos":[1555.6387189504107,741.7260602148749],"size":{"0":140,"1":39},"flags":{},"mode":0,"inputs":[{"name":"","type":"number","link":26}],"outputs":[{"name":"","type":"number","links":null}],"properties":{}},{"id":38,"type":"features/slots","pos":[1461.838718950411,987.9260602148759],"size":[100,40],"flags":{"horizontal":true},"mode":0,"inputs":[{"name":"C","type":"number","link":28}],"outputs":[{"name":"A","type":"number","links":[29,30]},{"name":"B","type":"number","links":[]}],"properties":{}},{"id":39,"type":"features/slots","pos":[1376.8387189504106,1088.9260602148756],"size":[100,40],"flags":{"horizontal":true},"mode":0,"inputs":[{"name":"C","type":"number","link":29}],"outputs":[{"name":"A","type":"number","links":null},{"name":"B","type":"number","links":null}],"properties":{}},{"id":40,"type":"features/slots","pos":[1551.838718950411,1086.9260602148754],"size":[100,40],"flags":{"horizontal":true},"mode":0,"inputs":[{"name":"C","type":"number","link":30}],"outputs":[{"name":"A","type":"number","links":null},{"name":"B","type":"number","links":null}],"properties":{}},{"id":45,"type":"features/slots","pos":[588.5619834710739,1055.9586776859496],"size":[100,40],"flags":{"horizontal":true},"mode":0,"inputs":[{"name":"C","type":"number","link":33}],"outputs":[{"name":"A","type":"number","links":null},{"name":"B","type":"number","links":null}],"properties":{}},{"id":46,"type":"features/slots","pos":[721.5619834710739,1058.9586776859496],"size":[100,40],"flags":{"horizontal":true},"mode":0,"inputs":[{"name":"C","type":"number","link":34}],"outputs":[{"name":"A","type":"number","links":null},{"name":"B","type":"number","links":null}],"properties":{}},{"id":19,"type":"features/slots","pos":[666.5619834710739,955.9586776859505],"size":[100,40],"flags":{"horizontal":true},"mode":0,"inputs":[{"name":"C","type":"number","link":17}],"outputs":[{"name":"A","type":"number","links":[33]},{"name":"B","type":"number","links":[34]}],"properties":{}},{"id":47,"type":"features/slots","pos":[846.5619834710739,1060.9586776859496],"size":[100,40],"flags":{"horizontal":true},"mode":0,"inputs":[{"name":"C","type":"number","link":35}],"outputs":[{"name":"A","type":"number","links":null},{"name":"B","type":"number","links":null}],"properties":{}},{"id":48,"type":"features/slots","pos":[976.5619834710739,1059.9586776859496],"size":[100,40],"flags":{"horizontal":true},"mode":0,"inputs":[{"name":"C","type":"number","link":36}],"outputs":[{"name":"A","type":"number","links":null},{"name":"B","type":"number","links":null}],"properties":{}},{"id":20,"type":"features/slots","pos":[841.5619834710739,953.9586776859505],"size":[100,40],"flags":{"horizontal":true},"mode":0,"inputs":[{"name":"C","type":"number","link":18}],"outputs":[{"name":"A","type":"number","links":[35]},{"name":"B","type":"number","links":[36]}],"properties":{}},{"id":29,"type":"features/slots","pos":[1307,490],"size":[100,40],"flags":{"horizontal":true},"mode":0,"inputs":[{"name":"C","type":"number","link":24}],"outputs":[{"name":"A","type":"number","links":[37]},{"name":"B","type":"number","links":[38]}],"properties":{}},{"id":30,"type":"features/slots","pos":[1476.3610329999974,488.62936599999995],"size":[100,40],"flags":{"horizontal":true},"mode":0,"inputs":[{"name":"C","type":"number","link":25}],"outputs":[{"name":"A","type":"number","links":[39]},{"name":"B","type":"number","links":[40]}],"properties":{}},{"id":7,"type":"features/slots","pos":[756,374],"size":[100,40],"flags":{"horizontal":true,"collapsed":false},"mode":0,"inputs":[{"name":"C","type":"number","link":13}],"outputs":[{"name":"A","type":"number","links":[10]},{"name":"B","type":"number","links":[6]}],"properties":{}},{"id":34,"type":"features/widgets","pos":[1054,349],"size":{"0":189,"1":176},"flags":{"collapsed":false},"mode":0,"outputs":[{"name":"","type":"number","links":[23]}],"properties":{}},{"id":12,"type":"input/gamepad","pos":[607,204],"size":{"0":155,"1":69},"flags":{"collapsed":false},"mode":0,"outputs":[{"name":"left_x_axis","type":"number","links":null},{"name":"left_y_axis","type":"number","links":null},{"name":"button_pressed","type":-1,"links":[12]}],"properties":{"gamepad_index":0,"threshold":0.1}},{"id":4,"type":"math/operation","pos":[604,106],"size":[164,51],"flags":{"collapsed":false},"mode":0,"inputs":[{"name":"A","type":"number","link":2},{"name":"B","type":"number","link":9}],"outputs":[{"name":"=","type":"number","links":[1]}],"properties":{"A":2,"B":0.5,"OP":"+"},"shape":2},{"id":2,"type":"features/shape","pos":[867,112],"size":{"0":140,"1":39},"flags":{},"mode":0,"inputs":[{"name":"","type":"number","link":1}],"outputs":[{"name":"","type":"number","links":null}],"properties":{}},{"id":13,"type":"events/log","pos":[868,215],"size":{"0":143,"1":30},"flags":{},"mode":0,"inputs":[{"name":"event","type":-1,"link":12}],"properties":{}},{"id":14,"type":"features/widgets","pos":[432,357],"size":{"0":209,"1":178},"flags":{},"mode":0,"outputs":[{"name":"","type":"number","links":[13]}],"properties":{}},{"id":24,"type":"features/widgets","pos":[429,841],"size":{"0":184.9173583984375,"1":176.34710693359375},"flags":{},"mode":0,"outputs":[{"name":"","type":"number","links":[16]}],"properties":{}},{"id":44,"type":"features/widgets","pos":[1154,942],"size":{"0":191,"1":174},"flags":{},"mode":0,"outputs":[{"name":"","type":"number","links":[28]}],"properties":{}},{"id":36,"type":"math/operation","pos":[1333,729],"size":[144,45],"flags":{},"mode":0,"inputs":[{"name":"A","type":"number","link":27},{"name":"B","type":"number","link":null}],"outputs":[{"name":"=","type":"number","links":[26]}],"properties":{"A":2,"B":1,"OP":"+"},"shape":2},{"id":42,"type":"input/gamepad","pos":[1316,821],"size":{"0":182.3612823486328,"1":69.27394104003906},"flags":{"collapsed":false},"mode":0,"outputs":[{"name":"left_x_axis","type":"number","links":null},{"name":"left_y_axis","type":"number","links":null},{"name":"button_pressed","type":-1,"links":[]}],"properties":{"gamepad_index":0,"threshold":0.1}},{"id":43,"type":"events/log","pos":[1562,845],"size":{"0":142.16128540039062,"1":31.07394027709961},"flags":{},"mode":0,"inputs":[{"name":"event","type":-1,"link":null}],"properties":{}},{"id":41,"type":"widget/knob","pos":[1155,824],"size":[54,74],"flags":{},"mode":0,"outputs":[{"name":"","type":"number","links":[]}],"properties":{"min":0,"max":1,"value":0.5,"color":"#7AF","precision":2,"wcolor":"#7AF","size":50},"boxcolor":"rgba(128,128,128,1.0)"},{"id":37,"type":"math/operation","pos":[1138,739],"size":[148,44],"flags":{},"mode":0,"inputs":[{"name":"A","type":"number","link":null},{"name":"B","type":"number","link":null}],"outputs":[{"name":"=","type":"number","links":[27]}],"properties":{"A":1,"B":1,"OP":"+"},"color":"#233","bgcolor":"#355","shape":2},{"id":33,"type":"events/log","pos":[1493.3610329999974,224.629366],"size":{"0":147.6389617919922,"1":29.370634078979492},"flags":{},"mode":0,"inputs":[{"name":"event","type":-1,"link":32}],"properties":{}},{"id":26,"type":"math/operation","pos":[1268,128],"size":[144,50],"flags":{},"mode":0,"inputs":[{"name":"A","type":"number","link":22},{"name":"B","type":"number","link":31}],"outputs":[{"name":"=","type":"number","links":[21]}],"properties":{"A":2,"B":0.5,"OP":"+"},"shape":2},{"id":5,"type":"math/operation","pos":[437,114],"size":[140,48],"flags":{"collapsed":true},"mode":0,"inputs":[{"name":"A","type":"number","link":null},{"name":"B","type":"number","link":null}],"outputs":[{"name":"=","type":"number","links":[2]}],"properties":{"A":1,"B":1,"OP":"+"},"shape":2},{"id":27,"type":"math/operation","pos":[1061,129],"size":[144,44],"flags":{"collapsed":true},"mode":0,"inputs":[{"name":"A","type":"number","link":null},{"name":"B","type":"number","link":null}],"outputs":[{"name":"=","type":"number","links":[22]}],"properties":{"A":1,"B":1,"OP":"+"},"shape":2},{"id":49,"type":"features/slots","pos":[1210,589],"size":[100,40],"flags":{"horizontal":true},"mode":0,"inputs":[{"name":"C","type":"number","link":37}],"outputs":[{"name":"A","type":"number","links":null},{"name":"B","type":"number","links":null}],"properties":{}},{"id":50,"type":"features/slots","pos":[1342,591],"size":[100,40],"flags":{"horizontal":true},"mode":0,"inputs":[{"name":"C","type":"number","link":38}],"outputs":[{"name":"A","type":"number","links":null},{"name":"B","type":"number","links":null}],"properties":{}},{"id":51,"type":"features/slots","pos":[1471,590],"size":[100,40],"flags":{"horizontal":true},"mode":0,"inputs":[{"name":"C","type":"number","link":39}],"outputs":[{"name":"A","type":"number","links":null},{"name":"B","type":"number","links":null}],"properties":{}},{"id":52,"type":"features/slots","pos":[1597,588],"size":[100,40],"flags":{"horizontal":true},"mode":0,"inputs":[{"name":"C","type":"number","link":40}],"outputs":[{"name":"A","type":"number","links":null},{"name":"B","type":"number","links":null}],"properties":{}},{"id":25,"type":"features/shape","pos":[1500,116],"size":{"0":140,"1":39},"flags":{},"mode":0,"inputs":[{"name":"","type":"number","link":21}],"outputs":[{"name":"","type":"number","links":null}],"properties":{}},{"id":10,"type":"widget/knob","pos":[435,161],"size":[91,111],"flags":{},"mode":0,"outputs":[{"name":"","type":"number","links":[9]}],"properties":{"min":0,"max":1,"value":0.5,"color":"#7AF","precision":2,"wcolor":"#7AF","size":50},"boxcolor":"rgba(128,128,128,1.0)"},{"id":31,"type":"widget/knob","pos":[1058,182],"size":[95,114],"flags":{"collapsed":false},"mode":0,"outputs":[{"name":"","type":"number","links":[31]}],"properties":{"min":0,"max":1,"value":0.5,"color":"#7AF","precision":2,"wcolor":"#7AF","size":50},"boxcolor":"rgba(128,128,128,1.0)"},{"id":32,"type":"input/gamepad","pos":[1232,231],"size":{"0":191.6389617919922,"1":73.37063598632812},"flags":{"collapsed":false},"mode":0,"outputs":[{"name":"left_x_axis","type":"number","links":[46]},{"name":"left_y_axis","type":"number","links":[47]},{"name":"button_pressed","type":-1,"links":[32]}],"properties":{"gamepad_index":0,"threshold":0.1}},{"id":57,"type":"graphics/plot","pos":[1722,335],"size":{"0":140,"1":86},"flags":{},"mode":0,"inputs":[{"name":"A","type":"Number","link":48},{"name":"B","type":"Number","link":null},{"name":"C","type":"Number","link":null},{"name":"D","type":"Number","link":null}],"properties":{"scale":2}},{"id":21,"type":"widget/knob","pos":[435,591],"size":[72,90],"flags":{"collapsed":false},"mode":0,"outputs":[{"name":"","type":"number","links":[43]}],"properties":{"min":0,"max":1,"value":0.7297408895318863,"color":"#7AF","precision":2,"wcolor":"#7AF","size":50},"boxcolor":"rgba(186,186,186,1.0)"},{"id":16,"type":"math/operation","pos":[583,611],"size":[140,47],"flags":{"collapsed":false},"mode":0,"inputs":[{"name":"A","type":"number","link":43},{"name":"B","type":"number","link":45}],"outputs":[{"name":"=","type":"number","links":[44]}],"properties":{"A":0.7297408895318863,"B":5000,"OP":"*"},"shape":2},{"id":54,"type":"events/timer","pos":[764,620],"size":{"0":147,"1":30},"flags":{"collapsed":false},"mode":0,"inputs":[{"name":"interval","type":"number","link":44}],"outputs":[{"name":"on_tick","type":-1,"links":[42]}],"properties":{"interval":1000,"event":"tick"},"boxcolor":"#222","shape":2},{"id":23,"type":"events/log","pos":[961,622],"size":{"0":137,"1":28},"flags":{},"mode":0,"inputs":[{"name":"event","type":-1,"link":42}],"properties":{}},{"id":56,"type":"graph/subgraph","pos":[1543,336],"size":{"0":140,"1":86},"flags":{},"mode":0,"inputs":[{"name":"enabled","type":"boolean","link":49},{"name":"AxisX","type":0,"link":46},{"name":"AxisY","type":0,"link":47}],"outputs":[{"name":"sum","type":0,"links":[48]}],"properties":{"enabled":true},"subgraph":{"last_node_id":6,"last_link_id":4,"nodes":[{"id":4,"type":"graph/output","pos":[1655,311],"size":[180,60],"flags":{},"mode":0,"inputs":[{"name":"","type":0,"link":3}],"properties":{"name":"sum","type":0}},{"id":2,"type":"graph/input","pos":[1227,233],"size":[180,60],"flags":{},"mode":0,"outputs":[{"name":"","type":"","links":[1]}],"properties":{"name":"AxisX","type":""}},{"id":3,"type":"graph/input","pos":[1234,341],"size":[180,60],"flags":{},"mode":0,"outputs":[{"name":"","type":"","links":[2]}],"properties":{"name":"AxisY","type":""}},{"id":6,"type":"math/operation","pos":[1496,268],"size":[100,60],"flags":{},"mode":0,"inputs":[{"name":"A","type":"number","link":1},{"name":"B","type":"number","link":2}],"outputs":[{"name":"=","type":"number","links":[3]}],"properties":{"A":1,"B":1,"OP":"+"}}],"links":[[1,2,0,6,0,"number"],[2,3,0,6,1,"number"],[3,6,0,4,0,0]],"groups":[],"config":{},"version":0.4}},{"id":58,"type":"widget/toggle","pos":[1686,131],"size":[160,44],"flags":{},"mode":0,"inputs":[{"name":"","type":"boolean","link":null},{"name":"e","type":-1,"link":null}],"outputs":[{"name":"v","type":"boolean","links":[49]},{"name":"e","type":-1,"links":null}],"properties":{"font":"","value":true}},{"id":60,"type":"widget/number","pos":[626,706],"size":[74,54],"flags":{},"mode":0,"outputs":[{"name":"","type":"number","links":null}],"properties":{"min":-1000,"max":1000,"value":1,"step":1}},{"id":55,"type":"basic/const","pos":[422,738],"size":{"0":139,"1":28},"flags":{"collapsed":false},"mode":0,"outputs":[{"name":"value","type":"number","links":[45],"label":"5000.000"}],"properties":{"value":5000}}],"links":[[1,4,0,2,0,"number"],[2,5,0,4,0,"number"],[6,7,1,9,0,"number"],[9,10,0,4,1,"number"],[10,7,0,8,0,"number"],[12,12,2,13,0,-1],[13,14,0,7,0,"number"],[16,24,0,18,0,"number"],[17,18,0,19,0,"number"],[18,18,0,20,0,"number"],[21,26,0,25,0,"number"],[22,27,0,26,0,"number"],[23,34,0,28,0,"number"],[24,28,0,29,0,"number"],[25,28,0,30,0,"number"],[26,36,0,35,0,"number"],[27,37,0,36,0,"number"],[28,44,0,38,0,"number"],[29,38,0,39,0,"number"],[30,38,0,40,0,"number"],[31,31,0,26,1,"number"],[32,32,2,33,0,-1],[33,19,0,45,0,"number"],[34,19,1,46,0,"number"],[35,20,0,47,0,"number"],[36,20,1,48,0,"number"],[37,29,0,49,0,"number"],[38,29,1,50,0,"number"],[39,30,0,51,0,"number"],[40,30,1,52,0,"number"],[42,54,0,23,0,-1],[43,21,0,16,0,"number"],[44,16,0,54,0,"number"],[45,55,0,16,1,"number"],[46,32,0,56,1,0],[47,32,1,56,2,0],[48,56,0,57,0,"Number"],[49,58,0,56,0,"boolean"]],"groups":[{"title":"Group","bounding":[417,292,564,255],"color":"#3f789e"},{"title":"Group","bounding":[1120,678,642,461],"color":"#A88"},{"title":"Group","bounding":[411,777,679,361],"color":"#8A8"}],"config":{},"version":0.4} \ No newline at end of file diff --git a/editor/examples/copypaste.json b/editor/examples/copypaste.json deleted file mode 100644 index 086476e89..000000000 --- a/editor/examples/copypaste.json +++ /dev/null @@ -1 +0,0 @@ -{"last_node_id":62,"last_link_id":157,"nodes":[{"id":35,"type":"widget/number","pos":[354.0977802999988,-703.6940983999988],"size":[80,60],"flags":{},"order":0,"mode":0,"outputs":[{"name":"","type":"number","links":[32,52,72,94,115,138]}],"properties":{"min":-1000,"max":1000,"value":1,"step":1}},{"id":36,"type":"widget/number","pos":[356.0977802999989,-596.6940983999991],"size":[80,60],"flags":{},"order":1,"mode":0,"outputs":[{"name":"","type":"number","links":[33,53,73,95,116,139]}],"properties":{"min":-1000,"max":1000,"value":1,"step":1}},{"id":37,"type":"widget/number","pos":[359.0977802999989,-495.69409839999975],"size":[80,60],"flags":{},"order":2,"mode":0,"outputs":[{"name":"","type":"number","links":[34,54,74,96,117,140]}],"properties":{"min":-1000,"max":1000,"value":1,"step":1}},{"id":38,"type":"widget/number","pos":[361.0977802999988,-385.69409839999867],"size":[80,60],"flags":{},"order":3,"mode":0,"outputs":[{"name":"","type":"number","links":[35,55,75,97,118,141]}],"properties":{"min":-1000,"max":1000,"value":1,"step":1}},{"id":39,"type":"widget/number","pos":[363.0977802999988,-269.6940983999987],"size":[80,60],"flags":{},"order":4,"mode":0,"outputs":[{"name":"","type":"number","links":[36,56,76,98,119,142]}],"properties":{"min":-1000,"max":1000,"value":1,"step":1}},{"id":40,"type":"widget/number","pos":[366.3589802999999,-157.40999840000026],"size":[80,60],"flags":{},"order":5,"mode":0,"outputs":[{"name":"","type":"number","links":[37,57,77,99,120,143]}],"properties":{"min":-1000,"max":1000,"value":1,"step":1}},{"id":41,"type":"widget/number","pos":[367.3589802999999,-49.40999839999942],"size":[80,60],"flags":{},"order":6,"mode":0,"outputs":[{"name":"","type":"number","links":[38,58,78,100,121,144]}],"properties":{"min":-1000,"max":1000,"value":1,"step":1}},{"id":42,"type":"widget/number","pos":[366.3589802999999,53.59000160000091],"size":[80,60],"flags":{},"order":7,"mode":0,"outputs":[{"name":"","type":"number","links":[39,59,79,101,122,145]}],"properties":{"min":-1000,"max":1000,"value":1,"step":1}},{"id":43,"type":"widget/number","pos":[366.3589802999999,157.59000160000005],"size":[80,60],"flags":{},"order":8,"mode":0,"outputs":[{"name":"","type":"number","links":[40,60,80,102,123,146]}],"properties":{"min":-1000,"max":1000,"value":1,"step":1}},{"id":45,"type":"widget/number","pos":[374,367.58986919999944],"size":[80,60],"flags":{},"order":9,"mode":0,"outputs":[{"name":"","type":"number","links":[42,62,82,104,125,148]}],"properties":{"min":-1000,"max":1000,"value":1,"step":1}},{"id":46,"type":"widget/number","pos":[375,464.58986920000046],"size":[80,60],"flags":{},"order":10,"mode":0,"outputs":[{"name":"","type":"number","links":[43,63,83,105,126,149]}],"properties":{"min":-1000,"max":1000,"value":1,"step":1}},{"id":47,"type":"widget/number","pos":[377,564.5898692000013],"size":[80,60],"flags":{},"order":11,"mode":0,"outputs":[{"name":"","type":"number","links":[44,64,84,106,127,150]}],"properties":{"min":-1000,"max":1000,"value":1,"step":1}},{"id":48,"type":"widget/number","pos":[380,661.5898692000013],"size":[80,60],"flags":{},"order":12,"mode":0,"outputs":[{"name":"","type":"number","links":[45,65,85,107,128,151]}],"properties":{"min":-1000,"max":1000,"value":1,"step":1}},{"id":44,"type":"widget/number","pos":[372,264],"size":[80,60],"flags":{},"order":13,"mode":0,"outputs":[{"name":"","type":"number","links":[41,61,81,103,124,147]}],"properties":{"min":-1000,"max":1000,"value":1,"step":1}},{"id":49,"type":"widget/number","pos":[383,768],"size":[80,60],"flags":{},"order":14,"mode":0,"outputs":[{"name":"","type":"number","links":[46,66,86,108,129,152]}],"properties":{"min":-1000,"max":1000,"value":1,"step":1}},{"id":50,"type":"widget/number","pos":[386,871],"size":[80,60],"flags":{},"order":15,"mode":0,"outputs":[{"name":"","type":"number","links":[47,67,87,109,130,153]}],"properties":{"min":-1000,"max":1000,"value":1,"step":1}},{"id":51,"type":"widget/number","pos":[390,976],"size":[80,60],"flags":{},"order":16,"mode":0,"outputs":[{"name":"","type":"number","links":[48,68,88,110,131,154]}],"properties":{"min":-1000,"max":1000,"value":1,"step":1}},{"id":52,"type":"widget/number","pos":[396,1079],"size":[80,60],"flags":{},"order":17,"mode":0,"outputs":[{"name":"","type":"number","links":[49,89,111]}],"properties":{"min":-1000,"max":1000,"value":1,"step":1}},{"id":55,"type":"features/largeinput_editor","pos":[814.2834913867189,1257.9045020637213],"size":[200,410],"flags":{},"order":28,"mode":0,"inputs":[{"name":"in 1","type":"number","link":71,"slot_index":0},{"name":"in 2","type":"number","link":72,"slot_index":1},{"name":"in 3","type":"number","link":73,"slot_index":2},{"name":"in 4","type":"number","link":74,"slot_index":3},{"name":"in 5","type":"number","link":75,"slot_index":4},{"name":"in 6","type":"number","link":76,"slot_index":5},{"name":"in 7","type":"number","link":77,"slot_index":6},{"name":"in 8","type":"number","link":78,"slot_index":7},{"name":"in 9","type":"number","link":79,"slot_index":8},{"name":"in 10","type":"number","link":80,"slot_index":9},{"name":"in 11","type":"number","link":81,"slot_index":10},{"name":"in 12","type":"number","link":82,"slot_index":11},{"name":"in 13","type":"number","link":83,"slot_index":12},{"name":"in 14","type":"number","link":84,"slot_index":13},{"name":"in 15","type":"number","link":85,"slot_index":14},{"name":"in 16","type":"number","link":86,"slot_index":15},{"name":"in 17","type":"number","link":87,"slot_index":16},{"name":"in 18","type":"number","link":88,"slot_index":17},{"name":"in 19","type":"number","link":89,"slot_index":18},{"name":"in 20","type":"number","link":91,"slot_index":19}],"properties":{}},{"id":54,"type":"features/largeinput_editor","pos":[816.2834913867189,800.9045020637204],"size":[200,410],"flags":{},"order":27,"mode":0,"inputs":[{"name":"in 1","type":"number","link":51,"slot_index":0},{"name":"in 2","type":"number","link":52,"slot_index":1},{"name":"in 3","type":"number","link":53,"slot_index":2},{"name":"in 4","type":"number","link":54,"slot_index":3},{"name":"in 5","type":"number","link":55,"slot_index":4},{"name":"in 6","type":"number","link":56,"slot_index":5},{"name":"in 7","type":"number","link":57,"slot_index":6},{"name":"in 8","type":"number","link":58,"slot_index":7},{"name":"in 9","type":"number","link":59,"slot_index":8},{"name":"in 10","type":"number","link":60,"slot_index":9},{"name":"in 11","type":"number","link":61,"slot_index":10},{"name":"in 12","type":"number","link":62,"slot_index":11},{"name":"in 13","type":"number","link":63,"slot_index":12},{"name":"in 14","type":"number","link":64,"slot_index":13},{"name":"in 15","type":"number","link":65,"slot_index":14},{"name":"in 16","type":"number","link":66,"slot_index":15},{"name":"in 17","type":"number","link":67,"slot_index":16},{"name":"in 18","type":"number","link":68,"slot_index":17},{"name":"in 19","type":"number","link":92,"slot_index":18},{"name":"in 20","type":"number","link":70,"slot_index":19}],"properties":{}},{"id":33,"type":"features/largeinput_editor","pos":[818.2834913867189,334.90450206372003],"size":[200,410],"flags":{},"order":26,"mode":0,"inputs":[{"name":"in 1","type":"number","link":31,"slot_index":0},{"name":"in 2","type":"number","link":32,"slot_index":1},{"name":"in 3","type":"number","link":33,"slot_index":2},{"name":"in 4","type":"number","link":34,"slot_index":3},{"name":"in 5","type":"number","link":35,"slot_index":4},{"name":"in 6","type":"number","link":36,"slot_index":5},{"name":"in 7","type":"number","link":37,"slot_index":6},{"name":"in 8","type":"number","link":38,"slot_index":7},{"name":"in 9","type":"number","link":39,"slot_index":8},{"name":"in 10","type":"number","link":40,"slot_index":9},{"name":"in 11","type":"number","link":41,"slot_index":10},{"name":"in 12","type":"number","link":42,"slot_index":11},{"name":"in 13","type":"number","link":43,"slot_index":12},{"name":"in 14","type":"number","link":44,"slot_index":13},{"name":"in 15","type":"number","link":45,"slot_index":14},{"name":"in 16","type":"number","link":46,"slot_index":15},{"name":"in 17","type":"number","link":47,"slot_index":16},{"name":"in 18","type":"number","link":48,"slot_index":17},{"name":"in 19","type":"number","link":49,"slot_index":18},{"name":"in 20","type":"number","link":50,"slot_index":19}],"properties":{}},{"id":53,"type":"widget/number","pos":[399,1182],"size":[80,60],"flags":{},"order":18,"mode":0,"outputs":[{"name":"","type":"number","links":[50,70,112,135],"slot_index":0}],"properties":{"min":-1000,"max":1000,"value":1,"step":1}},{"id":57,"type":"widget/number","pos":[405,1443],"size":[80,60],"flags":{},"order":19,"mode":0,"outputs":[{"name":"","type":"number","links":[92,136,155],"slot_index":0}],"properties":{"min":-1000,"max":1000,"value":1,"step":1}},{"id":61,"type":"features/largeinput_editor","pos":[1070.283491386718,1265.9045020637213],"size":[200,410],"flags":{},"order":25,"mode":0,"inputs":[{"name":"in 1","type":"number","link":137,"slot_index":0},{"name":"in 2","type":"number","link":138,"slot_index":1},{"name":"in 3","type":"number","link":139,"slot_index":2},{"name":"in 4","type":"number","link":140,"slot_index":3},{"name":"in 5","type":"number","link":141,"slot_index":4},{"name":"in 6","type":"number","link":142,"slot_index":5},{"name":"in 7","type":"number","link":143,"slot_index":6},{"name":"in 8","type":"number","link":144,"slot_index":7},{"name":"in 9","type":"number","link":145,"slot_index":8},{"name":"in 10","type":"number","link":146,"slot_index":9},{"name":"in 11","type":"number","link":147,"slot_index":10},{"name":"in 12","type":"number","link":148,"slot_index":11},{"name":"in 13","type":"number","link":149,"slot_index":12},{"name":"in 14","type":"number","link":150,"slot_index":13},{"name":"in 15","type":"number","link":151,"slot_index":14},{"name":"in 16","type":"number","link":152,"slot_index":15},{"name":"in 17","type":"number","link":153,"slot_index":16},{"name":"in 18","type":"number","link":154,"slot_index":17},{"name":"in 19","type":"number","link":155,"slot_index":18},{"name":"in 20","type":"number","link":157,"slot_index":19}],"properties":{}},{"id":56,"type":"widget/number","pos":[446,1703],"size":[80,60],"flags":{},"order":20,"mode":0,"outputs":[{"name":"","type":"number","links":[91,157],"slot_index":0}],"properties":{"min":-1000,"max":1000,"value":1,"step":1}},{"id":60,"type":"features/largeinput_editor","pos":[1069.283491386718,804.9045020637204],"size":[200,410],"flags":{},"order":24,"mode":0,"inputs":[{"name":"in 1","type":"number","link":114,"slot_index":0},{"name":"in 2","type":"number","link":115,"slot_index":1},{"name":"in 3","type":"number","link":116,"slot_index":2},{"name":"in 4","type":"number","link":117,"slot_index":3},{"name":"in 5","type":"number","link":118,"slot_index":4},{"name":"in 6","type":"number","link":119,"slot_index":5},{"name":"in 7","type":"number","link":120,"slot_index":6},{"name":"in 8","type":"number","link":121,"slot_index":7},{"name":"in 9","type":"number","link":122,"slot_index":8},{"name":"in 10","type":"number","link":123,"slot_index":9},{"name":"in 11","type":"number","link":124,"slot_index":10},{"name":"in 12","type":"number","link":125,"slot_index":11},{"name":"in 13","type":"number","link":126,"slot_index":12},{"name":"in 14","type":"number","link":127,"slot_index":13},{"name":"in 15","type":"number","link":128,"slot_index":14},{"name":"in 16","type":"number","link":129,"slot_index":15},{"name":"in 17","type":"number","link":130,"slot_index":16},{"name":"in 18","type":"number","link":131,"slot_index":17},{"name":"in 19","type":"number","link":136,"slot_index":18},{"name":"in 20","type":"number","link":135,"slot_index":19}],"properties":{}},{"id":59,"type":"widget/number","pos":[942.2834913867189,139.9045020637209],"size":[80,60],"flags":{},"order":21,"mode":0,"outputs":[{"name":"","type":"number","links":[113,114,137]}],"properties":{"min":-1000,"max":1000,"value":1,"step":1}},{"id":34,"type":"widget/number","pos":[354,-815],"size":[80,60],"flags":{},"order":22,"mode":0,"outputs":[{"name":"","type":"number","links":[31,51,71]}],"properties":{"min":-1000,"max":1000,"value":1,"step":1}},{"id":58,"type":"features/largeinput_editor","pos":[1066,342],"size":[200,410],"flags":{},"order":23,"mode":0,"inputs":[{"name":"in 1","type":"number","link":113,"slot_index":0},{"name":"in 2","type":"number","link":94,"slot_index":1},{"name":"in 3","type":"number","link":95,"slot_index":2},{"name":"in 4","type":"number","link":96,"slot_index":3},{"name":"in 5","type":"number","link":97,"slot_index":4},{"name":"in 6","type":"number","link":98,"slot_index":5},{"name":"in 7","type":"number","link":99,"slot_index":6},{"name":"in 8","type":"number","link":100,"slot_index":7},{"name":"in 9","type":"number","link":101,"slot_index":8},{"name":"in 10","type":"number","link":102,"slot_index":9},{"name":"in 11","type":"number","link":103,"slot_index":10},{"name":"in 12","type":"number","link":104,"slot_index":11},{"name":"in 13","type":"number","link":105,"slot_index":12},{"name":"in 14","type":"number","link":106,"slot_index":13},{"name":"in 15","type":"number","link":107,"slot_index":14},{"name":"in 16","type":"number","link":108,"slot_index":15},{"name":"in 17","type":"number","link":109,"slot_index":16},{"name":"in 18","type":"number","link":110,"slot_index":17},{"name":"in 19","type":"number","link":111,"slot_index":18},{"name":"in 20","type":"number","link":112,"slot_index":19}],"properties":{}}],"links":[[31,34,0,33,0,"number"],[32,35,0,33,1,"number"],[33,36,0,33,2,"number"],[34,37,0,33,3,"number"],[35,38,0,33,4,"number"],[36,39,0,33,5,"number"],[37,40,0,33,6,"number"],[38,41,0,33,7,"number"],[39,42,0,33,8,"number"],[40,43,0,33,9,"number"],[41,44,0,33,10,"number"],[42,45,0,33,11,"number"],[43,46,0,33,12,"number"],[44,47,0,33,13,"number"],[45,48,0,33,14,"number"],[46,49,0,33,15,"number"],[47,50,0,33,16,"number"],[48,51,0,33,17,"number"],[49,52,0,33,18,"number"],[50,53,0,33,19,"number"],[51,34,0,54,0,"number"],[52,35,0,54,1,"number"],[53,36,0,54,2,"number"],[54,37,0,54,3,"number"],[55,38,0,54,4,"number"],[56,39,0,54,5,"number"],[57,40,0,54,6,"number"],[58,41,0,54,7,"number"],[59,42,0,54,8,"number"],[60,43,0,54,9,"number"],[61,44,0,54,10,"number"],[62,45,0,54,11,"number"],[63,46,0,54,12,"number"],[64,47,0,54,13,"number"],[65,48,0,54,14,"number"],[66,49,0,54,15,"number"],[67,50,0,54,16,"number"],[68,51,0,54,17,"number"],[70,53,0,54,19,"number"],[71,34,0,55,0,"number"],[72,35,0,55,1,"number"],[73,36,0,55,2,"number"],[74,37,0,55,3,"number"],[75,38,0,55,4,"number"],[76,39,0,55,5,"number"],[77,40,0,55,6,"number"],[78,41,0,55,7,"number"],[79,42,0,55,8,"number"],[80,43,0,55,9,"number"],[81,44,0,55,10,"number"],[82,45,0,55,11,"number"],[83,46,0,55,12,"number"],[84,47,0,55,13,"number"],[85,48,0,55,14,"number"],[86,49,0,55,15,"number"],[87,50,0,55,16,"number"],[88,51,0,55,17,"number"],[89,52,0,55,18,"number"],[91,56,0,55,19,"number"],[92,57,0,54,18,"number"],[94,35,0,58,1,"number"],[95,36,0,58,2,"number"],[96,37,0,58,3,"number"],[97,38,0,58,4,"number"],[98,39,0,58,5,"number"],[99,40,0,58,6,"number"],[100,41,0,58,7,"number"],[101,42,0,58,8,"number"],[102,43,0,58,9,"number"],[103,44,0,58,10,"number"],[104,45,0,58,11,"number"],[105,46,0,58,12,"number"],[106,47,0,58,13,"number"],[107,48,0,58,14,"number"],[108,49,0,58,15,"number"],[109,50,0,58,16,"number"],[110,51,0,58,17,"number"],[111,52,0,58,18,"number"],[112,53,0,58,19,"number"],[113,59,0,58,0,"number"],[114,59,0,60,0,"number"],[115,35,0,60,1,"number"],[116,36,0,60,2,"number"],[117,37,0,60,3,"number"],[118,38,0,60,4,"number"],[119,39,0,60,5,"number"],[120,40,0,60,6,"number"],[121,41,0,60,7,"number"],[122,42,0,60,8,"number"],[123,43,0,60,9,"number"],[124,44,0,60,10,"number"],[125,45,0,60,11,"number"],[126,46,0,60,12,"number"],[127,47,0,60,13,"number"],[128,48,0,60,14,"number"],[129,49,0,60,15,"number"],[130,50,0,60,16,"number"],[131,51,0,60,17,"number"],[135,53,0,60,19,"number"],[136,57,0,60,18,"number"],[137,59,0,61,0,"number"],[138,35,0,61,1,"number"],[139,36,0,61,2,"number"],[140,37,0,61,3,"number"],[141,38,0,61,4,"number"],[142,39,0,61,5,"number"],[143,40,0,61,6,"number"],[144,41,0,61,7,"number"],[145,42,0,61,8,"number"],[146,43,0,61,9,"number"],[147,44,0,61,10,"number"],[148,45,0,61,11,"number"],[149,46,0,61,12,"number"],[150,47,0,61,13,"number"],[151,48,0,61,14,"number"],[152,49,0,61,15,"number"],[153,50,0,61,16,"number"],[154,51,0,61,17,"number"],[155,57,0,61,18,"number"],[157,56,0,61,19,"number"]],"groups":[{"title":"Use Ctrl+C/Ctrl+Shift+V to easily copy and paste new nodes maintaining connection to the outputs of unselected nodes","bounding":[1137,140,263,82],"color":"#3f789e"}],"config":{},"extra":{},"version":0.4} \ No newline at end of file diff --git a/editor/examples/features.json b/editor/examples/features.json deleted file mode 100755 index ead5a6d9c..000000000 --- a/editor/examples/features.json +++ /dev/null @@ -1 +0,0 @@ -{"last_node_id":14,"last_link_id":14,"nodes":[{"id":9,"type":"features/slots","pos":[847,479],"size":[100,40],"flags":{"horizontal":true},"mode":0,"inputs":[{"name":"C","type":"number","link":6}],"outputs":[{"name":"A","type":"number","links":null},{"name":"B","type":"number","links":null}],"properties":{}},{"id":7,"type":"features/slots","pos":[757,380],"size":[100,40],"flags":{"horizontal":true},"mode":0,"inputs":[{"name":"C","type":"number","link":13}],"outputs":[{"name":"A","type":"number","links":[10]},{"name":"B","type":"number","links":[6]}],"properties":{}},{"id":8,"type":"features/slots","pos":[672,481],"size":[100,40],"flags":{"horizontal":true},"mode":0,"inputs":[{"name":"C","type":"number","link":10}],"outputs":[{"name":"A","type":"number","links":null},{"name":"B","type":"number","links":null}],"properties":{}},{"id":5,"type":"math/operation","pos":[413,101],"size":[140,34],"flags":{"collapsed":true},"mode":0,"inputs":[{"name":"A","type":"number","link":null},{"name":"B","type":"number","link":null}],"outputs":[{"name":"=","type":"number","links":[2]}],"properties":{"A":1,"B":1,"OP":"+"},"shape":2},{"id":12,"type":"input/gamepad","pos":[593,208],"size":{"0":175,"1":74},"flags":{},"mode":0,"outputs":[{"name":"left_x_axis","type":"number","links":null},{"name":"left_y_axis","type":"number","links":null},{"name":"button_pressed","type":-1,"links":[12]}],"properties":{"gamepad_index":0,"threshold":0.1}},{"id":13,"type":"events/log","pos":[862,246],"size":{"0":144,"1":32},"flags":{},"mode":0,"inputs":[{"name":"event","type":-1,"link":12}],"properties":{}},{"id":14,"type":"features/widgets","pos":[441,365],"size":{"0":180,"1":170},"flags":{},"mode":0,"outputs":[{"name":"","type":"number","links":[13]}],"properties":{}},{"id":10,"type":"widget/knob","pos":[421,197],"size":[74,92],"flags":{},"mode":0,"outputs":[{"name":"","type":"number","links":[9]}],"properties":{"min":0,"max":1,"value":0.5,"wcolor":"#7AF","size":50}},{"id":4,"type":"math/operation","pos":[596,116],"size":[148,48],"flags":{},"mode":0,"inputs":[{"name":"A","type":"number","link":2},{"name":"B","type":"number","link":9}],"outputs":[{"name":"=","type":"number","links":[1]}],"properties":{"A":1,"B":1,"OP":"+"},"shape":2},{"id":2,"type":"features/shape","pos":[850,97],"size":{"0":140,"1":39},"flags":{},"mode":0,"inputs":[{"name":"","type":"number","link":1}],"outputs":[{"name":"","type":"number","links":null}],"properties":{}}],"links":[[1,4,0,2,0,"number"],[2,5,0,4,0,"number"],[6,7,1,9,0,"number"],[9,10,0,4,1,"number"],[10,7,0,8,0,"number"],[12,12,2,13,0,-1],[13,14,0,7,0,"number"]],"groups":[{"title":"Group","bounding":[418,298,609,255],"color":"#3f789e"}],"config":{}} \ No newline at end of file diff --git a/editor/examples/midi_generation.json b/editor/examples/midi_generation.json deleted file mode 100755 index 960c59f77..000000000 --- a/editor/examples/midi_generation.json +++ /dev/null @@ -1 +0,0 @@ -{"last_node_id":47,"last_link_id":64,"nodes":[{"id":8,"type":"midi/generator","pos":[548,390],"size":{"0":140,"1":66},"flags":{},"mode":0,"inputs":[{"name":"generate","type":-1,"link":11},{"name":"scale","type":"string","link":47},{"name":"octave","type":"number","link":null}],"outputs":[{"name":"note","type":-1,"links":[10,19]}],"properties":{"notes":"A,B,C","octave":2,"duration":0.5,"mode":"sequence"}},{"id":20,"type":"midi/transpose","pos":[726,489],"size":{"0":140,"1":46},"flags":{},"mode":0,"inputs":[{"name":"in","type":-1,"link":19},{"name":"amount","type":"number","link":null}],"outputs":[{"name":"out","type":-1,"links":[21]}],"properties":{"amount":5}},{"id":32,"type":"midi/event","pos":[1465,656],"size":{"0":140,"1":46},"flags":{},"mode":0,"inputs":[{"name":"send","type":-1,"link":null},{"name":"assign","type":-1,"link":44}],"outputs":[{"name":"on_midi","type":-1,"links":null},{"name":"note","type":"number","links":[45]}],"properties":{"channel":0,"cmd":128,"value1":0,"value2":0}},{"id":19,"type":"midi/play","pos":[1132,611],"size":{"0":140,"1":66},"flags":{},"mode":0,"inputs":[{"name":"note","type":-1,"link":53},{"name":"volume","type":"number","link":36},{"name":"duration","type":"number","link":null}],"outputs":[{"name":"note","type":-1,"links":[35,44]}],"properties":{"volume":0.3599999999999999,"duration":4,"value":0}},{"id":21,"type":"midi/quantize","pos":[903,589],"size":{"0":159.60000610351562,"1":46},"flags":{},"mode":0,"inputs":[{"name":"note","type":-1,"link":21},{"name":"scale","type":"string","link":49}],"outputs":[{"name":"out","type":-1,"links":[53]}],"properties":{"scale":"A,A#,B,C,C#,D,D#,E,F,F#,G,G#","amount":"A,B,C,D,E,F,G"}},{"id":37,"type":"basic/watch","pos":[547,615],"size":{"0":140,"1":26},"flags":{},"mode":0,"inputs":[{"name":"value","type":0,"link":52,"label":"A,B,C,B"}],"properties":{}},{"id":35,"type":"basic/string","pos":[79,456],"size":[210,58],"flags":{},"mode":0,"outputs":[{"name":"value","type":"string","links":[50],"label":"A,B,C"}],"title":"NOTE SCALE","properties":{"value":"A,B,C,B"}},{"id":7,"type":"midi/generator","pos":[549,289],"size":{"0":140,"1":66},"flags":{},"mode":0,"inputs":[{"name":"generate","type":-1,"link":5},{"name":"scale","type":"string","link":48},{"name":"octave","type":"number","link":null}],"outputs":[{"name":"note","type":-1,"links":[7,12]}],"properties":{"notes":"A,B,C","octave":2,"duration":0.5,"mode":"random"}},{"id":41,"type":"midi/generator","pos":[552,189],"size":{"0":140,"1":66},"flags":{},"mode":0,"inputs":[{"name":"generate","type":-1,"link":57},{"name":"scale","type":"string","link":55},{"name":"octave","type":"number","link":null}],"outputs":[{"name":"note","type":-1,"links":[62]}],"properties":{"notes":"A,B,C","octave":3,"duration":0.5,"mode":"sequence"}},{"id":12,"type":"events/timer","pos":[180,284],"size":{"0":140,"1":26},"flags":{},"mode":0,"inputs":[{"name":"interval","type":"number","link":null}],"outputs":[{"name":"on_tick","type":-1,"links":[11]}],"properties":{"interval":1200,"event":"tick"},"boxcolor":"#222"},{"id":34,"type":"logic/selector","pos":[351,468],"size":{"0":140,"1":106},"flags":{},"mode":0,"inputs":[{"name":"sel","type":"number","link":58},{"name":"A","type":0,"link":46},{"name":"B","type":0,"link":50},{"name":"C","type":0,"link":59},{"name":"D","type":0,"link":null}],"outputs":[{"name":"out","links":[47,48,49,52,55]}],"properties":{}},{"id":47,"type":"midi/keys","pos":[1153,88],"size":[423,104],"flags":{},"mode":0,"inputs":[{"name":"note","type":-1,"link":62},{"name":"reset","type":-1,"link":null}],"outputs":[{"name":"note","type":-1,"links":[63]}],"properties":{"num_octaves":2,"start_octave":3}},{"id":15,"type":"math/floor","pos":[505,85],"size":[140,26],"flags":{"collapsed":true},"mode":0,"inputs":[{"name":"in","type":"number","link":14}],"outputs":[{"name":"out","type":"number","links":[15]}],"properties":{}},{"id":14,"type":"math/rand","pos":[344,83],"size":[140,26],"flags":{"collapsed":true},"mode":0,"outputs":[{"name":"value","type":"number","links":[14],"label":"1.191"}],"properties":{"min":-1,"max":2}},{"id":16,"type":"math/operation","pos":[645,85],"size":[100,50],"flags":{"collapsed":true},"mode":0,"inputs":[{"name":"A","type":"number","link":15},{"name":"B","type":"number","link":null}],"outputs":[{"name":"=","type":"number","links":[16]}],"properties":{"A":1,"B":12,"OP":"*"}},{"id":10,"type":"basic/string","pos":[77,360],"size":[208,48],"flags":{},"mode":0,"outputs":[{"name":"value","type":"string","links":[46],"label":"A,B,C"}],"title":"NOTE SCALE","properties":{"value":"A,B,C,D,E,F,G"}},{"id":43,"type":"basic/string","pos":[79,556],"size":[210,58],"flags":{},"mode":0,"outputs":[{"name":"value","type":"string","links":[59],"label":"A,B,C"}],"title":"NOTE SCALE","properties":{"value":"D,E,F,G,F,E"}},{"id":44,"type":"math/rand","pos":[143,664],"size":[140,26],"flags":{},"mode":0,"outputs":[{"name":"value","type":"number","links":[58],"label":"0.750"}],"properties":{"min":0,"max":1}},{"id":11,"type":"midi/play","pos":[1135,496],"size":{"0":140,"1":66},"flags":{},"mode":0,"inputs":[{"name":"note","type":-1,"link":10},{"name":"volume","type":"number","link":18},{"name":"duration","type":"number","link":null}],"outputs":[{"name":"note","type":-1,"links":[34,43]}],"properties":{"volume":0.3599999999999999,"duration":4,"value":0}},{"id":13,"type":"midi/transpose","pos":[893,258],"size":{"0":140,"1":46},"flags":{},"mode":0,"inputs":[{"name":"in","type":-1,"link":12},{"name":"amount","type":"number","link":16}],"outputs":[{"name":"out","type":-1,"links":[54]}],"properties":{"amount":12}},{"id":4,"type":"midi/play","pos":[1155,249],"size":{"0":140,"1":66},"flags":{},"mode":0,"inputs":[{"name":"note","type":-1,"link":54},{"name":"volume","type":"number","link":17},{"name":"duration","type":"number","link":null}],"outputs":[{"name":"note","type":-1,"links":[33,39]}],"properties":{"volume":0.21000000000000005,"duration":1,"value":0}},{"id":30,"type":"midi/event","pos":[1433,260],"size":{"0":140,"1":46},"flags":{},"mode":0,"inputs":[{"name":"send","type":-1,"link":null},{"name":"assign","type":-1,"link":39}],"outputs":[{"name":"on_midi","type":-1,"links":null},{"name":"note","type":"number","links":[38]}],"properties":{"channel":0,"cmd":128,"value1":57,"value2":0}},{"id":28,"type":"midi/output","pos":[1428,414],"size":{"0":140,"1":66},"flags":{},"mode":0,"inputs":[{"name":"send","type":-1,"link":33},{"name":"send","type":-1,"link":34},{"name":"send","type":-1,"link":35}],"properties":{"port":0}},{"id":31,"type":"midi/event","pos":[1469,563],"size":{"0":140,"1":46},"flags":{},"mode":0,"inputs":[{"name":"send","type":-1,"link":null},{"name":"assign","type":-1,"link":43}],"outputs":[{"name":"on_midi","type":-1,"links":null},{"name":"note","type":"number","links":[42]}],"properties":{"channel":0,"cmd":128,"value1":50,"value2":0}},{"id":29,"type":"graphics/plot","pos":[1675,328],"size":{"0":348,"1":139},"flags":{},"mode":0,"inputs":[{"name":"A","type":"Number","link":38},{"name":"B","type":"Number","link":42},{"name":"C","type":"Number","link":45},{"name":"D","type":"Number","link":null}],"properties":{"scale":100}},{"id":46,"type":"math/rand","pos":[1455,42],"size":[140,26],"flags":{"collapsed":true},"mode":0,"outputs":[{"name":"value","type":"number","links":[60],"label":"0.007"}],"properties":{"min":0,"max":0.2}},{"id":39,"type":"midi/play","pos":[1656,116],"size":{"0":140,"1":66},"flags":{},"mode":0,"inputs":[{"name":"note","type":-1,"link":63},{"name":"volume","type":"number","link":60},{"name":"duration","type":"number","link":null}],"outputs":[{"name":"note","type":-1,"links":[]}],"properties":{"volume":0.006812153971126511,"duration":1,"value":0}},{"id":3,"type":"events/timer","pos":[178,212],"size":{"0":140,"1":26},"flags":{},"mode":0,"inputs":[{"name":"interval","type":"number","link":null}],"outputs":[{"name":"on_tick","type":-1,"links":[5,57]}],"properties":{"interval":300,"event":"tick"},"boxcolor":"#222"},{"id":18,"type":"widget/knob","pos":[819,62],"size":[82.78512396694214,93.87603305785123],"flags":{},"mode":0,"outputs":[{"name":"","type":"number","links":[18,36]}],"properties":{"min":0,"max":1,"value":0.4504132231404958,"wcolor":"#7AF","size":50},"boxcolor":"rgba(128,128,128,1.0)"},{"id":17,"type":"widget/knob","pos":[916,62],"size":[78.34710743801656,94.70247933884298],"flags":{"collapsed":false},"mode":0,"outputs":[{"name":"","type":"number","links":[17]}],"properties":{"min":0,"max":1,"value":0.21000000000000005,"wcolor":"#7AF","size":50},"boxcolor":"rgba(128,128,128,1.0)"},{"id":6,"type":"midi/show","pos":[898,357],"size":[266.5950413223138,61.685950413223],"flags":{},"mode":0,"inputs":[{"name":"on_midi","type":-1,"link":7}],"properties":{}}],"links":[[5,3,0,7,0,-1],[7,7,0,6,0,-1],[10,8,0,11,0,-1],[11,12,0,8,0,-1],[12,7,0,13,0,-1],[14,14,0,15,0,"number"],[15,15,0,16,0,"number"],[16,16,0,13,1,"number"],[17,17,0,4,1,"number"],[18,18,0,11,1,"number"],[19,8,0,20,0,-1],[21,20,0,21,0,-1],[33,4,0,28,0,-1],[34,11,0,28,1,-1],[35,19,0,28,2,-1],[36,18,0,19,1,"number"],[38,30,1,29,0,"Number"],[39,4,0,30,1,-1],[42,31,1,29,1,"Number"],[43,11,0,31,1,-1],[44,19,0,32,1,-1],[45,32,1,29,2,"Number"],[46,10,0,34,1,0],[47,34,0,8,1,"string"],[48,34,0,7,1,"string"],[49,34,0,21,1,"string"],[50,35,0,34,2,0],[52,34,0,37,0,0],[53,21,0,19,0,-1],[54,13,0,4,0,-1],[55,34,0,41,1,"string"],[57,3,0,41,0,-1],[58,44,0,34,0,"number"],[59,43,0,34,3,0],[60,46,0,39,1,"number"],[62,41,0,47,0,-1],[63,47,0,39,0,-1]],"groups":[],"config":{}} \ No newline at end of file diff --git a/editor/examples/subgraph.json b/editor/examples/subgraph.json deleted file mode 100755 index 27c134765..000000000 --- a/editor/examples/subgraph.json +++ /dev/null @@ -1 +0,0 @@ -{"last_node_id":6,"last_link_id":5,"nodes":[{"id":3,"type":"basic/time","pos":[312,145],"size":{"0":140,"1":46},"flags":{},"mode":0,"outputs":[{"name":"in ms","type":"number","links":null},{"name":"in sec","type":"number","links":[1]}],"properties":{}},{"id":4,"type":"basic/watch","pos":[864,156],"size":{"0":140,"1":26},"flags":{},"mode":0,"inputs":[{"name":"value","type":0,"link":2,"label":"5.000"}],"properties":{}},{"id":6,"type":"events/counter","pos":[864,229],"size":{"0":140,"1":66},"flags":{},"mode":0,"inputs":[{"name":"inc","type":-1,"link":4},{"name":"dec","type":-1,"link":null},{"name":"reset","type":-1,"link":null}],"outputs":[{"name":"change","type":-1,"links":null},{"name":"num","type":"number","links":null}],"properties":{}},{"id":2,"type":"graph/subgraph","pos":[573,168],"size":{"0":140,"1":86},"flags":{},"mode":0,"inputs":[{"name":"enabled","type":"boolean","link":null},{"name":"foo","type":"","link":1},{"name":"EV","type":-1,"link":3}],"outputs":[{"name":"faa","type":0,"links":[2]},{"name":"EV","type":-1,"links":[4]}],"properties":{"enabled":true},"subgraph":{"last_node_id":7,"last_link_id":6,"nodes":[{"id":3,"type":"graph/output","pos":[1119,139],"size":[180,60],"flags":{},"mode":0,"inputs":[{"name":"","type":0,"link":2}],"properties":{"name":"faa","type":0}},{"id":4,"type":"math/floor","pos":[872,194],"size":[112,28],"flags":{},"mode":0,"inputs":[{"name":"in","type":"number","link":1}],"outputs":[{"name":"out","type":"number","links":[2]}],"properties":{}},{"id":2,"type":"graph/input","pos":[440,149],"size":[180,60],"flags":{},"mode":0,"outputs":[{"name":"","type":"","links":[1]}],"properties":{"name":"foo","type":""}},{"id":5,"type":"graph/input","pos":[460,282],"size":[180,60],"flags":{},"mode":0,"outputs":[{"name":"","type":-1,"links":[4]}],"properties":{"name":"EV","type":-1}},{"id":6,"type":"graph/output","pos":[1054,293],"size":[180,60],"flags":{},"mode":0,"inputs":[{"name":"","type":-1,"link":5}],"properties":{"name":"EV","type":-1}},{"id":7,"type":"events/delay","pos":[742,300],"size":{"0":140,"1":26},"flags":{},"mode":0,"inputs":[{"name":"event","type":-1,"link":4}],"outputs":[{"name":"on_time","type":-1,"links":[5]}],"properties":{"time_in_ms":1000}}],"links":[[1,2,0,4,0,"number"],[2,4,0,3,0,0],[4,5,0,7,0,-1],[5,7,0,6,0,-1]],"groups":[],"config":{},"version":0.4}},{"id":5,"type":"events/timer","pos":[311,240],"size":{"0":140,"1":26},"flags":{},"mode":0,"outputs":[{"name":"on_tick","type":-1,"links":[3]}],"properties":{"interval":2000,"event":"tick"},"boxcolor":"#222"}],"links":[[1,3,1,2,1,0],[2,2,0,4,0,0],[3,5,0,2,2,-1],[4,2,1,6,0,-1]],"groups":[],"config":{},"version":0.4} \ No newline at end of file diff --git a/editor/imgs/grid.png b/editor/imgs/grid.png deleted file mode 100755 index 2feb956e6..000000000 Binary files a/editor/imgs/grid.png and /dev/null differ diff --git a/editor/imgs/icon-edit.png b/editor/imgs/icon-edit.png deleted file mode 100755 index 17f31e5c1..000000000 Binary files a/editor/imgs/icon-edit.png and /dev/null differ diff --git a/editor/imgs/icon-gear.png b/editor/imgs/icon-gear.png deleted file mode 100755 index 34e192d77..000000000 Binary files a/editor/imgs/icon-gear.png and /dev/null differ diff --git a/editor/imgs/icon-load.png b/editor/imgs/icon-load.png deleted file mode 100755 index 4ab6e4e0e..000000000 Binary files a/editor/imgs/icon-load.png and /dev/null differ diff --git a/editor/imgs/icon-maximize.png b/editor/imgs/icon-maximize.png deleted file mode 100755 index c4d98455b..000000000 Binary files a/editor/imgs/icon-maximize.png and /dev/null differ diff --git a/editor/imgs/icon-play.png b/editor/imgs/icon-play.png deleted file mode 100755 index 5f9a57b8f..000000000 Binary files a/editor/imgs/icon-play.png and /dev/null differ diff --git a/editor/imgs/icon-playstep.png b/editor/imgs/icon-playstep.png deleted file mode 100755 index fac10905d..000000000 Binary files a/editor/imgs/icon-playstep.png and /dev/null differ diff --git a/editor/imgs/icon-record.png b/editor/imgs/icon-record.png deleted file mode 100755 index 7cd35ada0..000000000 Binary files a/editor/imgs/icon-record.png and /dev/null differ diff --git a/editor/imgs/icon-save.png b/editor/imgs/icon-save.png deleted file mode 100755 index 209afe833..000000000 Binary files a/editor/imgs/icon-save.png and /dev/null differ diff --git a/editor/imgs/icon-stop.png b/editor/imgs/icon-stop.png deleted file mode 100755 index a22993f48..000000000 Binary files a/editor/imgs/icon-stop.png and /dev/null differ diff --git a/editor/imgs/load-progress-empty.png b/editor/imgs/load-progress-empty.png deleted file mode 100755 index eb781a7bc..000000000 Binary files a/editor/imgs/load-progress-empty.png and /dev/null differ diff --git a/editor/imgs/load-progress-full.png b/editor/imgs/load-progress-full.png deleted file mode 100755 index e58e06f70..000000000 Binary files a/editor/imgs/load-progress-full.png and /dev/null differ diff --git a/editor/imgs/load-progress-grey.png b/editor/imgs/load-progress-grey.png deleted file mode 100755 index 7a8e68cb9..000000000 Binary files a/editor/imgs/load-progress-grey.png and /dev/null differ diff --git a/editor/imgs/play-icons-light-alpha.png b/editor/imgs/play-icons-light-alpha.png deleted file mode 100755 index 0c9ac5662..000000000 Binary files a/editor/imgs/play-icons-light-alpha.png and /dev/null differ diff --git a/editor/imgs/play-icons-light.png b/editor/imgs/play-icons-light.png deleted file mode 100755 index 74a9168e5..000000000 Binary files a/editor/imgs/play-icons-light.png and /dev/null differ diff --git a/editor/imgs/play-icons.png b/editor/imgs/play-icons.png deleted file mode 100755 index 4c6fadd36..000000000 Binary files a/editor/imgs/play-icons.png and /dev/null differ diff --git a/editor/index.html b/editor/index.html deleted file mode 100755 index c43922b45..000000000 --- a/editor/index.html +++ /dev/null @@ -1,47 +0,0 @@ - - - - LiteGraph - - - - - - - - -
    -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/editor/js/code.js b/editor/js/code.js deleted file mode 100755 index 3140e7dc7..000000000 --- a/editor/js/code.js +++ /dev/null @@ -1,195 +0,0 @@ -var webgl_canvas = null; - -LiteGraph.node_images_path = "../nodes_data/"; - -var editor = new LiteGraph.Editor("main",{miniwindow:false}); -window.graphcanvas = editor.graphcanvas; -window.graph = editor.graph; -updateEditorHiPPICanvas(); -window.addEventListener("resize", function() { - editor.graphcanvas.resize(); - updateEditorHiPPICanvas(); -} ); -//window.addEventListener("keydown", editor.graphcanvas.processKey.bind(editor.graphcanvas) ); -window.onbeforeunload = function(){ - var data = JSON.stringify( graph.serialize() ); - localStorage.setItem("litegraphg demo backup", data ); -} - -function updateEditorHiPPICanvas() { - const ratio = window.devicePixelRatio; - if(ratio == 1) { return } - const rect = editor.canvas.parentNode.getBoundingClientRect(); - const { width, height } = rect; - editor.canvas.width = width * ratio; - editor.canvas.height = height * ratio; - editor.canvas.style.width = width + "px"; - editor.canvas.style.height = height + "px"; - editor.canvas.getContext("2d").scale(ratio, ratio); - return editor.canvas; -} - -//enable scripting -LiteGraph.allow_scripts = true; - -//test -//editor.graphcanvas.viewport = [200,200,400,400]; - -//create scene selector -var elem = document.createElement("span"); -elem.id = "LGEditorTopBarSelector"; -elem.className = "selector"; -elem.innerHTML = ""; -elem.innerHTML += "Demo | "; -editor.tools.appendChild(elem); -var select = elem.querySelector("select"); -select.addEventListener("change", function(e){ - var option = this.options[this.selectedIndex]; - var url = option.dataset["url"]; - - if(url) - graph.load( url ); - else if(option.callback) - option.callback(); - else - graph.clear(); -}); - -elem.querySelector("#save").addEventListener("click",function(){ - console.log("saved"); - localStorage.setItem( "graphdemo_save", JSON.stringify( graph.serialize() ) ); -}); - -elem.querySelector("#load").addEventListener("click",function(){ - var data = localStorage.getItem( "graphdemo_save" ); - if(data) - graph.configure( JSON.parse( data ) ); - console.log("loaded"); -}); - -elem.querySelector("#download").addEventListener("click",function(){ - var data = JSON.stringify( graph.serialize() ); - var file = new Blob( [ data ] ); - var url = URL.createObjectURL( file ); - var element = document.createElement("a"); - element.setAttribute('href', url); - element.setAttribute('download', "graph.JSON" ); - element.style.display = 'none'; - document.body.appendChild(element); - element.click(); - document.body.removeChild(element); - setTimeout( function(){ URL.revokeObjectURL( url ); }, 1000*60 ); //wait one minute to revoke url -}); - -elem.querySelector("#webgl").addEventListener("click", enableWebGL ); -elem.querySelector("#multiview").addEventListener("click", function(){ editor.addMultiview() } ); - - -function addDemo( name, url ) -{ - var option = document.createElement("option"); - if(url.constructor === String) - option.dataset["url"] = url; - else - option.callback = url; - option.innerHTML = name; - select.appendChild( option ); -} - -//some examples -addDemo("Features", "examples/features.json"); -addDemo("Benchmark", "examples/benchmark.json"); -addDemo("Subgraph", "examples/subgraph.json"); -addDemo("Audio", "examples/audio.json"); -addDemo("Audio Delay", "examples/audio_delay.json"); -addDemo("Audio Reverb", "examples/audio_reverb.json"); -addDemo("MIDI Generation", "examples/midi_generation.json"); -addDemo("Copy Paste", "examples/copypaste.json"); -addDemo("autobackup", function(){ - var data = localStorage.getItem("litegraphg demo backup"); - if(!data) - return; - var graph_data = JSON.parse(data); - 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 ); - } - } -} - -// Tests -// CopyPasteWithConnectionToUnselectedOutputTest(); -// demo(); \ No newline at end of file diff --git a/editor/js/defaults.js b/editor/js/defaults.js deleted file mode 100755 index f51256d83..000000000 --- a/editor/js/defaults.js +++ /dev/null @@ -1,32 +0,0 @@ - -LiteGraph.debug = false; -LiteGraph.catch_exceptions = true; -LiteGraph.throw_errors = true; -LiteGraph.allow_scripts = false; //if set to true some nodes like Formula would be allowed to evaluate code that comes from unsafe sources (like node configuration); which could lead to exploits - -LiteGraph.searchbox_extras = {}; //used to add extra features to the search box -LiteGraph.auto_sort_node_types = true; // [true!] If set to true; will automatically sort node types / categories in the context menus -LiteGraph.node_box_coloured_when_on = true; // [true!] this make the nodes box (top left circle) coloured when triggered (execute/action); visual feedback -LiteGraph.node_box_coloured_by_mode = true; // [true!] nodebox based on node mode; visual feedback -LiteGraph.dialog_close_on_mouse_leave = true; // [false on mobile] better true if not touch device; -LiteGraph.dialog_close_on_mouse_leave_delay = 500; -LiteGraph.shift_click_do_break_link_from = false; // [false!] prefer false if results too easy to break links -LiteGraph.click_do_break_link_to = false; // [false!]prefer false; way too easy to break links -LiteGraph.search_hide_on_mouse_leave = true; // [false on mobile] better true if not touch device; -LiteGraph.search_filter_enabled = true; // [true!] enable filtering slots type in the search widget; !requires auto_load_slot_types or manual set registered_slot_[in/out]_types and slot_types_[in/out] -LiteGraph.search_show_all_on_open = true; // [true!] opens the results list when opening the search widget - -LiteGraph.auto_load_slot_types = true; // [if want false; use true; run; get vars values to be statically set; than disable] nodes types and nodeclass association with node types need to be calculated; if dont want this; calculate once and set registered_slot_[in/out]_types and slot_types_[in/out] -/*// set these values if not using auto_load_slot_types -LiteGraph.registered_slot_in_types = {}; // slot types for nodeclass -LiteGraph.registered_slot_out_types = {}; // slot types for nodeclass -LiteGraph.slot_types_in = []; // slot types IN -LiteGraph.slot_types_out = []; // slot types OUT*/ - -LiteGraph.alt_drag_do_clone_nodes = true; // [true!] very handy; ALT click to clone and drag the new node -LiteGraph.do_add_triggers_slots = true; // [true!] will create and connect event slots when using action/events connections; !WILL CHANGE node mode when using onTrigger (enable mode colors); onExecuted does not need this -LiteGraph.allow_multi_output_for_events = false; // [false!] being events; it is strongly reccomended to use them sequentially; one by one -LiteGraph.middle_click_slot_add_default_node = true; //[true!] allows to create and connect a ndoe clicking with the third button (wheel) -LiteGraph.release_link_on_empty_shows_menu = true; //[true!] dragging a link to empty space will open a menu, add from list, search or defaults -LiteGraph.pointerevents_method = "mouse"; // "mouse"|"pointer" use mouse for retrocompatibility issues? (none found @ now) -LiteGraph.ctrl_shift_v_paste_connect_unselected_outputs = true; //[true!] allows ctrl + shift + v to paste nodes with the outputs of the unselected nodes connected with the inputs of the newly pasted nodes \ No newline at end of file diff --git a/editor/js/defaults_mobile.js b/editor/js/defaults_mobile.js deleted file mode 100755 index 04383b1c5..000000000 --- a/editor/js/defaults_mobile.js +++ /dev/null @@ -1,32 +0,0 @@ - -LiteGraph.debug = false; -LiteGraph.catch_exceptions = true; -LiteGraph.throw_errors = true; -LiteGraph.allow_scripts = false; //if set to true some nodes like Formula would be allowed to evaluate code that comes from unsafe sources (like node configuration); which could lead to exploits - -LiteGraph.searchbox_extras = {}; //used to add extra features to the search box -LiteGraph.auto_sort_node_types = true; // [true!] If set to true; will automatically sort node types / categories in the context menus -LiteGraph.node_box_coloured_when_on = true; // [true!] this make the nodes box (top left circle) coloured when triggered (execute/action); visual feedback -LiteGraph.node_box_coloured_by_mode = true; // [true!] nodebox based on node mode; visual feedback -LiteGraph.dialog_close_on_mouse_leave = false; // [false on mobile] better true if not touch device; -LiteGraph.dialog_close_on_mouse_leave_delay = 500; -LiteGraph.shift_click_do_break_link_from = false; // [false!] prefer false if results too easy to break links -LiteGraph.click_do_break_link_to = false; // [false!]prefer false; way too easy to break links -LiteGraph.search_hide_on_mouse_leave = false; // [false on mobile] better true if not touch device; -LiteGraph.search_filter_enabled = true; // [true!] enable filtering slots type in the search widget; !requires auto_load_slot_types or manual set registered_slot_[in/out]_types and slot_types_[in/out] -LiteGraph.search_show_all_on_open = true; // [true!] opens the results list when opening the search widget - -LiteGraph.auto_load_slot_types = true; // [if want false; use true; run; get vars values to be statically set; than disable] nodes types and nodeclass association with node types need to be calculated; if dont want this; calculate once and set registered_slot_[in/out]_types and slot_types_[in/out] -/*// set these values if not using auto_load_slot_types -LiteGraph.registered_slot_in_types = {}; // slot types for nodeclass -LiteGraph.registered_slot_out_types = {}; // slot types for nodeclass -LiteGraph.slot_types_in = []; // slot types IN -LiteGraph.slot_types_out = []; // slot types OUT*/ - -LiteGraph.alt_drag_do_clone_nodes = true; // [true!] very handy; ALT click to clone and drag the new node -LiteGraph.do_add_triggers_slots = true; // [true!] will create and connect event slots when using action/events connections; !WILL CHANGE node mode when using onTrigger (enable mode colors); onExecuted does not need this -LiteGraph.allow_multi_output_for_events = false; // [false!] being events; it is strongly reccomended to use them sequentially; one by one -LiteGraph.middle_click_slot_add_default_node = true; //[true!] allows to create and connect a ndoe clicking with the third button (wheel) -LiteGraph.release_link_on_empty_shows_menu = true; //[true!] dragging a link to empty space will open a menu, add from list, search or defaults -LiteGraph.pointerevents_method = "pointer"; // "mouse"|"pointer" use mouse for retrocompatibility issues? (none found @ now) -LiteGraph.ctrl_shift_v_paste_connect_unselected_outputs = true; //[true!] allows ctrl + shift + v to paste nodes with the outputs of the unselected nodes connected with the inputs of the newly pasted nodes \ No newline at end of file diff --git a/editor/js/demos.js b/editor/js/demos.js deleted file mode 100755 index 9bd0f804c..000000000 --- a/editor/js/demos.js +++ /dev/null @@ -1,307 +0,0 @@ - -function demo() -{ - multiConnection(); -} - -function multiConnection() -{ - var node_button = LiteGraph.createNode("widget/button"); - node_button.pos = [100,400]; - graph.add(node_button); - - var node_console = LiteGraph.createNode("basic/console"); - node_console.pos = [400,400]; - graph.add(node_console); - node_button.connect(0, node_console ); - - var node_const_A = LiteGraph.createNode("basic/const"); - node_const_A.pos = [200,200]; - graph.add(node_const_A); - node_const_A.setValue(4.5); - - var node_const_B = LiteGraph.createNode("basic/const"); - node_const_B.pos = [200,300]; - graph.add(node_const_B); - node_const_B.setValue(10); - - var node_math = LiteGraph.createNode("math/operation"); - node_math.pos = [400,200]; - graph.add(node_math); - - var node_watch = LiteGraph.createNode("basic/watch"); - node_watch.pos = [700,200]; - graph.add(node_watch); - - var node_watch2 = LiteGraph.createNode("basic/watch"); - node_watch2.pos = [700,300]; - graph.add(node_watch2); - - node_const_A.connect(0,node_math,0 ); - node_const_B.connect(0,node_math,1 ); - node_math.connect(0,node_watch,0 ); - node_math.connect(0,node_watch2,0 ); -} - -function CopyPasteWithConnectionToUnselectedOutputTest() -{ - // number - var nodeConstA = LiteGraph.createNode("basic/const"); - nodeConstA.pos = [200,200]; - graph.add(nodeConstA); - nodeConstA.setValue(4.5); - - // number - var nodeConstB = LiteGraph.createNode("basic/const"); - nodeConstB.pos = [200,300]; - graph.add(nodeConstB); - nodeConstB.setValue(10); - - // math - var nodeMath = LiteGraph.createNode("math/operation"); - nodeMath.pos = [400,200]; - graph.add(nodeMath); - - // connection - nodeConstA.connect(0,nodeMath,0 ); - nodeConstB.connect(0,nodeMath,1 ); - - // copy with unselected nodes connected - graphcanvas.selectNodes([nodeMath]); - graphcanvas.copyToClipboard(); - graphcanvas.pasteFromClipboard(true); - - var count = 1; - var lastNode = null; - for (const [key, element] of Object.entries(graphcanvas.selected_nodes)) { - element.pos = [nodeMath.pos[0], nodeMath.pos[1] + 100 * count]; - lastNode = element - count++; - } - - // copy with unselected nodes unconnected - graphcanvas.pasteFromClipboard(false); - var count = 1; - for (const [key, element] of Object.entries(graphcanvas.selected_nodes)) { - element.pos = [nodeMath.pos[0], lastNode.pos[1] + 100 * count]; - count++; - } -} - -function sortTest() -{ - var rand = LiteGraph.createNode("math/rand",null, {pos: [10,100] }); - graph.add(rand); - - var nodes = []; - for(var i = 4; i >= 1; i--) - { - var n = LiteGraph.createNode("basic/watch",null, {pos: [i * 120,100] }); - graph.add(n); - nodes[i-1] = n; - } - - rand.connect(0, nodes[0], 0); - - for(var i = 0; i < nodes.length - 1; i++) - nodes[i].connect(0,nodes[i+1], 0); -} - -function benchmark() -{ - var num_nodes = 200; - var nodes = []; - for(var i = 0; i < num_nodes; i++) - { - var n = LiteGraph.createNode("basic/watch",null, {pos: [(2000 * Math.random())|0, (2000 * Math.random())|0] }); - graph.add(n); - nodes.push(n); - } - - for(var i = 0; i < nodes.length; i++) - nodes[ (Math.random() * nodes.length)|0 ].connect(0, nodes[ (Math.random() * nodes.length)|0 ], 0 ); -} - - - -//Show value inside the debug console -function TestWidgetsNode() -{ - this.addOutput("","number"); - this.properties = {}; - var that = this; - this.slider = this.addWidget("slider","Slider", 0.5, function(v){}, { min: 0, max: 1} ); - this.number = this.addWidget("number","Number", 0.5, function(v){}, { min: 0, max: 100} ); - this.combo = this.addWidget("combo","Combo", "red", function(v){}, { values:["red","green","blue"]} ); - this.text = this.addWidget("text","Text", "edit me", function(v){}, {} ); - this.text2 = this.addWidget("text","Text", "multiline", function(v){}, { multiline:true } ); - 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; -} - -TestWidgetsNode.title = "Widgets"; - -LiteGraph.registerNodeType("features/widgets", TestWidgetsNode ); - -//Show value inside the debug console -function TestSpecialNode() -{ - this.addInput("","number"); - this.addOutput("","number"); - this.properties = {}; - var that = this; - this.size = this.computeSize(); - this.enabled = false; - this.visible = false; -} - -TestSpecialNode.title = "Custom Shapes"; -TestSpecialNode.title_mode = LiteGraph.TRANSPARENT_TITLE; -TestSpecialNode.slot_start_y = 20; - -TestSpecialNode.prototype.onDrawBackground = function(ctx) -{ - if(this.flags.collapsed) - return; - - ctx.fillStyle = "#555"; - ctx.fillRect(0,0,this.size[0],20); - - if(this.enabled) - { - ctx.fillStyle = "#AFB"; - ctx.beginPath(); - ctx.moveTo(this.size[0]-20,0); - ctx.lineTo(this.size[0]-25,20); - ctx.lineTo(this.size[0],20); - ctx.lineTo(this.size[0],0); - ctx.fill(); - } - - if(this.visible) - { - ctx.fillStyle = "#ABF"; - ctx.beginPath(); - ctx.moveTo(this.size[0]-40,0); - ctx.lineTo(this.size[0]-45,20); - ctx.lineTo(this.size[0]-25,20); - ctx.lineTo(this.size[0]-20,0); - ctx.fill(); - } - - ctx.strokeStyle = "#333"; - ctx.beginPath(); - ctx.moveTo(0,20); - ctx.lineTo(this.size[0]+1,20); - ctx.moveTo(this.size[0]-20,0); - ctx.lineTo(this.size[0]-25,20); - ctx.moveTo(this.size[0]-40,0); - ctx.lineTo(this.size[0]-45,20); - ctx.stroke(); - - if( this.mouseOver ) - { - ctx.fillStyle = "#AAA"; - ctx.fillText( "Example of helper", 0, this.size[1] + 14 ); - } -} - -TestSpecialNode.prototype.onMouseDown = function(e, pos) -{ - if(pos[1] > 20) - return; - - if( pos[0] > this.size[0] - 20) - this.enabled = !this.enabled; - else if( pos[0] > this.size[0] - 40) - this.visible = !this.visible; -} - -TestSpecialNode.prototype.onBounding = function(rect) -{ - if(!this.flags.collapsed && this.mouseOver ) - rect[3] = this.size[1] + 20; -} - -LiteGraph.registerNodeType("features/shape", TestSpecialNode ); - - - -//Show value inside the debug console -function TestSlotsNode() -{ - this.addInput("C","number"); - this.addOutput("A","number"); - this.addOutput("B","number"); - this.horizontal = true; - this.size = [100,40]; -} - -TestSlotsNode.title = "Flat Slots"; - - -LiteGraph.registerNodeType("features/slots", TestSlotsNode ); - - -//Show value inside the debug console -function TestPropertyEditorsNode() -{ - this.properties = { - name: "foo", - age: 10, - alive: true, - children: ["John","Emily","Charles"], - skills: { - speed: 10, - dexterity: 100 - } - } - - var that = this; - this.addWidget("button","Log",null,function(){ - console.log(that.properties); - }); -} - -TestPropertyEditorsNode.title = "Properties"; - - -LiteGraph.registerNodeType("features/properties_editor", TestPropertyEditorsNode ); - - - -//Show value inside the debug console -function LargeInputNode() -{ - this.addInput("in 1","number"); - this.addInput("in 2","number"); - this.addInput("in 3","number"); - this.addInput("in 4","number"); - this.addInput("in 5","number"); - this.addInput("in 6","number"); - this.addInput("in 7","number"); - this.addInput("in 8","number"); - this.addInput("in 9","number"); - this.addInput("in 10","number"); - this.addInput("in 11","number"); - this.addInput("in 12","number"); - this.addInput("in 13","number"); - this.addInput("in 14","number"); - this.addInput("in 15","number"); - this.addInput("in 16","number"); - this.addInput("in 17","number"); - this.addInput("in 18","number"); - this.addInput("in 19","number"); - this.addInput("in 20","number"); - this.size = [200,410]; -} - -LargeInputNode.title = "Large Input Node"; - - -LiteGraph.registerNodeType("features/largeinput_editor", LargeInputNode); - diff --git a/editor/js/libs/audiosynth.js b/editor/js/libs/audiosynth.js deleted file mode 100755 index beb0cc53d..000000000 --- a/editor/js/libs/audiosynth.js +++ /dev/null @@ -1,356 +0,0 @@ -var Synth, AudioSynth, AudioSynthInstrument; -!function(){ - - var URL = window.URL || window.webkitURL; - var Blob = window.Blob; - - if(!URL || !Blob) { - throw new Error('This browser does not support AudioSynth'); - } - - var _encapsulated = false; - var AudioSynthInstance = null; - var pack = function(c,arg){ return [new Uint8Array([arg, arg >> 8]), new Uint8Array([arg, arg >> 8, arg >> 16, arg >> 24])][c]; }; - var setPrivateVar = function(n,v,w,e){Object.defineProperty(this,n,{value:v,writable:!!w,enumerable:!!e});}; - var setPublicVar = function(n,v,w){setPrivateVar.call(this,n,v,w,true);}; - AudioSynthInstrument = function AudioSynthInstrument(){this.__init__.apply(this,arguments);}; - var setPriv = setPrivateVar.bind(AudioSynthInstrument.prototype); - var setPub = setPublicVar.bind(AudioSynthInstrument.prototype); - setPriv('__init__', function(a,b,c) { - if(!_encapsulated) { throw new Error('AudioSynthInstrument can only be instantiated from the createInstrument method of the AudioSynth object.'); } - setPrivateVar.call(this, '_parent', a); - setPublicVar.call(this, 'name', b); - setPrivateVar.call(this, '_soundID', c); - }); - setPub('play', function(note, octave, duration,volume) { - return this._parent.play(this._soundID, note, octave, duration, volume); - }); - setPub('generate', function(note, octave, duration) { - return this._parent.generate(this._soundID, note, octave, duration); - }); - AudioSynth = function AudioSynth(){if(AudioSynthInstance instanceof AudioSynth){return AudioSynthInstance;}else{ this.__init__(); return this; }}; - setPriv = setPrivateVar.bind(AudioSynth.prototype); - setPub = setPublicVar.bind(AudioSynth.prototype); - setPriv('_debug',false,true); - setPriv('_bitsPerSample',16); - setPriv('_channels',1); - setPriv('_sampleRate',44100,true); - setPub('setSampleRate', function(v) { - this._sampleRate = Math.max(Math.min(v|0,44100), 4000); - this._clearCache(); - return this._sampleRate; - }); - setPub('getSampleRate', function() { return this._sampleRate; }); - setPriv('_volume',32768,true); - setPub('setVolume', function(v) { - v = parseFloat(v); if(isNaN(v)) { v = 0; } - v = Math.round(v*32768); - this._volume = Math.max(Math.min(v|0,32768), 0); - this._clearCache(); - return this._volume; - }); - setPub('getVolume', function() { return Math.round(this._volume/32768*10000)/10000; }); - setPriv('_notes',{'C':261.63,'C#':277.18,'D':293.66,'D#':311.13,'E':329.63,'F':346.23,'F#':369.99,'G':392.00,'G#':415.30,'A':440.00,'A#':466.16,'B':493.88}); - setPriv('_fileCache',[],true); - setPriv('_temp',{},true); - setPriv('_sounds',[],true); - setPriv('_mod',[function(i,s,f,x){return Math.sin((2 * Math.PI)*(i/s)*f+x);}]); - setPriv('_resizeCache', function() { - var f = this._fileCache; - var l = this._sounds.length; - while(f.length> 8; - - } - - for (; i !== decayLen; i++) { - - val = volume * Math.pow((1-((i-(sampleRate*attack))/(sampleRate*(time-attack)))),dampen) * waveFunc.call(waveBind, i, sampleRate, frequency, volume); - - data[i << 1] = val; - data[(i << 1) + 1] = val >> 8; - - } - - var out = [ - 'RIFF', - pack(1, 4 + (8 + 24/* chunk 1 length */) + (8 + 8/* chunk 2 length */)), // Length - 'WAVE', - // chunk 1 - 'fmt ', // Sub-chunk identifier - pack(1, 16), // Chunk length - pack(0, 1), // Audio format (1 is linear quantization) - pack(0, channels), - pack(1, sampleRate), - pack(1, sampleRate * channels * bitsPerSample / 8), // Byte rate - pack(0, channels * bitsPerSample / 8), - pack(0, bitsPerSample), - // chunk 2 - 'data', // Sub-chunk identifier - pack(1, data.length * channels * bitsPerSample / 8), // Chunk length - data - ]; - var blob = new Blob(out, {type: 'audio/wav'}); - var dataURI = URL.createObjectURL(blob); - this._fileCache[sound][octave-1][note][time] = dataURI; - if(this._debug) { console.log((new Date).valueOf() - t, 'ms to generate'); } - return dataURI; - } - }); - setPub('play', function(sound, note, octave, duration, volume) { - var src = this.generate( sound, note, octave, duration ); - var audio = new Audio(src); - if(volume != null) - { - if(volume <= 0) - return true; - audio.volume = volume > 1 ? 1 : volume; - } - audio.play(); - return true; - }); - setPub('debug', function() { this._debug = true; }); - setPub('createInstrument', function(sound) { - var n = 0; - var found = false; - if(typeof(sound)=='string') { - for(var i=0;i=(valueTable.length-1)?0:playVal+1] + valueTable[playVal]) * 0.5; - - if(playVal>=Math.floor(period)) { - if(playVal=p_hundredth) { - // Reset - resetPlay = true; - valueTable[playVal+1] = (valueTable[0] + valueTable[playVal+1]) * 0.5; - vars.periodCount++; - } - } else { - resetPlay = true; - } - } - - var _return = valueTable[playVal]; - if(resetPlay) { vars.playVal = 0; } else { vars.playVal++; } - - return _return; - - } - } -}, -{ - name: 'edm', - attack: function() { return 0.002; }, - dampen: function() { return 1; }, - wave: function(i, sampleRate, frequency) { - var base = this.modulate[0]; - var mod = this.modulate.slice(1); - return mod[0]( - i, - sampleRate, - frequency, - mod[9]( - i, - sampleRate, - frequency, - mod[2]( - i, - sampleRate, - frequency, - Math.pow(base(i, sampleRate, frequency, 0), 3) + - Math.pow(base(i, sampleRate, frequency, 0.5), 5) + - Math.pow(base(i, sampleRate, frequency, 1), 7) - ) - ) + - mod[8]( - i, - sampleRate, - frequency, - base(i, sampleRate, frequency, 1.75) - ) - ); - } -}); diff --git a/editor/js/libs/gl-matrix-min.js b/editor/js/libs/gl-matrix-min.js deleted file mode 100755 index 747b3a77e..000000000 --- a/editor/js/libs/gl-matrix-min.js +++ /dev/null @@ -1,28 +0,0 @@ -/*! -@fileoverview gl-matrix - High performance matrix and vector operations -@author Brandon Jones -@author Colin MacKenzie IV -@version 2.7.0 - -Copyright (c) 2015-2018, Brandon Jones, Colin MacKenzie IV. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - -*/ -!function(t,n){if("object"==typeof exports&&"object"==typeof module)module.exports=n();else if("function"==typeof define&&define.amd)define([],n);else{var r=n();for(var a in r)("object"==typeof exports?exports:t)[a]=r[a]}}("undefined"!=typeof self?self:this,function(){return function(t){var n={};function r(a){if(n[a])return n[a].exports;var e=n[a]={i:a,l:!1,exports:{}};return t[a].call(e.exports,e,e.exports,r),e.l=!0,e.exports}return r.m=t,r.c=n,r.d=function(t,n,a){r.o(t,n)||Object.defineProperty(t,n,{enumerable:!0,get:a})},r.r=function(t){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},r.t=function(t,n){if(1&n&&(t=r(t)),8&n)return t;if(4&n&&"object"==typeof t&&t&&t.__esModule)return t;var a=Object.create(null);if(r.r(a),Object.defineProperty(a,"default",{enumerable:!0,value:t}),2&n&&"string"!=typeof t)for(var e in t)r.d(a,e,function(n){return t[n]}.bind(null,e));return a},r.n=function(t){var n=t&&t.__esModule?function(){return t.default}:function(){return t};return r.d(n,"a",n),n},r.o=function(t,n){return Object.prototype.hasOwnProperty.call(t,n)},r.p="",r(r.s=10)}([function(t,n,r){"use strict";Object.defineProperty(n,"__esModule",{value:!0}),n.setMatrixArrayType=function(t){n.ARRAY_TYPE=t},n.toRadian=function(t){return t*e},n.equals=function(t,n){return Math.abs(t-n)<=a*Math.max(1,Math.abs(t),Math.abs(n))};var a=n.EPSILON=1e-6;n.ARRAY_TYPE="undefined"!=typeof Float32Array?Float32Array:Array,n.RANDOM=Math.random;var e=Math.PI/180},function(t,n,r){"use strict";Object.defineProperty(n,"__esModule",{value:!0}),n.forEach=n.sqrLen=n.len=n.sqrDist=n.dist=n.div=n.mul=n.sub=void 0,n.create=e,n.clone=function(t){var n=new a.ARRAY_TYPE(4);return n[0]=t[0],n[1]=t[1],n[2]=t[2],n[3]=t[3],n},n.fromValues=function(t,n,r,e){var u=new a.ARRAY_TYPE(4);return u[0]=t,u[1]=n,u[2]=r,u[3]=e,u},n.copy=function(t,n){return t[0]=n[0],t[1]=n[1],t[2]=n[2],t[3]=n[3],t},n.set=function(t,n,r,a,e){return t[0]=n,t[1]=r,t[2]=a,t[3]=e,t},n.add=function(t,n,r){return t[0]=n[0]+r[0],t[1]=n[1]+r[1],t[2]=n[2]+r[2],t[3]=n[3]+r[3],t},n.subtract=u,n.multiply=o,n.divide=i,n.ceil=function(t,n){return t[0]=Math.ceil(n[0]),t[1]=Math.ceil(n[1]),t[2]=Math.ceil(n[2]),t[3]=Math.ceil(n[3]),t},n.floor=function(t,n){return t[0]=Math.floor(n[0]),t[1]=Math.floor(n[1]),t[2]=Math.floor(n[2]),t[3]=Math.floor(n[3]),t},n.min=function(t,n,r){return t[0]=Math.min(n[0],r[0]),t[1]=Math.min(n[1],r[1]),t[2]=Math.min(n[2],r[2]),t[3]=Math.min(n[3],r[3]),t},n.max=function(t,n,r){return t[0]=Math.max(n[0],r[0]),t[1]=Math.max(n[1],r[1]),t[2]=Math.max(n[2],r[2]),t[3]=Math.max(n[3],r[3]),t},n.round=function(t,n){return t[0]=Math.round(n[0]),t[1]=Math.round(n[1]),t[2]=Math.round(n[2]),t[3]=Math.round(n[3]),t},n.scale=function(t,n,r){return t[0]=n[0]*r,t[1]=n[1]*r,t[2]=n[2]*r,t[3]=n[3]*r,t},n.scaleAndAdd=function(t,n,r,a){return t[0]=n[0]+r[0]*a,t[1]=n[1]+r[1]*a,t[2]=n[2]+r[2]*a,t[3]=n[3]+r[3]*a,t},n.distance=s,n.squaredDistance=c,n.length=f,n.squaredLength=M,n.negate=function(t,n){return t[0]=-n[0],t[1]=-n[1],t[2]=-n[2],t[3]=-n[3],t},n.inverse=function(t,n){return t[0]=1/n[0],t[1]=1/n[1],t[2]=1/n[2],t[3]=1/n[3],t},n.normalize=function(t,n){var r=n[0],a=n[1],e=n[2],u=n[3],o=r*r+a*a+e*e+u*u;o>0&&(o=1/Math.sqrt(o),t[0]=r*o,t[1]=a*o,t[2]=e*o,t[3]=u*o);return t},n.dot=function(t,n){return t[0]*n[0]+t[1]*n[1]+t[2]*n[2]+t[3]*n[3]},n.lerp=function(t,n,r,a){var e=n[0],u=n[1],o=n[2],i=n[3];return t[0]=e+a*(r[0]-e),t[1]=u+a*(r[1]-u),t[2]=o+a*(r[2]-o),t[3]=i+a*(r[3]-i),t},n.random=function(t,n){var r,e,u,o,i,s;n=n||1;do{r=2*a.RANDOM()-1,e=2*a.RANDOM()-1,i=r*r+e*e}while(i>=1);do{u=2*a.RANDOM()-1,o=2*a.RANDOM()-1,s=u*u+o*o}while(s>=1);var c=Math.sqrt((1-i)/s);return t[0]=n*r,t[1]=n*e,t[2]=n*u*c,t[3]=n*o*c,t},n.transformMat4=function(t,n,r){var a=n[0],e=n[1],u=n[2],o=n[3];return t[0]=r[0]*a+r[4]*e+r[8]*u+r[12]*o,t[1]=r[1]*a+r[5]*e+r[9]*u+r[13]*o,t[2]=r[2]*a+r[6]*e+r[10]*u+r[14]*o,t[3]=r[3]*a+r[7]*e+r[11]*u+r[15]*o,t},n.transformQuat=function(t,n,r){var a=n[0],e=n[1],u=n[2],o=r[0],i=r[1],s=r[2],c=r[3],f=c*a+i*u-s*e,M=c*e+s*a-o*u,h=c*u+o*e-i*a,l=-o*a-i*e-s*u;return t[0]=f*c+l*-o+M*-s-h*-i,t[1]=M*c+l*-i+h*-o-f*-s,t[2]=h*c+l*-s+f*-i-M*-o,t[3]=n[3],t},n.str=function(t){return"vec4("+t[0]+", "+t[1]+", "+t[2]+", "+t[3]+")"},n.exactEquals=function(t,n){return t[0]===n[0]&&t[1]===n[1]&&t[2]===n[2]&&t[3]===n[3]},n.equals=function(t,n){var r=t[0],e=t[1],u=t[2],o=t[3],i=n[0],s=n[1],c=n[2],f=n[3];return Math.abs(r-i)<=a.EPSILON*Math.max(1,Math.abs(r),Math.abs(i))&&Math.abs(e-s)<=a.EPSILON*Math.max(1,Math.abs(e),Math.abs(s))&&Math.abs(u-c)<=a.EPSILON*Math.max(1,Math.abs(u),Math.abs(c))&&Math.abs(o-f)<=a.EPSILON*Math.max(1,Math.abs(o),Math.abs(f))};var a=function(t){if(t&&t.__esModule)return t;var n={};if(null!=t)for(var r in t)Object.prototype.hasOwnProperty.call(t,r)&&(n[r]=t[r]);return n.default=t,n}(r(0));function e(){var t=new a.ARRAY_TYPE(4);return a.ARRAY_TYPE!=Float32Array&&(t[0]=0,t[1]=0,t[2]=0,t[3]=0),t}function u(t,n,r){return t[0]=n[0]-r[0],t[1]=n[1]-r[1],t[2]=n[2]-r[2],t[3]=n[3]-r[3],t}function o(t,n,r){return t[0]=n[0]*r[0],t[1]=n[1]*r[1],t[2]=n[2]*r[2],t[3]=n[3]*r[3],t}function i(t,n,r){return t[0]=n[0]/r[0],t[1]=n[1]/r[1],t[2]=n[2]/r[2],t[3]=n[3]/r[3],t}function s(t,n){var r=n[0]-t[0],a=n[1]-t[1],e=n[2]-t[2],u=n[3]-t[3];return Math.sqrt(r*r+a*a+e*e+u*u)}function c(t,n){var r=n[0]-t[0],a=n[1]-t[1],e=n[2]-t[2],u=n[3]-t[3];return r*r+a*a+e*e+u*u}function f(t){var n=t[0],r=t[1],a=t[2],e=t[3];return Math.sqrt(n*n+r*r+a*a+e*e)}function M(t){var n=t[0],r=t[1],a=t[2],e=t[3];return n*n+r*r+a*a+e*e}n.sub=u,n.mul=o,n.div=i,n.dist=s,n.sqrDist=c,n.len=f,n.sqrLen=M,n.forEach=function(){var t=e();return function(n,r,a,e,u,o){var i=void 0,s=void 0;for(r||(r=4),a||(a=0),s=e?Math.min(e*r+a,n.length):n.length,i=a;i1?0:e<-1?Math.PI:Math.acos(e)},n.str=function(t){return"vec3("+t[0]+", "+t[1]+", "+t[2]+")"},n.exactEquals=function(t,n){return t[0]===n[0]&&t[1]===n[1]&&t[2]===n[2]},n.equals=function(t,n){var r=t[0],e=t[1],u=t[2],o=n[0],i=n[1],s=n[2];return Math.abs(r-o)<=a.EPSILON*Math.max(1,Math.abs(r),Math.abs(o))&&Math.abs(e-i)<=a.EPSILON*Math.max(1,Math.abs(e),Math.abs(i))&&Math.abs(u-s)<=a.EPSILON*Math.max(1,Math.abs(u),Math.abs(s))};var a=function(t){if(t&&t.__esModule)return t;var n={};if(null!=t)for(var r in t)Object.prototype.hasOwnProperty.call(t,r)&&(n[r]=t[r]);return n.default=t,n}(r(0));function e(){var t=new a.ARRAY_TYPE(3);return a.ARRAY_TYPE!=Float32Array&&(t[0]=0,t[1]=0,t[2]=0),t}function u(t){var n=t[0],r=t[1],a=t[2];return Math.sqrt(n*n+r*r+a*a)}function o(t,n,r){var e=new a.ARRAY_TYPE(3);return e[0]=t,e[1]=n,e[2]=r,e}function i(t,n,r){return t[0]=n[0]-r[0],t[1]=n[1]-r[1],t[2]=n[2]-r[2],t}function s(t,n,r){return t[0]=n[0]*r[0],t[1]=n[1]*r[1],t[2]=n[2]*r[2],t}function c(t,n,r){return t[0]=n[0]/r[0],t[1]=n[1]/r[1],t[2]=n[2]/r[2],t}function f(t,n){var r=n[0]-t[0],a=n[1]-t[1],e=n[2]-t[2];return Math.sqrt(r*r+a*a+e*e)}function M(t,n){var r=n[0]-t[0],a=n[1]-t[1],e=n[2]-t[2];return r*r+a*a+e*e}function h(t){var n=t[0],r=t[1],a=t[2];return n*n+r*r+a*a}function l(t,n){var r=n[0],a=n[1],e=n[2],u=r*r+a*a+e*e;return u>0&&(u=1/Math.sqrt(u),t[0]=n[0]*u,t[1]=n[1]*u,t[2]=n[2]*u),t}function v(t,n){return t[0]*n[0]+t[1]*n[1]+t[2]*n[2]}n.sub=i,n.mul=s,n.div=c,n.dist=f,n.sqrDist=M,n.len=u,n.sqrLen=h,n.forEach=function(){var t=e();return function(n,r,a,e,u,o){var i=void 0,s=void 0;for(r||(r=3),a||(a=0),s=e?Math.min(e*r+a,n.length):n.length,i=a;ia.EPSILON?(t[0]=n[0]/e,t[1]=n[1]/e,t[2]=n[2]/e):(t[0]=1,t[1]=0,t[2]=0);return r},n.multiply=f,n.rotateX=function(t,n,r){r*=.5;var a=n[0],e=n[1],u=n[2],o=n[3],i=Math.sin(r),s=Math.cos(r);return t[0]=a*s+o*i,t[1]=e*s+u*i,t[2]=u*s-e*i,t[3]=o*s-a*i,t},n.rotateY=function(t,n,r){r*=.5;var a=n[0],e=n[1],u=n[2],o=n[3],i=Math.sin(r),s=Math.cos(r);return t[0]=a*s-u*i,t[1]=e*s+o*i,t[2]=u*s+a*i,t[3]=o*s-e*i,t},n.rotateZ=function(t,n,r){r*=.5;var a=n[0],e=n[1],u=n[2],o=n[3],i=Math.sin(r),s=Math.cos(r);return t[0]=a*s+e*i,t[1]=e*s-a*i,t[2]=u*s+o*i,t[3]=o*s-u*i,t},n.calculateW=function(t,n){var r=n[0],a=n[1],e=n[2];return t[0]=r,t[1]=a,t[2]=e,t[3]=Math.sqrt(Math.abs(1-r*r-a*a-e*e)),t},n.slerp=M,n.random=function(t){var n=a.RANDOM(),r=a.RANDOM(),e=a.RANDOM(),u=Math.sqrt(1-n),o=Math.sqrt(n);return t[0]=u*Math.sin(2*Math.PI*r),t[1]=u*Math.cos(2*Math.PI*r),t[2]=o*Math.sin(2*Math.PI*e),t[3]=o*Math.cos(2*Math.PI*e),t},n.invert=function(t,n){var r=n[0],a=n[1],e=n[2],u=n[3],o=r*r+a*a+e*e+u*u,i=o?1/o:0;return t[0]=-r*i,t[1]=-a*i,t[2]=-e*i,t[3]=u*i,t},n.conjugate=function(t,n){return t[0]=-n[0],t[1]=-n[1],t[2]=-n[2],t[3]=n[3],t},n.fromMat3=h,n.fromEuler=function(t,n,r,a){var e=.5*Math.PI/180;n*=e,r*=e,a*=e;var u=Math.sin(n),o=Math.cos(n),i=Math.sin(r),s=Math.cos(r),c=Math.sin(a),f=Math.cos(a);return t[0]=u*s*f-o*i*c,t[1]=o*i*f+u*s*c,t[2]=o*s*c-u*i*f,t[3]=o*s*f+u*i*c,t},n.str=function(t){return"quat("+t[0]+", "+t[1]+", "+t[2]+", "+t[3]+")"};var a=i(r(0)),e=i(r(5)),u=i(r(2)),o=i(r(1));function i(t){if(t&&t.__esModule)return t;var n={};if(null!=t)for(var r in t)Object.prototype.hasOwnProperty.call(t,r)&&(n[r]=t[r]);return n.default=t,n}function s(){var t=new a.ARRAY_TYPE(4);return a.ARRAY_TYPE!=Float32Array&&(t[0]=0,t[1]=0,t[2]=0),t[3]=1,t}function c(t,n,r){r*=.5;var a=Math.sin(r);return t[0]=a*n[0],t[1]=a*n[1],t[2]=a*n[2],t[3]=Math.cos(r),t}function f(t,n,r){var a=n[0],e=n[1],u=n[2],o=n[3],i=r[0],s=r[1],c=r[2],f=r[3];return t[0]=a*f+o*i+e*c-u*s,t[1]=e*f+o*s+u*i-a*c,t[2]=u*f+o*c+a*s-e*i,t[3]=o*f-a*i-e*s-u*c,t}function M(t,n,r,e){var u=n[0],o=n[1],i=n[2],s=n[3],c=r[0],f=r[1],M=r[2],h=r[3],l=void 0,v=void 0,d=void 0,b=void 0,m=void 0;return(v=u*c+o*f+i*M+s*h)<0&&(v=-v,c=-c,f=-f,M=-M,h=-h),1-v>a.EPSILON?(l=Math.acos(v),d=Math.sin(l),b=Math.sin((1-e)*l)/d,m=Math.sin(e*l)/d):(b=1-e,m=e),t[0]=b*u+m*c,t[1]=b*o+m*f,t[2]=b*i+m*M,t[3]=b*s+m*h,t}function h(t,n){var r=n[0]+n[4]+n[8],a=void 0;if(r>0)a=Math.sqrt(r+1),t[3]=.5*a,a=.5/a,t[0]=(n[5]-n[7])*a,t[1]=(n[6]-n[2])*a,t[2]=(n[1]-n[3])*a;else{var e=0;n[4]>n[0]&&(e=1),n[8]>n[3*e+e]&&(e=2);var u=(e+1)%3,o=(e+2)%3;a=Math.sqrt(n[3*e+e]-n[3*u+u]-n[3*o+o]+1),t[e]=.5*a,a=.5/a,t[3]=(n[3*u+o]-n[3*o+u])*a,t[u]=(n[3*u+e]+n[3*e+u])*a,t[o]=(n[3*o+e]+n[3*e+o])*a}return t}n.clone=o.clone,n.fromValues=o.fromValues,n.copy=o.copy,n.set=o.set,n.add=o.add,n.mul=f,n.scale=o.scale,n.dot=o.dot,n.lerp=o.lerp;var l=n.length=o.length,v=(n.len=l,n.squaredLength=o.squaredLength),d=(n.sqrLen=v,n.normalize=o.normalize);n.exactEquals=o.exactEquals,n.equals=o.equals,n.rotationTo=function(){var t=u.create(),n=u.fromValues(1,0,0),r=u.fromValues(0,1,0);return function(a,e,o){var i=u.dot(e,o);return i<-.999999?(u.cross(t,n,e),u.len(t)<1e-6&&u.cross(t,r,e),u.normalize(t,t),c(a,t,Math.PI),a):i>.999999?(a[0]=0,a[1]=0,a[2]=0,a[3]=1,a):(u.cross(t,e,o),a[0]=t[0],a[1]=t[1],a[2]=t[2],a[3]=1+i,d(a,a))}}(),n.sqlerp=function(){var t=s(),n=s();return function(r,a,e,u,o,i){return M(t,a,o,i),M(n,e,u,i),M(r,t,n,2*i*(1-i)),r}}(),n.setAxes=function(){var t=e.create();return function(n,r,a,e){return t[0]=a[0],t[3]=a[1],t[6]=a[2],t[1]=e[0],t[4]=e[1],t[7]=e[2],t[2]=-r[0],t[5]=-r[1],t[8]=-r[2],d(n,h(n,t))}}()},function(t,n,r){"use strict";Object.defineProperty(n,"__esModule",{value:!0}),n.sub=n.mul=void 0,n.create=function(){var t=new a.ARRAY_TYPE(16);a.ARRAY_TYPE!=Float32Array&&(t[1]=0,t[2]=0,t[3]=0,t[4]=0,t[6]=0,t[7]=0,t[8]=0,t[9]=0,t[11]=0,t[12]=0,t[13]=0,t[14]=0);return t[0]=1,t[5]=1,t[10]=1,t[15]=1,t},n.clone=function(t){var n=new a.ARRAY_TYPE(16);return n[0]=t[0],n[1]=t[1],n[2]=t[2],n[3]=t[3],n[4]=t[4],n[5]=t[5],n[6]=t[6],n[7]=t[7],n[8]=t[8],n[9]=t[9],n[10]=t[10],n[11]=t[11],n[12]=t[12],n[13]=t[13],n[14]=t[14],n[15]=t[15],n},n.copy=function(t,n){return t[0]=n[0],t[1]=n[1],t[2]=n[2],t[3]=n[3],t[4]=n[4],t[5]=n[5],t[6]=n[6],t[7]=n[7],t[8]=n[8],t[9]=n[9],t[10]=n[10],t[11]=n[11],t[12]=n[12],t[13]=n[13],t[14]=n[14],t[15]=n[15],t},n.fromValues=function(t,n,r,e,u,o,i,s,c,f,M,h,l,v,d,b){var m=new a.ARRAY_TYPE(16);return m[0]=t,m[1]=n,m[2]=r,m[3]=e,m[4]=u,m[5]=o,m[6]=i,m[7]=s,m[8]=c,m[9]=f,m[10]=M,m[11]=h,m[12]=l,m[13]=v,m[14]=d,m[15]=b,m},n.set=function(t,n,r,a,e,u,o,i,s,c,f,M,h,l,v,d,b){return t[0]=n,t[1]=r,t[2]=a,t[3]=e,t[4]=u,t[5]=o,t[6]=i,t[7]=s,t[8]=c,t[9]=f,t[10]=M,t[11]=h,t[12]=l,t[13]=v,t[14]=d,t[15]=b,t},n.identity=e,n.transpose=function(t,n){if(t===n){var r=n[1],a=n[2],e=n[3],u=n[6],o=n[7],i=n[11];t[1]=n[4],t[2]=n[8],t[3]=n[12],t[4]=r,t[6]=n[9],t[7]=n[13],t[8]=a,t[9]=u,t[11]=n[14],t[12]=e,t[13]=o,t[14]=i}else t[0]=n[0],t[1]=n[4],t[2]=n[8],t[3]=n[12],t[4]=n[1],t[5]=n[5],t[6]=n[9],t[7]=n[13],t[8]=n[2],t[9]=n[6],t[10]=n[10],t[11]=n[14],t[12]=n[3],t[13]=n[7],t[14]=n[11],t[15]=n[15];return t},n.invert=function(t,n){var r=n[0],a=n[1],e=n[2],u=n[3],o=n[4],i=n[5],s=n[6],c=n[7],f=n[8],M=n[9],h=n[10],l=n[11],v=n[12],d=n[13],b=n[14],m=n[15],p=r*i-a*o,P=r*s-e*o,A=r*c-u*o,E=a*s-e*i,O=a*c-u*i,R=e*c-u*s,y=f*d-M*v,q=f*b-h*v,x=f*m-l*v,_=M*b-h*d,Y=M*m-l*d,L=h*m-l*b,S=p*L-P*Y+A*_+E*x-O*q+R*y;if(!S)return null;return S=1/S,t[0]=(i*L-s*Y+c*_)*S,t[1]=(e*Y-a*L-u*_)*S,t[2]=(d*R-b*O+m*E)*S,t[3]=(h*O-M*R-l*E)*S,t[4]=(s*x-o*L-c*q)*S,t[5]=(r*L-e*x+u*q)*S,t[6]=(b*A-v*R-m*P)*S,t[7]=(f*R-h*A+l*P)*S,t[8]=(o*Y-i*x+c*y)*S,t[9]=(a*x-r*Y-u*y)*S,t[10]=(v*O-d*A+m*p)*S,t[11]=(M*A-f*O-l*p)*S,t[12]=(i*q-o*_-s*y)*S,t[13]=(r*_-a*q+e*y)*S,t[14]=(d*P-v*E-b*p)*S,t[15]=(f*E-M*P+h*p)*S,t},n.adjoint=function(t,n){var r=n[0],a=n[1],e=n[2],u=n[3],o=n[4],i=n[5],s=n[6],c=n[7],f=n[8],M=n[9],h=n[10],l=n[11],v=n[12],d=n[13],b=n[14],m=n[15];return t[0]=i*(h*m-l*b)-M*(s*m-c*b)+d*(s*l-c*h),t[1]=-(a*(h*m-l*b)-M*(e*m-u*b)+d*(e*l-u*h)),t[2]=a*(s*m-c*b)-i*(e*m-u*b)+d*(e*c-u*s),t[3]=-(a*(s*l-c*h)-i*(e*l-u*h)+M*(e*c-u*s)),t[4]=-(o*(h*m-l*b)-f*(s*m-c*b)+v*(s*l-c*h)),t[5]=r*(h*m-l*b)-f*(e*m-u*b)+v*(e*l-u*h),t[6]=-(r*(s*m-c*b)-o*(e*m-u*b)+v*(e*c-u*s)),t[7]=r*(s*l-c*h)-o*(e*l-u*h)+f*(e*c-u*s),t[8]=o*(M*m-l*d)-f*(i*m-c*d)+v*(i*l-c*M),t[9]=-(r*(M*m-l*d)-f*(a*m-u*d)+v*(a*l-u*M)),t[10]=r*(i*m-c*d)-o*(a*m-u*d)+v*(a*c-u*i),t[11]=-(r*(i*l-c*M)-o*(a*l-u*M)+f*(a*c-u*i)),t[12]=-(o*(M*b-h*d)-f*(i*b-s*d)+v*(i*h-s*M)),t[13]=r*(M*b-h*d)-f*(a*b-e*d)+v*(a*h-e*M),t[14]=-(r*(i*b-s*d)-o*(a*b-e*d)+v*(a*s-e*i)),t[15]=r*(i*h-s*M)-o*(a*h-e*M)+f*(a*s-e*i),t},n.determinant=function(t){var n=t[0],r=t[1],a=t[2],e=t[3],u=t[4],o=t[5],i=t[6],s=t[7],c=t[8],f=t[9],M=t[10],h=t[11],l=t[12],v=t[13],d=t[14],b=t[15];return(n*o-r*u)*(M*b-h*d)-(n*i-a*u)*(f*b-h*v)+(n*s-e*u)*(f*d-M*v)+(r*i-a*o)*(c*b-h*l)-(r*s-e*o)*(c*d-M*l)+(a*s-e*i)*(c*v-f*l)},n.multiply=u,n.translate=function(t,n,r){var a=r[0],e=r[1],u=r[2],o=void 0,i=void 0,s=void 0,c=void 0,f=void 0,M=void 0,h=void 0,l=void 0,v=void 0,d=void 0,b=void 0,m=void 0;n===t?(t[12]=n[0]*a+n[4]*e+n[8]*u+n[12],t[13]=n[1]*a+n[5]*e+n[9]*u+n[13],t[14]=n[2]*a+n[6]*e+n[10]*u+n[14],t[15]=n[3]*a+n[7]*e+n[11]*u+n[15]):(o=n[0],i=n[1],s=n[2],c=n[3],f=n[4],M=n[5],h=n[6],l=n[7],v=n[8],d=n[9],b=n[10],m=n[11],t[0]=o,t[1]=i,t[2]=s,t[3]=c,t[4]=f,t[5]=M,t[6]=h,t[7]=l,t[8]=v,t[9]=d,t[10]=b,t[11]=m,t[12]=o*a+f*e+v*u+n[12],t[13]=i*a+M*e+d*u+n[13],t[14]=s*a+h*e+b*u+n[14],t[15]=c*a+l*e+m*u+n[15]);return t},n.scale=function(t,n,r){var a=r[0],e=r[1],u=r[2];return t[0]=n[0]*a,t[1]=n[1]*a,t[2]=n[2]*a,t[3]=n[3]*a,t[4]=n[4]*e,t[5]=n[5]*e,t[6]=n[6]*e,t[7]=n[7]*e,t[8]=n[8]*u,t[9]=n[9]*u,t[10]=n[10]*u,t[11]=n[11]*u,t[12]=n[12],t[13]=n[13],t[14]=n[14],t[15]=n[15],t},n.rotate=function(t,n,r,e){var u=e[0],o=e[1],i=e[2],s=Math.sqrt(u*u+o*o+i*i),c=void 0,f=void 0,M=void 0,h=void 0,l=void 0,v=void 0,d=void 0,b=void 0,m=void 0,p=void 0,P=void 0,A=void 0,E=void 0,O=void 0,R=void 0,y=void 0,q=void 0,x=void 0,_=void 0,Y=void 0,L=void 0,S=void 0,w=void 0,I=void 0;if(s0?(r[0]=2*(c*s+h*e+f*i-M*u)/l,r[1]=2*(f*s+h*u+M*e-c*i)/l,r[2]=2*(M*s+h*i+c*u-f*e)/l):(r[0]=2*(c*s+h*e+f*i-M*u),r[1]=2*(f*s+h*u+M*e-c*i),r[2]=2*(M*s+h*i+c*u-f*e));return o(t,n,r),t},n.getTranslation=function(t,n){return t[0]=n[12],t[1]=n[13],t[2]=n[14],t},n.getScaling=function(t,n){var r=n[0],a=n[1],e=n[2],u=n[4],o=n[5],i=n[6],s=n[8],c=n[9],f=n[10];return t[0]=Math.sqrt(r*r+a*a+e*e),t[1]=Math.sqrt(u*u+o*o+i*i),t[2]=Math.sqrt(s*s+c*c+f*f),t},n.getRotation=function(t,n){var r=n[0]+n[5]+n[10],a=0;r>0?(a=2*Math.sqrt(r+1),t[3]=.25*a,t[0]=(n[6]-n[9])/a,t[1]=(n[8]-n[2])/a,t[2]=(n[1]-n[4])/a):n[0]>n[5]&&n[0]>n[10]?(a=2*Math.sqrt(1+n[0]-n[5]-n[10]),t[3]=(n[6]-n[9])/a,t[0]=.25*a,t[1]=(n[1]+n[4])/a,t[2]=(n[8]+n[2])/a):n[5]>n[10]?(a=2*Math.sqrt(1+n[5]-n[0]-n[10]),t[3]=(n[8]-n[2])/a,t[0]=(n[1]+n[4])/a,t[1]=.25*a,t[2]=(n[6]+n[9])/a):(a=2*Math.sqrt(1+n[10]-n[0]-n[5]),t[3]=(n[1]-n[4])/a,t[0]=(n[8]+n[2])/a,t[1]=(n[6]+n[9])/a,t[2]=.25*a);return t},n.fromRotationTranslationScale=function(t,n,r,a){var e=n[0],u=n[1],o=n[2],i=n[3],s=e+e,c=u+u,f=o+o,M=e*s,h=e*c,l=e*f,v=u*c,d=u*f,b=o*f,m=i*s,p=i*c,P=i*f,A=a[0],E=a[1],O=a[2];return t[0]=(1-(v+b))*A,t[1]=(h+P)*A,t[2]=(l-p)*A,t[3]=0,t[4]=(h-P)*E,t[5]=(1-(M+b))*E,t[6]=(d+m)*E,t[7]=0,t[8]=(l+p)*O,t[9]=(d-m)*O,t[10]=(1-(M+v))*O,t[11]=0,t[12]=r[0],t[13]=r[1],t[14]=r[2],t[15]=1,t},n.fromRotationTranslationScaleOrigin=function(t,n,r,a,e){var u=n[0],o=n[1],i=n[2],s=n[3],c=u+u,f=o+o,M=i+i,h=u*c,l=u*f,v=u*M,d=o*f,b=o*M,m=i*M,p=s*c,P=s*f,A=s*M,E=a[0],O=a[1],R=a[2],y=e[0],q=e[1],x=e[2],_=(1-(d+m))*E,Y=(l+A)*E,L=(v-P)*E,S=(l-A)*O,w=(1-(h+m))*O,I=(b+p)*O,N=(v+P)*R,g=(b-p)*R,T=(1-(h+d))*R;return t[0]=_,t[1]=Y,t[2]=L,t[3]=0,t[4]=S,t[5]=w,t[6]=I,t[7]=0,t[8]=N,t[9]=g,t[10]=T,t[11]=0,t[12]=r[0]+y-(_*y+S*q+N*x),t[13]=r[1]+q-(Y*y+w*q+g*x),t[14]=r[2]+x-(L*y+I*q+T*x),t[15]=1,t},n.fromQuat=function(t,n){var r=n[0],a=n[1],e=n[2],u=n[3],o=r+r,i=a+a,s=e+e,c=r*o,f=a*o,M=a*i,h=e*o,l=e*i,v=e*s,d=u*o,b=u*i,m=u*s;return t[0]=1-M-v,t[1]=f+m,t[2]=h-b,t[3]=0,t[4]=f-m,t[5]=1-c-v,t[6]=l+d,t[7]=0,t[8]=h+b,t[9]=l-d,t[10]=1-c-M,t[11]=0,t[12]=0,t[13]=0,t[14]=0,t[15]=1,t},n.frustum=function(t,n,r,a,e,u,o){var i=1/(r-n),s=1/(e-a),c=1/(u-o);return t[0]=2*u*i,t[1]=0,t[2]=0,t[3]=0,t[4]=0,t[5]=2*u*s,t[6]=0,t[7]=0,t[8]=(r+n)*i,t[9]=(e+a)*s,t[10]=(o+u)*c,t[11]=-1,t[12]=0,t[13]=0,t[14]=o*u*2*c,t[15]=0,t},n.perspective=function(t,n,r,a,e){var u=1/Math.tan(n/2),o=void 0;t[0]=u/r,t[1]=0,t[2]=0,t[3]=0,t[4]=0,t[5]=u,t[6]=0,t[7]=0,t[8]=0,t[9]=0,t[11]=-1,t[12]=0,t[13]=0,t[15]=0,null!=e&&e!==1/0?(o=1/(a-e),t[10]=(e+a)*o,t[14]=2*e*a*o):(t[10]=-1,t[14]=-2*a);return t},n.perspectiveFromFieldOfView=function(t,n,r,a){var e=Math.tan(n.upDegrees*Math.PI/180),u=Math.tan(n.downDegrees*Math.PI/180),o=Math.tan(n.leftDegrees*Math.PI/180),i=Math.tan(n.rightDegrees*Math.PI/180),s=2/(o+i),c=2/(e+u);return t[0]=s,t[1]=0,t[2]=0,t[3]=0,t[4]=0,t[5]=c,t[6]=0,t[7]=0,t[8]=-(o-i)*s*.5,t[9]=(e-u)*c*.5,t[10]=a/(r-a),t[11]=-1,t[12]=0,t[13]=0,t[14]=a*r/(r-a),t[15]=0,t},n.ortho=function(t,n,r,a,e,u,o){var i=1/(n-r),s=1/(a-e),c=1/(u-o);return t[0]=-2*i,t[1]=0,t[2]=0,t[3]=0,t[4]=0,t[5]=-2*s,t[6]=0,t[7]=0,t[8]=0,t[9]=0,t[10]=2*c,t[11]=0,t[12]=(n+r)*i,t[13]=(e+a)*s,t[14]=(o+u)*c,t[15]=1,t},n.lookAt=function(t,n,r,u){var o=void 0,i=void 0,s=void 0,c=void 0,f=void 0,M=void 0,h=void 0,l=void 0,v=void 0,d=void 0,b=n[0],m=n[1],p=n[2],P=u[0],A=u[1],E=u[2],O=r[0],R=r[1],y=r[2];if(Math.abs(b-O)0&&(l=1/Math.sqrt(l),f*=l,M*=l,h*=l);var v=s*h-c*M,d=c*f-i*h,b=i*M-s*f;(l=v*v+d*d+b*b)>0&&(l=1/Math.sqrt(l),v*=l,d*=l,b*=l);return t[0]=v,t[1]=d,t[2]=b,t[3]=0,t[4]=M*b-h*d,t[5]=h*v-f*b,t[6]=f*d-M*v,t[7]=0,t[8]=f,t[9]=M,t[10]=h,t[11]=0,t[12]=e,t[13]=u,t[14]=o,t[15]=1,t},n.str=function(t){return"mat4("+t[0]+", "+t[1]+", "+t[2]+", "+t[3]+", "+t[4]+", "+t[5]+", "+t[6]+", "+t[7]+", "+t[8]+", "+t[9]+", "+t[10]+", "+t[11]+", "+t[12]+", "+t[13]+", "+t[14]+", "+t[15]+")"},n.frob=function(t){return Math.sqrt(Math.pow(t[0],2)+Math.pow(t[1],2)+Math.pow(t[2],2)+Math.pow(t[3],2)+Math.pow(t[4],2)+Math.pow(t[5],2)+Math.pow(t[6],2)+Math.pow(t[7],2)+Math.pow(t[8],2)+Math.pow(t[9],2)+Math.pow(t[10],2)+Math.pow(t[11],2)+Math.pow(t[12],2)+Math.pow(t[13],2)+Math.pow(t[14],2)+Math.pow(t[15],2))},n.add=function(t,n,r){return t[0]=n[0]+r[0],t[1]=n[1]+r[1],t[2]=n[2]+r[2],t[3]=n[3]+r[3],t[4]=n[4]+r[4],t[5]=n[5]+r[5],t[6]=n[6]+r[6],t[7]=n[7]+r[7],t[8]=n[8]+r[8],t[9]=n[9]+r[9],t[10]=n[10]+r[10],t[11]=n[11]+r[11],t[12]=n[12]+r[12],t[13]=n[13]+r[13],t[14]=n[14]+r[14],t[15]=n[15]+r[15],t},n.subtract=i,n.multiplyScalar=function(t,n,r){return t[0]=n[0]*r,t[1]=n[1]*r,t[2]=n[2]*r,t[3]=n[3]*r,t[4]=n[4]*r,t[5]=n[5]*r,t[6]=n[6]*r,t[7]=n[7]*r,t[8]=n[8]*r,t[9]=n[9]*r,t[10]=n[10]*r,t[11]=n[11]*r,t[12]=n[12]*r,t[13]=n[13]*r,t[14]=n[14]*r,t[15]=n[15]*r,t},n.multiplyScalarAndAdd=function(t,n,r,a){return t[0]=n[0]+r[0]*a,t[1]=n[1]+r[1]*a,t[2]=n[2]+r[2]*a,t[3]=n[3]+r[3]*a,t[4]=n[4]+r[4]*a,t[5]=n[5]+r[5]*a,t[6]=n[6]+r[6]*a,t[7]=n[7]+r[7]*a,t[8]=n[8]+r[8]*a,t[9]=n[9]+r[9]*a,t[10]=n[10]+r[10]*a,t[11]=n[11]+r[11]*a,t[12]=n[12]+r[12]*a,t[13]=n[13]+r[13]*a,t[14]=n[14]+r[14]*a,t[15]=n[15]+r[15]*a,t},n.exactEquals=function(t,n){return t[0]===n[0]&&t[1]===n[1]&&t[2]===n[2]&&t[3]===n[3]&&t[4]===n[4]&&t[5]===n[5]&&t[6]===n[6]&&t[7]===n[7]&&t[8]===n[8]&&t[9]===n[9]&&t[10]===n[10]&&t[11]===n[11]&&t[12]===n[12]&&t[13]===n[13]&&t[14]===n[14]&&t[15]===n[15]},n.equals=function(t,n){var r=t[0],e=t[1],u=t[2],o=t[3],i=t[4],s=t[5],c=t[6],f=t[7],M=t[8],h=t[9],l=t[10],v=t[11],d=t[12],b=t[13],m=t[14],p=t[15],P=n[0],A=n[1],E=n[2],O=n[3],R=n[4],y=n[5],q=n[6],x=n[7],_=n[8],Y=n[9],L=n[10],S=n[11],w=n[12],I=n[13],N=n[14],g=n[15];return Math.abs(r-P)<=a.EPSILON*Math.max(1,Math.abs(r),Math.abs(P))&&Math.abs(e-A)<=a.EPSILON*Math.max(1,Math.abs(e),Math.abs(A))&&Math.abs(u-E)<=a.EPSILON*Math.max(1,Math.abs(u),Math.abs(E))&&Math.abs(o-O)<=a.EPSILON*Math.max(1,Math.abs(o),Math.abs(O))&&Math.abs(i-R)<=a.EPSILON*Math.max(1,Math.abs(i),Math.abs(R))&&Math.abs(s-y)<=a.EPSILON*Math.max(1,Math.abs(s),Math.abs(y))&&Math.abs(c-q)<=a.EPSILON*Math.max(1,Math.abs(c),Math.abs(q))&&Math.abs(f-x)<=a.EPSILON*Math.max(1,Math.abs(f),Math.abs(x))&&Math.abs(M-_)<=a.EPSILON*Math.max(1,Math.abs(M),Math.abs(_))&&Math.abs(h-Y)<=a.EPSILON*Math.max(1,Math.abs(h),Math.abs(Y))&&Math.abs(l-L)<=a.EPSILON*Math.max(1,Math.abs(l),Math.abs(L))&&Math.abs(v-S)<=a.EPSILON*Math.max(1,Math.abs(v),Math.abs(S))&&Math.abs(d-w)<=a.EPSILON*Math.max(1,Math.abs(d),Math.abs(w))&&Math.abs(b-I)<=a.EPSILON*Math.max(1,Math.abs(b),Math.abs(I))&&Math.abs(m-N)<=a.EPSILON*Math.max(1,Math.abs(m),Math.abs(N))&&Math.abs(p-g)<=a.EPSILON*Math.max(1,Math.abs(p),Math.abs(g))};var a=function(t){if(t&&t.__esModule)return t;var n={};if(null!=t)for(var r in t)Object.prototype.hasOwnProperty.call(t,r)&&(n[r]=t[r]);return n.default=t,n}(r(0));function e(t){return t[0]=1,t[1]=0,t[2]=0,t[3]=0,t[4]=0,t[5]=1,t[6]=0,t[7]=0,t[8]=0,t[9]=0,t[10]=1,t[11]=0,t[12]=0,t[13]=0,t[14]=0,t[15]=1,t}function u(t,n,r){var a=n[0],e=n[1],u=n[2],o=n[3],i=n[4],s=n[5],c=n[6],f=n[7],M=n[8],h=n[9],l=n[10],v=n[11],d=n[12],b=n[13],m=n[14],p=n[15],P=r[0],A=r[1],E=r[2],O=r[3];return t[0]=P*a+A*i+E*M+O*d,t[1]=P*e+A*s+E*h+O*b,t[2]=P*u+A*c+E*l+O*m,t[3]=P*o+A*f+E*v+O*p,P=r[4],A=r[5],E=r[6],O=r[7],t[4]=P*a+A*i+E*M+O*d,t[5]=P*e+A*s+E*h+O*b,t[6]=P*u+A*c+E*l+O*m,t[7]=P*o+A*f+E*v+O*p,P=r[8],A=r[9],E=r[10],O=r[11],t[8]=P*a+A*i+E*M+O*d,t[9]=P*e+A*s+E*h+O*b,t[10]=P*u+A*c+E*l+O*m,t[11]=P*o+A*f+E*v+O*p,P=r[12],A=r[13],E=r[14],O=r[15],t[12]=P*a+A*i+E*M+O*d,t[13]=P*e+A*s+E*h+O*b,t[14]=P*u+A*c+E*l+O*m,t[15]=P*o+A*f+E*v+O*p,t}function o(t,n,r){var a=n[0],e=n[1],u=n[2],o=n[3],i=a+a,s=e+e,c=u+u,f=a*i,M=a*s,h=a*c,l=e*s,v=e*c,d=u*c,b=o*i,m=o*s,p=o*c;return t[0]=1-(l+d),t[1]=M+p,t[2]=h-m,t[3]=0,t[4]=M-p,t[5]=1-(f+d),t[6]=v+b,t[7]=0,t[8]=h+m,t[9]=v-b,t[10]=1-(f+l),t[11]=0,t[12]=r[0],t[13]=r[1],t[14]=r[2],t[15]=1,t}function i(t,n,r){return t[0]=n[0]-r[0],t[1]=n[1]-r[1],t[2]=n[2]-r[2],t[3]=n[3]-r[3],t[4]=n[4]-r[4],t[5]=n[5]-r[5],t[6]=n[6]-r[6],t[7]=n[7]-r[7],t[8]=n[8]-r[8],t[9]=n[9]-r[9],t[10]=n[10]-r[10],t[11]=n[11]-r[11],t[12]=n[12]-r[12],t[13]=n[13]-r[13],t[14]=n[14]-r[14],t[15]=n[15]-r[15],t}n.mul=u,n.sub=i},function(t,n,r){"use strict";Object.defineProperty(n,"__esModule",{value:!0}),n.sub=n.mul=void 0,n.create=function(){var t=new a.ARRAY_TYPE(9);a.ARRAY_TYPE!=Float32Array&&(t[1]=0,t[2]=0,t[3]=0,t[5]=0,t[6]=0,t[7]=0);return t[0]=1,t[4]=1,t[8]=1,t},n.fromMat4=function(t,n){return t[0]=n[0],t[1]=n[1],t[2]=n[2],t[3]=n[4],t[4]=n[5],t[5]=n[6],t[6]=n[8],t[7]=n[9],t[8]=n[10],t},n.clone=function(t){var n=new a.ARRAY_TYPE(9);return n[0]=t[0],n[1]=t[1],n[2]=t[2],n[3]=t[3],n[4]=t[4],n[5]=t[5],n[6]=t[6],n[7]=t[7],n[8]=t[8],n},n.copy=function(t,n){return t[0]=n[0],t[1]=n[1],t[2]=n[2],t[3]=n[3],t[4]=n[4],t[5]=n[5],t[6]=n[6],t[7]=n[7],t[8]=n[8],t},n.fromValues=function(t,n,r,e,u,o,i,s,c){var f=new a.ARRAY_TYPE(9);return f[0]=t,f[1]=n,f[2]=r,f[3]=e,f[4]=u,f[5]=o,f[6]=i,f[7]=s,f[8]=c,f},n.set=function(t,n,r,a,e,u,o,i,s,c){return t[0]=n,t[1]=r,t[2]=a,t[3]=e,t[4]=u,t[5]=o,t[6]=i,t[7]=s,t[8]=c,t},n.identity=function(t){return t[0]=1,t[1]=0,t[2]=0,t[3]=0,t[4]=1,t[5]=0,t[6]=0,t[7]=0,t[8]=1,t},n.transpose=function(t,n){if(t===n){var r=n[1],a=n[2],e=n[5];t[1]=n[3],t[2]=n[6],t[3]=r,t[5]=n[7],t[6]=a,t[7]=e}else t[0]=n[0],t[1]=n[3],t[2]=n[6],t[3]=n[1],t[4]=n[4],t[5]=n[7],t[6]=n[2],t[7]=n[5],t[8]=n[8];return t},n.invert=function(t,n){var r=n[0],a=n[1],e=n[2],u=n[3],o=n[4],i=n[5],s=n[6],c=n[7],f=n[8],M=f*o-i*c,h=-f*u+i*s,l=c*u-o*s,v=r*M+a*h+e*l;if(!v)return null;return v=1/v,t[0]=M*v,t[1]=(-f*a+e*c)*v,t[2]=(i*a-e*o)*v,t[3]=h*v,t[4]=(f*r-e*s)*v,t[5]=(-i*r+e*u)*v,t[6]=l*v,t[7]=(-c*r+a*s)*v,t[8]=(o*r-a*u)*v,t},n.adjoint=function(t,n){var r=n[0],a=n[1],e=n[2],u=n[3],o=n[4],i=n[5],s=n[6],c=n[7],f=n[8];return t[0]=o*f-i*c,t[1]=e*c-a*f,t[2]=a*i-e*o,t[3]=i*s-u*f,t[4]=r*f-e*s,t[5]=e*u-r*i,t[6]=u*c-o*s,t[7]=a*s-r*c,t[8]=r*o-a*u,t},n.determinant=function(t){var n=t[0],r=t[1],a=t[2],e=t[3],u=t[4],o=t[5],i=t[6],s=t[7],c=t[8];return n*(c*u-o*s)+r*(-c*e+o*i)+a*(s*e-u*i)},n.multiply=e,n.translate=function(t,n,r){var a=n[0],e=n[1],u=n[2],o=n[3],i=n[4],s=n[5],c=n[6],f=n[7],M=n[8],h=r[0],l=r[1];return t[0]=a,t[1]=e,t[2]=u,t[3]=o,t[4]=i,t[5]=s,t[6]=h*a+l*o+c,t[7]=h*e+l*i+f,t[8]=h*u+l*s+M,t},n.rotate=function(t,n,r){var a=n[0],e=n[1],u=n[2],o=n[3],i=n[4],s=n[5],c=n[6],f=n[7],M=n[8],h=Math.sin(r),l=Math.cos(r);return t[0]=l*a+h*o,t[1]=l*e+h*i,t[2]=l*u+h*s,t[3]=l*o-h*a,t[4]=l*i-h*e,t[5]=l*s-h*u,t[6]=c,t[7]=f,t[8]=M,t},n.scale=function(t,n,r){var a=r[0],e=r[1];return t[0]=a*n[0],t[1]=a*n[1],t[2]=a*n[2],t[3]=e*n[3],t[4]=e*n[4],t[5]=e*n[5],t[6]=n[6],t[7]=n[7],t[8]=n[8],t},n.fromTranslation=function(t,n){return t[0]=1,t[1]=0,t[2]=0,t[3]=0,t[4]=1,t[5]=0,t[6]=n[0],t[7]=n[1],t[8]=1,t},n.fromRotation=function(t,n){var r=Math.sin(n),a=Math.cos(n);return t[0]=a,t[1]=r,t[2]=0,t[3]=-r,t[4]=a,t[5]=0,t[6]=0,t[7]=0,t[8]=1,t},n.fromScaling=function(t,n){return t[0]=n[0],t[1]=0,t[2]=0,t[3]=0,t[4]=n[1],t[5]=0,t[6]=0,t[7]=0,t[8]=1,t},n.fromMat2d=function(t,n){return t[0]=n[0],t[1]=n[1],t[2]=0,t[3]=n[2],t[4]=n[3],t[5]=0,t[6]=n[4],t[7]=n[5],t[8]=1,t},n.fromQuat=function(t,n){var r=n[0],a=n[1],e=n[2],u=n[3],o=r+r,i=a+a,s=e+e,c=r*o,f=a*o,M=a*i,h=e*o,l=e*i,v=e*s,d=u*o,b=u*i,m=u*s;return t[0]=1-M-v,t[3]=f-m,t[6]=h+b,t[1]=f+m,t[4]=1-c-v,t[7]=l-d,t[2]=h-b,t[5]=l+d,t[8]=1-c-M,t},n.normalFromMat4=function(t,n){var r=n[0],a=n[1],e=n[2],u=n[3],o=n[4],i=n[5],s=n[6],c=n[7],f=n[8],M=n[9],h=n[10],l=n[11],v=n[12],d=n[13],b=n[14],m=n[15],p=r*i-a*o,P=r*s-e*o,A=r*c-u*o,E=a*s-e*i,O=a*c-u*i,R=e*c-u*s,y=f*d-M*v,q=f*b-h*v,x=f*m-l*v,_=M*b-h*d,Y=M*m-l*d,L=h*m-l*b,S=p*L-P*Y+A*_+E*x-O*q+R*y;if(!S)return null;return S=1/S,t[0]=(i*L-s*Y+c*_)*S,t[1]=(s*x-o*L-c*q)*S,t[2]=(o*Y-i*x+c*y)*S,t[3]=(e*Y-a*L-u*_)*S,t[4]=(r*L-e*x+u*q)*S,t[5]=(a*x-r*Y-u*y)*S,t[6]=(d*R-b*O+m*E)*S,t[7]=(b*A-v*R-m*P)*S,t[8]=(v*O-d*A+m*p)*S,t},n.projection=function(t,n,r){return t[0]=2/n,t[1]=0,t[2]=0,t[3]=0,t[4]=-2/r,t[5]=0,t[6]=-1,t[7]=1,t[8]=1,t},n.str=function(t){return"mat3("+t[0]+", "+t[1]+", "+t[2]+", "+t[3]+", "+t[4]+", "+t[5]+", "+t[6]+", "+t[7]+", "+t[8]+")"},n.frob=function(t){return Math.sqrt(Math.pow(t[0],2)+Math.pow(t[1],2)+Math.pow(t[2],2)+Math.pow(t[3],2)+Math.pow(t[4],2)+Math.pow(t[5],2)+Math.pow(t[6],2)+Math.pow(t[7],2)+Math.pow(t[8],2))},n.add=function(t,n,r){return t[0]=n[0]+r[0],t[1]=n[1]+r[1],t[2]=n[2]+r[2],t[3]=n[3]+r[3],t[4]=n[4]+r[4],t[5]=n[5]+r[5],t[6]=n[6]+r[6],t[7]=n[7]+r[7],t[8]=n[8]+r[8],t},n.subtract=u,n.multiplyScalar=function(t,n,r){return t[0]=n[0]*r,t[1]=n[1]*r,t[2]=n[2]*r,t[3]=n[3]*r,t[4]=n[4]*r,t[5]=n[5]*r,t[6]=n[6]*r,t[7]=n[7]*r,t[8]=n[8]*r,t},n.multiplyScalarAndAdd=function(t,n,r,a){return t[0]=n[0]+r[0]*a,t[1]=n[1]+r[1]*a,t[2]=n[2]+r[2]*a,t[3]=n[3]+r[3]*a,t[4]=n[4]+r[4]*a,t[5]=n[5]+r[5]*a,t[6]=n[6]+r[6]*a,t[7]=n[7]+r[7]*a,t[8]=n[8]+r[8]*a,t},n.exactEquals=function(t,n){return t[0]===n[0]&&t[1]===n[1]&&t[2]===n[2]&&t[3]===n[3]&&t[4]===n[4]&&t[5]===n[5]&&t[6]===n[6]&&t[7]===n[7]&&t[8]===n[8]},n.equals=function(t,n){var r=t[0],e=t[1],u=t[2],o=t[3],i=t[4],s=t[5],c=t[6],f=t[7],M=t[8],h=n[0],l=n[1],v=n[2],d=n[3],b=n[4],m=n[5],p=n[6],P=n[7],A=n[8];return Math.abs(r-h)<=a.EPSILON*Math.max(1,Math.abs(r),Math.abs(h))&&Math.abs(e-l)<=a.EPSILON*Math.max(1,Math.abs(e),Math.abs(l))&&Math.abs(u-v)<=a.EPSILON*Math.max(1,Math.abs(u),Math.abs(v))&&Math.abs(o-d)<=a.EPSILON*Math.max(1,Math.abs(o),Math.abs(d))&&Math.abs(i-b)<=a.EPSILON*Math.max(1,Math.abs(i),Math.abs(b))&&Math.abs(s-m)<=a.EPSILON*Math.max(1,Math.abs(s),Math.abs(m))&&Math.abs(c-p)<=a.EPSILON*Math.max(1,Math.abs(c),Math.abs(p))&&Math.abs(f-P)<=a.EPSILON*Math.max(1,Math.abs(f),Math.abs(P))&&Math.abs(M-A)<=a.EPSILON*Math.max(1,Math.abs(M),Math.abs(A))};var a=function(t){if(t&&t.__esModule)return t;var n={};if(null!=t)for(var r in t)Object.prototype.hasOwnProperty.call(t,r)&&(n[r]=t[r]);return n.default=t,n}(r(0));function e(t,n,r){var a=n[0],e=n[1],u=n[2],o=n[3],i=n[4],s=n[5],c=n[6],f=n[7],M=n[8],h=r[0],l=r[1],v=r[2],d=r[3],b=r[4],m=r[5],p=r[6],P=r[7],A=r[8];return t[0]=h*a+l*o+v*c,t[1]=h*e+l*i+v*f,t[2]=h*u+l*s+v*M,t[3]=d*a+b*o+m*c,t[4]=d*e+b*i+m*f,t[5]=d*u+b*s+m*M,t[6]=p*a+P*o+A*c,t[7]=p*e+P*i+A*f,t[8]=p*u+P*s+A*M,t}function u(t,n,r){return t[0]=n[0]-r[0],t[1]=n[1]-r[1],t[2]=n[2]-r[2],t[3]=n[3]-r[3],t[4]=n[4]-r[4],t[5]=n[5]-r[5],t[6]=n[6]-r[6],t[7]=n[7]-r[7],t[8]=n[8]-r[8],t}n.mul=e,n.sub=u},function(t,n,r){"use strict";Object.defineProperty(n,"__esModule",{value:!0}),n.forEach=n.sqrLen=n.sqrDist=n.dist=n.div=n.mul=n.sub=n.len=void 0,n.create=e,n.clone=function(t){var n=new a.ARRAY_TYPE(2);return n[0]=t[0],n[1]=t[1],n},n.fromValues=function(t,n){var r=new a.ARRAY_TYPE(2);return r[0]=t,r[1]=n,r},n.copy=function(t,n){return t[0]=n[0],t[1]=n[1],t},n.set=function(t,n,r){return t[0]=n,t[1]=r,t},n.add=function(t,n,r){return t[0]=n[0]+r[0],t[1]=n[1]+r[1],t},n.subtract=u,n.multiply=o,n.divide=i,n.ceil=function(t,n){return t[0]=Math.ceil(n[0]),t[1]=Math.ceil(n[1]),t},n.floor=function(t,n){return t[0]=Math.floor(n[0]),t[1]=Math.floor(n[1]),t},n.min=function(t,n,r){return t[0]=Math.min(n[0],r[0]),t[1]=Math.min(n[1],r[1]),t},n.max=function(t,n,r){return t[0]=Math.max(n[0],r[0]),t[1]=Math.max(n[1],r[1]),t},n.round=function(t,n){return t[0]=Math.round(n[0]),t[1]=Math.round(n[1]),t},n.scale=function(t,n,r){return t[0]=n[0]*r,t[1]=n[1]*r,t},n.scaleAndAdd=function(t,n,r,a){return t[0]=n[0]+r[0]*a,t[1]=n[1]+r[1]*a,t},n.distance=s,n.squaredDistance=c,n.length=f,n.squaredLength=M,n.negate=function(t,n){return t[0]=-n[0],t[1]=-n[1],t},n.inverse=function(t,n){return t[0]=1/n[0],t[1]=1/n[1],t},n.normalize=function(t,n){var r=n[0],a=n[1],e=r*r+a*a;e>0&&(e=1/Math.sqrt(e),t[0]=n[0]*e,t[1]=n[1]*e);return t},n.dot=function(t,n){return t[0]*n[0]+t[1]*n[1]},n.cross=function(t,n,r){var a=n[0]*r[1]-n[1]*r[0];return t[0]=t[1]=0,t[2]=a,t},n.lerp=function(t,n,r,a){var e=n[0],u=n[1];return t[0]=e+a*(r[0]-e),t[1]=u+a*(r[1]-u),t},n.random=function(t,n){n=n||1;var r=2*a.RANDOM()*Math.PI;return t[0]=Math.cos(r)*n,t[1]=Math.sin(r)*n,t},n.transformMat2=function(t,n,r){var a=n[0],e=n[1];return t[0]=r[0]*a+r[2]*e,t[1]=r[1]*a+r[3]*e,t},n.transformMat2d=function(t,n,r){var a=n[0],e=n[1];return t[0]=r[0]*a+r[2]*e+r[4],t[1]=r[1]*a+r[3]*e+r[5],t},n.transformMat3=function(t,n,r){var a=n[0],e=n[1];return t[0]=r[0]*a+r[3]*e+r[6],t[1]=r[1]*a+r[4]*e+r[7],t},n.transformMat4=function(t,n,r){var a=n[0],e=n[1];return t[0]=r[0]*a+r[4]*e+r[12],t[1]=r[1]*a+r[5]*e+r[13],t},n.rotate=function(t,n,r,a){var e=n[0]-r[0],u=n[1]-r[1],o=Math.sin(a),i=Math.cos(a);return t[0]=e*i-u*o+r[0],t[1]=e*o+u*i+r[1],t},n.angle=function(t,n){var r=t[0],a=t[1],e=n[0],u=n[1],o=r*r+a*a;o>0&&(o=1/Math.sqrt(o));var i=e*e+u*u;i>0&&(i=1/Math.sqrt(i));var s=(r*e+a*u)*o*i;return s>1?0:s<-1?Math.PI:Math.acos(s)},n.str=function(t){return"vec2("+t[0]+", "+t[1]+")"},n.exactEquals=function(t,n){return t[0]===n[0]&&t[1]===n[1]},n.equals=function(t,n){var r=t[0],e=t[1],u=n[0],o=n[1];return Math.abs(r-u)<=a.EPSILON*Math.max(1,Math.abs(r),Math.abs(u))&&Math.abs(e-o)<=a.EPSILON*Math.max(1,Math.abs(e),Math.abs(o))};var a=function(t){if(t&&t.__esModule)return t;var n={};if(null!=t)for(var r in t)Object.prototype.hasOwnProperty.call(t,r)&&(n[r]=t[r]);return n.default=t,n}(r(0));function e(){var t=new a.ARRAY_TYPE(2);return a.ARRAY_TYPE!=Float32Array&&(t[0]=0,t[1]=0),t}function u(t,n,r){return t[0]=n[0]-r[0],t[1]=n[1]-r[1],t}function o(t,n,r){return t[0]=n[0]*r[0],t[1]=n[1]*r[1],t}function i(t,n,r){return t[0]=n[0]/r[0],t[1]=n[1]/r[1],t}function s(t,n){var r=n[0]-t[0],a=n[1]-t[1];return Math.sqrt(r*r+a*a)}function c(t,n){var r=n[0]-t[0],a=n[1]-t[1];return r*r+a*a}function f(t){var n=t[0],r=t[1];return Math.sqrt(n*n+r*r)}function M(t){var n=t[0],r=t[1];return n*n+r*r}n.len=f,n.sub=u,n.mul=o,n.div=i,n.dist=s,n.sqrDist=c,n.sqrLen=M,n.forEach=function(){var t=e();return function(n,r,a,e,u,o){var i=void 0,s=void 0;for(r||(r=2),a||(a=0),s=e?Math.min(e*r+a,n.length):n.length,i=a;i0){r=Math.sqrt(r);var a=n[0]/r,e=n[1]/r,u=n[2]/r,o=n[3]/r,i=n[4],s=n[5],c=n[6],f=n[7],M=a*i+e*s+u*c+o*f;t[0]=a,t[1]=e,t[2]=u,t[3]=o,t[4]=(i-a*M)/r,t[5]=(s-e*M)/r,t[6]=(c-u*M)/r,t[7]=(f-o*M)/r}return t},n.str=function(t){return"quat2("+t[0]+", "+t[1]+", "+t[2]+", "+t[3]+", "+t[4]+", "+t[5]+", "+t[6]+", "+t[7]+")"},n.exactEquals=function(t,n){return t[0]===n[0]&&t[1]===n[1]&&t[2]===n[2]&&t[3]===n[3]&&t[4]===n[4]&&t[5]===n[5]&&t[6]===n[6]&&t[7]===n[7]},n.equals=function(t,n){var r=t[0],e=t[1],u=t[2],o=t[3],i=t[4],s=t[5],c=t[6],f=t[7],M=n[0],h=n[1],l=n[2],v=n[3],d=n[4],b=n[5],m=n[6],p=n[7];return Math.abs(r-M)<=a.EPSILON*Math.max(1,Math.abs(r),Math.abs(M))&&Math.abs(e-h)<=a.EPSILON*Math.max(1,Math.abs(e),Math.abs(h))&&Math.abs(u-l)<=a.EPSILON*Math.max(1,Math.abs(u),Math.abs(l))&&Math.abs(o-v)<=a.EPSILON*Math.max(1,Math.abs(o),Math.abs(v))&&Math.abs(i-d)<=a.EPSILON*Math.max(1,Math.abs(i),Math.abs(d))&&Math.abs(s-b)<=a.EPSILON*Math.max(1,Math.abs(s),Math.abs(b))&&Math.abs(c-m)<=a.EPSILON*Math.max(1,Math.abs(c),Math.abs(m))&&Math.abs(f-p)<=a.EPSILON*Math.max(1,Math.abs(f),Math.abs(p))};var a=o(r(0)),e=o(r(3)),u=o(r(4));function o(t){if(t&&t.__esModule)return t;var n={};if(null!=t)for(var r in t)Object.prototype.hasOwnProperty.call(t,r)&&(n[r]=t[r]);return n.default=t,n}function i(t,n,r){var a=.5*r[0],e=.5*r[1],u=.5*r[2],o=n[0],i=n[1],s=n[2],c=n[3];return t[0]=o,t[1]=i,t[2]=s,t[3]=c,t[4]=a*c+e*s-u*i,t[5]=e*c+u*o-a*s,t[6]=u*c+a*i-e*o,t[7]=-a*o-e*i-u*s,t}function s(t,n){return t[0]=n[0],t[1]=n[1],t[2]=n[2],t[3]=n[3],t[4]=n[4],t[5]=n[5],t[6]=n[6],t[7]=n[7],t}n.getReal=e.copy;n.setReal=e.copy;function c(t,n,r){var a=n[0],e=n[1],u=n[2],o=n[3],i=r[4],s=r[5],c=r[6],f=r[7],M=n[4],h=n[5],l=n[6],v=n[7],d=r[0],b=r[1],m=r[2],p=r[3];return t[0]=a*p+o*d+e*m-u*b,t[1]=e*p+o*b+u*d-a*m,t[2]=u*p+o*m+a*b-e*d,t[3]=o*p-a*d-e*b-u*m,t[4]=a*f+o*i+e*c-u*s+M*p+v*d+h*m-l*b,t[5]=e*f+o*s+u*i-a*c+h*p+v*b+l*d-M*m,t[6]=u*f+o*c+a*s-e*i+l*p+v*m+M*b-h*d,t[7]=o*f-a*i-e*s-u*c+v*p-M*d-h*b-l*m,t}n.mul=c;var f=n.dot=e.dot;var M=n.length=e.length,h=(n.len=M,n.squaredLength=e.squaredLength);n.sqrLen=h},function(t,n,r){"use strict";Object.defineProperty(n,"__esModule",{value:!0}),n.sub=n.mul=void 0,n.create=function(){var t=new a.ARRAY_TYPE(6);a.ARRAY_TYPE!=Float32Array&&(t[1]=0,t[2]=0,t[4]=0,t[5]=0);return t[0]=1,t[3]=1,t},n.clone=function(t){var n=new a.ARRAY_TYPE(6);return n[0]=t[0],n[1]=t[1],n[2]=t[2],n[3]=t[3],n[4]=t[4],n[5]=t[5],n},n.copy=function(t,n){return t[0]=n[0],t[1]=n[1],t[2]=n[2],t[3]=n[3],t[4]=n[4],t[5]=n[5],t},n.identity=function(t){return t[0]=1,t[1]=0,t[2]=0,t[3]=1,t[4]=0,t[5]=0,t},n.fromValues=function(t,n,r,e,u,o){var i=new a.ARRAY_TYPE(6);return i[0]=t,i[1]=n,i[2]=r,i[3]=e,i[4]=u,i[5]=o,i},n.set=function(t,n,r,a,e,u,o){return t[0]=n,t[1]=r,t[2]=a,t[3]=e,t[4]=u,t[5]=o,t},n.invert=function(t,n){var r=n[0],a=n[1],e=n[2],u=n[3],o=n[4],i=n[5],s=r*u-a*e;if(!s)return null;return s=1/s,t[0]=u*s,t[1]=-a*s,t[2]=-e*s,t[3]=r*s,t[4]=(e*i-u*o)*s,t[5]=(a*o-r*i)*s,t},n.determinant=function(t){return t[0]*t[3]-t[1]*t[2]},n.multiply=e,n.rotate=function(t,n,r){var a=n[0],e=n[1],u=n[2],o=n[3],i=n[4],s=n[5],c=Math.sin(r),f=Math.cos(r);return t[0]=a*f+u*c,t[1]=e*f+o*c,t[2]=a*-c+u*f,t[3]=e*-c+o*f,t[4]=i,t[5]=s,t},n.scale=function(t,n,r){var a=n[0],e=n[1],u=n[2],o=n[3],i=n[4],s=n[5],c=r[0],f=r[1];return t[0]=a*c,t[1]=e*c,t[2]=u*f,t[3]=o*f,t[4]=i,t[5]=s,t},n.translate=function(t,n,r){var a=n[0],e=n[1],u=n[2],o=n[3],i=n[4],s=n[5],c=r[0],f=r[1];return t[0]=a,t[1]=e,t[2]=u,t[3]=o,t[4]=a*c+u*f+i,t[5]=e*c+o*f+s,t},n.fromRotation=function(t,n){var r=Math.sin(n),a=Math.cos(n);return t[0]=a,t[1]=r,t[2]=-r,t[3]=a,t[4]=0,t[5]=0,t},n.fromScaling=function(t,n){return t[0]=n[0],t[1]=0,t[2]=0,t[3]=n[1],t[4]=0,t[5]=0,t},n.fromTranslation=function(t,n){return t[0]=1,t[1]=0,t[2]=0,t[3]=1,t[4]=n[0],t[5]=n[1],t},n.str=function(t){return"mat2d("+t[0]+", "+t[1]+", "+t[2]+", "+t[3]+", "+t[4]+", "+t[5]+")"},n.frob=function(t){return Math.sqrt(Math.pow(t[0],2)+Math.pow(t[1],2)+Math.pow(t[2],2)+Math.pow(t[3],2)+Math.pow(t[4],2)+Math.pow(t[5],2)+1)},n.add=function(t,n,r){return t[0]=n[0]+r[0],t[1]=n[1]+r[1],t[2]=n[2]+r[2],t[3]=n[3]+r[3],t[4]=n[4]+r[4],t[5]=n[5]+r[5],t},n.subtract=u,n.multiplyScalar=function(t,n,r){return t[0]=n[0]*r,t[1]=n[1]*r,t[2]=n[2]*r,t[3]=n[3]*r,t[4]=n[4]*r,t[5]=n[5]*r,t},n.multiplyScalarAndAdd=function(t,n,r,a){return t[0]=n[0]+r[0]*a,t[1]=n[1]+r[1]*a,t[2]=n[2]+r[2]*a,t[3]=n[3]+r[3]*a,t[4]=n[4]+r[4]*a,t[5]=n[5]+r[5]*a,t},n.exactEquals=function(t,n){return t[0]===n[0]&&t[1]===n[1]&&t[2]===n[2]&&t[3]===n[3]&&t[4]===n[4]&&t[5]===n[5]},n.equals=function(t,n){var r=t[0],e=t[1],u=t[2],o=t[3],i=t[4],s=t[5],c=n[0],f=n[1],M=n[2],h=n[3],l=n[4],v=n[5];return Math.abs(r-c)<=a.EPSILON*Math.max(1,Math.abs(r),Math.abs(c))&&Math.abs(e-f)<=a.EPSILON*Math.max(1,Math.abs(e),Math.abs(f))&&Math.abs(u-M)<=a.EPSILON*Math.max(1,Math.abs(u),Math.abs(M))&&Math.abs(o-h)<=a.EPSILON*Math.max(1,Math.abs(o),Math.abs(h))&&Math.abs(i-l)<=a.EPSILON*Math.max(1,Math.abs(i),Math.abs(l))&&Math.abs(s-v)<=a.EPSILON*Math.max(1,Math.abs(s),Math.abs(v))};var a=function(t){if(t&&t.__esModule)return t;var n={};if(null!=t)for(var r in t)Object.prototype.hasOwnProperty.call(t,r)&&(n[r]=t[r]);return n.default=t,n}(r(0));function e(t,n,r){var a=n[0],e=n[1],u=n[2],o=n[3],i=n[4],s=n[5],c=r[0],f=r[1],M=r[2],h=r[3],l=r[4],v=r[5];return t[0]=a*c+u*f,t[1]=e*c+o*f,t[2]=a*M+u*h,t[3]=e*M+o*h,t[4]=a*l+u*v+i,t[5]=e*l+o*v+s,t}function u(t,n,r){return t[0]=n[0]-r[0],t[1]=n[1]-r[1],t[2]=n[2]-r[2],t[3]=n[3]-r[3],t[4]=n[4]-r[4],t[5]=n[5]-r[5],t}n.mul=e,n.sub=u},function(t,n,r){"use strict";Object.defineProperty(n,"__esModule",{value:!0}),n.sub=n.mul=void 0,n.create=function(){var t=new a.ARRAY_TYPE(4);a.ARRAY_TYPE!=Float32Array&&(t[1]=0,t[2]=0);return t[0]=1,t[3]=1,t},n.clone=function(t){var n=new a.ARRAY_TYPE(4);return n[0]=t[0],n[1]=t[1],n[2]=t[2],n[3]=t[3],n},n.copy=function(t,n){return t[0]=n[0],t[1]=n[1],t[2]=n[2],t[3]=n[3],t},n.identity=function(t){return t[0]=1,t[1]=0,t[2]=0,t[3]=1,t},n.fromValues=function(t,n,r,e){var u=new a.ARRAY_TYPE(4);return u[0]=t,u[1]=n,u[2]=r,u[3]=e,u},n.set=function(t,n,r,a,e){return t[0]=n,t[1]=r,t[2]=a,t[3]=e,t},n.transpose=function(t,n){if(t===n){var r=n[1];t[1]=n[2],t[2]=r}else t[0]=n[0],t[1]=n[2],t[2]=n[1],t[3]=n[3];return t},n.invert=function(t,n){var r=n[0],a=n[1],e=n[2],u=n[3],o=r*u-e*a;if(!o)return null;return o=1/o,t[0]=u*o,t[1]=-a*o,t[2]=-e*o,t[3]=r*o,t},n.adjoint=function(t,n){var r=n[0];return t[0]=n[3],t[1]=-n[1],t[2]=-n[2],t[3]=r,t},n.determinant=function(t){return t[0]*t[3]-t[2]*t[1]},n.multiply=e,n.rotate=function(t,n,r){var a=n[0],e=n[1],u=n[2],o=n[3],i=Math.sin(r),s=Math.cos(r);return t[0]=a*s+u*i,t[1]=e*s+o*i,t[2]=a*-i+u*s,t[3]=e*-i+o*s,t},n.scale=function(t,n,r){var a=n[0],e=n[1],u=n[2],o=n[3],i=r[0],s=r[1];return t[0]=a*i,t[1]=e*i,t[2]=u*s,t[3]=o*s,t},n.fromRotation=function(t,n){var r=Math.sin(n),a=Math.cos(n);return t[0]=a,t[1]=r,t[2]=-r,t[3]=a,t},n.fromScaling=function(t,n){return t[0]=n[0],t[1]=0,t[2]=0,t[3]=n[1],t},n.str=function(t){return"mat2("+t[0]+", "+t[1]+", "+t[2]+", "+t[3]+")"},n.frob=function(t){return Math.sqrt(Math.pow(t[0],2)+Math.pow(t[1],2)+Math.pow(t[2],2)+Math.pow(t[3],2))},n.LDU=function(t,n,r,a){return t[2]=a[2]/a[0],r[0]=a[0],r[1]=a[1],r[3]=a[3]-t[2]*r[1],[t,n,r]},n.add=function(t,n,r){return t[0]=n[0]+r[0],t[1]=n[1]+r[1],t[2]=n[2]+r[2],t[3]=n[3]+r[3],t},n.subtract=u,n.exactEquals=function(t,n){return t[0]===n[0]&&t[1]===n[1]&&t[2]===n[2]&&t[3]===n[3]},n.equals=function(t,n){var r=t[0],e=t[1],u=t[2],o=t[3],i=n[0],s=n[1],c=n[2],f=n[3];return Math.abs(r-i)<=a.EPSILON*Math.max(1,Math.abs(r),Math.abs(i))&&Math.abs(e-s)<=a.EPSILON*Math.max(1,Math.abs(e),Math.abs(s))&&Math.abs(u-c)<=a.EPSILON*Math.max(1,Math.abs(u),Math.abs(c))&&Math.abs(o-f)<=a.EPSILON*Math.max(1,Math.abs(o),Math.abs(f))},n.multiplyScalar=function(t,n,r){return t[0]=n[0]*r,t[1]=n[1]*r,t[2]=n[2]*r,t[3]=n[3]*r,t},n.multiplyScalarAndAdd=function(t,n,r,a){return t[0]=n[0]+r[0]*a,t[1]=n[1]+r[1]*a,t[2]=n[2]+r[2]*a,t[3]=n[3]+r[3]*a,t};var a=function(t){if(t&&t.__esModule)return t;var n={};if(null!=t)for(var r in t)Object.prototype.hasOwnProperty.call(t,r)&&(n[r]=t[r]);return n.default=t,n}(r(0));function e(t,n,r){var a=n[0],e=n[1],u=n[2],o=n[3],i=r[0],s=r[1],c=r[2],f=r[3];return t[0]=a*i+u*s,t[1]=e*i+o*s,t[2]=a*c+u*f,t[3]=e*c+o*f,t}function u(t,n,r){return t[0]=n[0]-r[0],t[1]=n[1]-r[1],t[2]=n[2]-r[2],t[3]=n[3]-r[3],t}n.mul=e,n.sub=u},function(t,n,r){"use strict";Object.defineProperty(n,"__esModule",{value:!0}),n.vec4=n.vec3=n.vec2=n.quat2=n.quat=n.mat4=n.mat3=n.mat2d=n.mat2=n.glMatrix=void 0;var a=l(r(0)),e=l(r(9)),u=l(r(8)),o=l(r(5)),i=l(r(4)),s=l(r(3)),c=l(r(7)),f=l(r(6)),M=l(r(2)),h=l(r(1));function l(t){if(t&&t.__esModule)return t;var n={};if(null!=t)for(var r in t)Object.prototype.hasOwnProperty.call(t,r)&&(n[r]=t[r]);return n.default=t,n}n.glMatrix=a,n.mat2=e,n.mat2d=u,n.mat3=o,n.mat4=i,n.quat=s,n.quat2=c,n.vec2=f,n.vec3=M,n.vec4=h}])}); \ No newline at end of file diff --git a/editor/js/libs/litegl.js b/editor/js/libs/litegl.js deleted file mode 100755 index f9ae7f822..000000000 --- a/editor/js/libs/litegl.js +++ /dev/null @@ -1,13432 +0,0 @@ -//packer version -//litegl.js by Javi Agenjo 2014 @tamat (tamats.com) -//forked from lightgl.js by Evan Wallace (madebyevan.com) -"use strict"; - -(function(global){ - -var GL = global.GL = {}; - -if(typeof(glMatrix) == "undefined") - throw("litegl.js requires gl-matrix to work. It must be included before litegl."); -else -{ - if(!global.vec2) - throw("litegl.js does not support gl-matrix 3.0, download 2.8 https://github.com/toji/gl-matrix/releases/tag/v2.8.1"); -} - -//polyfill -global.requestAnimationFrame = global.requestAnimationFrame || global.mozRequestAnimationFrame || global.webkitRequestAnimationFrame || function(callback) { setTimeout(callback, 1000 / 60); }; - -GL.blockable_keys = {"Up":true,"Down":true,"Left":true,"Right":true}; - -GL.reverse = null; - -//some consts -//https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/button -GL.LEFT_MOUSE_BUTTON = 0; -GL.MIDDLE_MOUSE_BUTTON = 1; -GL.RIGHT_MOUSE_BUTTON = 2; - -GL.LEFT_MOUSE_BUTTON_MASK = 1; -GL.RIGHT_MOUSE_BUTTON_MASK = 2; -GL.MIDDLE_MOUSE_BUTTON_MASK = 4; - -GL.last_context_id = 0; - - -//Define WEBGL ENUMS as statics (more to come in WebGL 2) -//sometimes we need some gl enums before having the gl context, solution: define them globally because the specs says they are constant) - -GL.COLOR_BUFFER_BIT = 16384; -GL.DEPTH_BUFFER_BIT = 256; -GL.STENCIL_BUFFER_BIT = 1024; - -GL.TEXTURE_2D = 3553; -GL.TEXTURE_CUBE_MAP = 34067; -GL.TEXTURE_3D = 32879; - -GL.TEXTURE_MAG_FILTER = 10240; -GL.TEXTURE_MIN_FILTER = 10241; -GL.TEXTURE_WRAP_S = 10242; -GL.TEXTURE_WRAP_T = 10243; - -GL.BYTE = 5120; -GL.UNSIGNED_BYTE = 5121; -GL.SHORT = 5122; -GL.UNSIGNED_SHORT = 5123; -GL.INT = 5124; -GL.UNSIGNED_INT = 5125; -GL.FLOAT = 5126; -GL.HALF_FLOAT_OES = 36193; //webgl 1.0 only - -//webgl2 formats -GL.HALF_FLOAT = 5131; -GL.DEPTH_COMPONENT16 = 33189; -GL.DEPTH_COMPONENT24 = 33190; -GL.DEPTH_COMPONENT32F = 36012; - -GL.FLOAT_VEC2 = 35664; -GL.FLOAT_VEC3 = 35665; -GL.FLOAT_VEC4 = 35666; -GL.INT_VEC2 = 35667; -GL.INT_VEC3 = 35668; -GL.INT_VEC4 = 35669; -GL.BOOL = 35670; -GL.BOOL_VEC2 = 35671; -GL.BOOL_VEC3 = 35672; -GL.BOOL_VEC4 = 35673; -GL.FLOAT_MAT2 = 35674; -GL.FLOAT_MAT3 = 35675; -GL.FLOAT_MAT4 = 35676; - -//used to know the amount of data to reserve per uniform -GL.TYPE_LENGTH = {}; -GL.TYPE_LENGTH[ GL.FLOAT ] = GL.TYPE_LENGTH[ GL.INT ] = GL.TYPE_LENGTH[ GL.BYTE ] = GL.TYPE_LENGTH[ GL.BOOL ] = 1; -GL.TYPE_LENGTH[ GL.FLOAT_VEC2 ] = GL.TYPE_LENGTH[ GL.INT_VEC2 ] = GL.TYPE_LENGTH[ GL.BOOL_VEC2 ] = 2; -GL.TYPE_LENGTH[ GL.FLOAT_VEC3 ] = GL.TYPE_LENGTH[ GL.INT_VEC3 ] = GL.TYPE_LENGTH[ GL.BOOL_VEC3 ] = 3; -GL.TYPE_LENGTH[ GL.FLOAT_VEC4 ] = GL.TYPE_LENGTH[ GL.INT_VEC4 ] = GL.TYPE_LENGTH[ GL.BOOL_VEC4 ] = 4; -GL.TYPE_LENGTH[ GL.FLOAT_MAT3 ] = 9; -GL.TYPE_LENGTH[ GL.FLOAT_MAT4 ] = 16; - - -GL.SAMPLER_2D = 35678; -GL.SAMPLER_3D = 35679; -GL.SAMPLER_CUBE = 35680; - -GL.DEPTH_COMPONENT = 6402; -GL.ALPHA = 6406; -GL.RGB = 6407; -GL.RGBA = 6408; -GL.LUMINANCE = 6409; -GL.LUMINANCE_ALPHA = 6410; -GL.DEPTH_STENCIL = 34041; -GL.UNSIGNED_INT_24_8_WEBGL = 34042; - -//webgl2 formats -GL.R8 = 33321; -GL.R16F = 33325; -GL.R32F = 33326; -GL.R8UI = 33330; -GL.RG8 = 33323; -GL.RG16F = 33327; -GL.RG32F = 33328; -GL.RGB8 = 32849; -GL.SRGB8 = 35905; -GL.RGB565 = 36194; -GL.R11F_G11F_B10F = 35898; -GL.RGB9_E5 = 35901; -GL.RGB16F = 34843; -GL.RGB32F = 34837; -GL.RGB8UI = 36221; -GL.RGBA8 = 32856; -GL.RGB5_A1 = 32855; -GL.RGBA16F = 34842; -GL.RGBA32F = 34836; -GL.RGBA8UI = 36220; -GL.RGBA16I = 36232; -GL.RGBA16UI = 36214; -GL.RGBA32I = 36226; -GL.RGBA32UI = 36208; - -GL.NEAREST = 9728; -GL.LINEAR = 9729; -GL.NEAREST_MIPMAP_NEAREST = 9984; -GL.LINEAR_MIPMAP_NEAREST = 9985; -GL.NEAREST_MIPMAP_LINEAR = 9986; -GL.LINEAR_MIPMAP_LINEAR = 9987; - -GL.REPEAT = 10497; -GL.CLAMP_TO_EDGE = 33071; -GL.MIRRORED_REPEAT = 33648; - -GL.ZERO = 0; -GL.ONE = 1; -GL.SRC_COLOR = 768; -GL.ONE_MINUS_SRC_COLOR = 769; -GL.SRC_ALPHA = 770; -GL.ONE_MINUS_SRC_ALPHA = 771; -GL.DST_ALPHA = 772; -GL.ONE_MINUS_DST_ALPHA = 773; -GL.DST_COLOR = 774; -GL.ONE_MINUS_DST_COLOR = 775; -GL.SRC_ALPHA_SATURATE = 776; -GL.CONSTANT_COLOR = 32769; -GL.ONE_MINUS_CONSTANT_COLOR = 32770; -GL.CONSTANT_ALPHA = 32771; -GL.ONE_MINUS_CONSTANT_ALPHA = 32772; - -GL.VERTEX_SHADER = 35633; -GL.FRAGMENT_SHADER = 35632; - -GL.FRONT = 1028; -GL.BACK = 1029; -GL.FRONT_AND_BACK = 1032; - -GL.NEVER = 512; -GL.LESS = 513; -GL.EQUAL = 514; -GL.LEQUAL = 515; -GL.GREATER = 516; -GL.NOTEQUAL = 517; -GL.GEQUAL = 518; -GL.ALWAYS = 519; - -GL.KEEP = 7680; -GL.REPLACE = 7681; -GL.INCR = 7682; -GL.DECR = 7683; -GL.INCR_WRAP = 34055; -GL.DECR_WRAP = 34056; -GL.INVERT = 5386; - -GL.STREAM_DRAW = 35040; -GL.STATIC_DRAW = 35044; -GL.DYNAMIC_DRAW = 35048; - -GL.ARRAY_BUFFER = 34962; -GL.ELEMENT_ARRAY_BUFFER = 34963; - -GL.POINTS = 0; -GL.LINES = 1; -GL.LINE_LOOP = 2; -GL.LINE_STRIP = 3; -GL.TRIANGLES = 4; -GL.TRIANGLE_STRIP = 5; -GL.TRIANGLE_FAN = 6; - -GL.CW = 2304; -GL.CCW = 2305; - -GL.CULL_FACE = 2884; -GL.DEPTH_TEST = 2929; -GL.BLEND = 3042; - -GL.temp_vec3 = vec3.create(); -GL.temp2_vec3 = vec3.create(); -GL.temp_vec4 = vec4.create(); -GL.temp_quat = quat.create(); -GL.temp_mat3 = mat3.create(); -GL.temp_mat4 = mat4.create(); - - -global.DEG2RAD = 0.0174532925; -global.RAD2DEG = 57.295779578552306; -global.EPSILON = 0.000001; - -/** -* Tells if one number is power of two (used for textures) -* @method isPowerOfTwo -* @param {v} number -* @return {boolean} -*/ -global.isPowerOfTwo = GL.isPowerOfTwo = function isPowerOfTwo(v) -{ - return ((Math.log(v) / Math.log(2)) % 1) == 0; -} - -/** -* Tells if one number is power of two (used for textures) -* @method isPowerOfTwo -* @param {v} number -* @return {boolean} -*/ -global.nearestPowerOfTwo = GL.nearestPowerOfTwo = function nearestPowerOfTwo(v) -{ - return Math.pow(2, Math.round( Math.log( v ) / Math.log(2) ) ) -} - - -/** -* converts from polar to cartesian -* @method polarToCartesian -* @param {vec3} out -* @param {number} azimuth orientation from 0 to 2PI -* @param {number} inclianation from -PI to PI -* @param {number} radius -* @return {vec3} returns out -*/ -global.polarToCartesian = function( out, azimuth, inclination, radius ) -{ - out = out || vec3.create(); - out[0] = radius * Math.sin(inclination) * Math.cos(azimuth); - out[1] = radius * Math.cos(inclination); - out[2] = radius * Math.sin(inclination) * Math.sin(azimuth); - return out; -} - -/** -* converts from cartesian to polar -* @method cartesianToPolar -* @param {vec3} out -* @param {number} x -* @param {number} y -* @param {number} z -* @return {vec3} returns [azimuth,inclination,radius] -*/ -global.cartesianToPolar = function( out, x,y,z ) -{ - out = out || vec3.create(); - out[2] = Math.sqrt(x*x+y*y+z*z); - out[0] = Math.atan2(x,z); - out[1] = Math.acos(z/out[2]); - return out; -} - -//Global Scope -//better array conversion to string for serializing -var typed_arrays = [ Uint8Array, Int8Array, Uint16Array, Int16Array, Uint32Array, Int32Array, Float32Array, Float64Array ]; -function typedToArray(){ - return Array.prototype.slice.call(this); -} -typed_arrays.forEach( function(v) { - if(!v.prototype.toJSON) - Object.defineProperty( v.prototype, "toJSON", { - value: typedToArray, - enumerable: false - }); -}); - - - -/** -* Get current time in milliseconds -* @method getTime -* @return {number} -*/ -if(typeof(performance) != "undefined") - global.getTime = performance.now.bind(performance); -else - global.getTime = Date.now.bind( Date ); -GL.getTime = global.getTime; - - -global.isFunction = function isFunction(obj) { - return !!(obj && obj.constructor && obj.call && obj.apply); -} - -global.isArray = function isArray(obj) { - return (obj && obj.constructor === Array ); - //var str = Object.prototype.toString.call(obj); - //return str == '[object Array]' || str == '[object Float32Array]'; -} - -global.isNumber = function isNumber(obj) { - return (obj != null && obj.constructor === Number ); -} - -global.getClassName = function getClassName(obj) -{ - if (!obj) - return; - - //from function info, but not standard - if(obj.name) - return obj.name; - - //from sourcecode - if(obj.toString) { - var arr = obj.toString().match( - /function\s*(\w+)/); - if (arr && arr.length == 2) { - return arr[1]; - } - } -} - -/** -* clone one object recursively, only allows objects containing number,strings,typed-arrays or other objects -* @method cloneObject -* @param {Object} object -* @param {Object} target if omited an empty object is created -* @return {Object} -*/ -global.cloneObject = GL.cloneObject = function(o, t) -{ - if(o.constructor !== Object) - throw("cloneObject only can clone pure javascript objects, not classes"); - - t = t || {}; - - for(var i in o) - { - var v = o[i]; - if(v === null) - { - t[i] = null; - continue; - } - - switch(v.constructor) - { - case Int8Array: - case Uint8Array: - case Int16Array: - case Uint16Array: - case Int32Array: - case Uint32Array: - case Float32Array: - case Float64Array: - t[i] = new v.constructor(v); - break; - case Boolean: - case Number: - case String: - t[i] = v; - break; - case Array: - t[i] = v.concat(); //content is not cloned - break; - case Object: - t[i] = GL.cloneObject(v); - break; - } - } - - return t; -} - - -/* SLOW because accepts booleans -function isNumber(obj) { - var str = Object.prototype.toString.call(obj); - return str == '[object Number]' || str == '[object Boolean]'; -} -*/ - -//given a regular expression, a text and a callback, it calls the function every time it finds it -global.regexMap = function regexMap(regex, text, callback) { - var result; - while ((result = regex.exec(text)) != null) { - callback(result); - } -} - -global.createCanvas = GL.createCanvas = function createCanvas(width, height) { - var canvas = document.createElement('canvas'); - canvas.width = width; - canvas.height = height; - return canvas; -} - -global.cloneCanvas = GL.cloneCanvas = function cloneCanvas(c) { - var canvas = document.createElement('canvas'); - canvas.width = c.width; - canvas.height = c.height; - var ctx = canvas.getContext("2d"); - ctx.drawImage(c,0,0); - return canvas; -} - -if(typeof(Image) != "undefined") //not existing inside workers -{ - Image.prototype.getPixels = function() - { - var canvas = document.createElement('canvas'); - canvas.width = this.width; - canvas.height = this.height; - var ctx = canvas.getContext("2d"); - ctx.drawImage(this,0,0); - return ctx.getImageData(0, 0, this.width, this.height).data; - } -} - -//you must pass an object with characters to replace and replace with what {"a":"A","c":"C"} -if(!String.prototype.hasOwnProperty("replaceAll")) - Object.defineProperty(String.prototype, "replaceAll", { - value: function(words){ - var str = this; - for(var i in words) - str = str.split(i).join(words[i]); - return str; - }, - enumerable: false - }); - -/* -String.prototype.replaceAll = function(words){ - var str = this; - for(var i in words) - str = str.split(i).join(words[i]); - return str; -}; -*/ - -//used for hashing keys -if(!String.prototype.hasOwnProperty("hashCode")) - Object.defineProperty(String.prototype, "hashCode", { - value: function(){ - var hash = 0, i, c, l; - if (this.length == 0) return hash; - for (i = 0, l = this.length; i < l; ++i) { - c = this.charCodeAt(i); - hash = ((hash<<5)-hash)+c; - hash |= 0; // Convert to 32bit integer - } - return hash; - }, - enumerable: false - }); - -//avoid errors when Typed array is expected and regular array is found -//Array.prototype.subarray = Array.prototype.slice; -//if(!Array.prototype.hasOwnProperty("subarray")) -// Object.defineProperty(Array.prototype, "subarray", { value: Array.prototype.slice, enumerable: false }); - -if(!Array.prototype.hasOwnProperty("clone")) - Object.defineProperty(Array.prototype, "clone", { value: Array.prototype.concat, enumerable: false }); -if(!Float32Array.prototype.hasOwnProperty("clone")) - Object.defineProperty(Float32Array.prototype, "clone", { value: function() { return new Float32Array(this); }, enumerable: false }); - - -// remove all properties on obj, effectively reverting it to a new object (to reduce garbage) -global.wipeObject = function wipeObject(obj) -{ - for (var p in obj) - { - if (obj.hasOwnProperty(p)) - delete obj[p]; - } -}; - -//copy methods from origin to target -global.extendClass = GL.extendClass = function extendClass( target, origin ) { - for(var i in origin) //copy class properties - { - if(target.hasOwnProperty(i)) - continue; - target[i] = origin[i]; - } - - if(origin.prototype) //copy prototype properties - { - var prop_names = Object.getOwnPropertyNames( origin.prototype ); - for(var i = 0; i < prop_names.length; ++i) //only enumerables - { - var name = prop_names[i]; - //if(!origin.prototype.hasOwnProperty(name)) - // continue; - - if(target.prototype.hasOwnProperty(name)) //avoid overwritting existing ones - continue; - - //copy getters - if(origin.prototype.__lookupGetter__(name)) - target.prototype.__defineGetter__(name, origin.prototype.__lookupGetter__(name)); - else - target.prototype[name] = origin.prototype[name]; - - //and setters - if(origin.prototype.__lookupSetter__(name)) - target.prototype.__defineSetter__(name, origin.prototype.__lookupSetter__(name)); - } - } - - if(!target.hasOwnProperty("superclass")) - Object.defineProperty(target, "superclass", { - get: function() { return origin }, - enumerable: false - }); -} - - - -//simple http request -global.HttpRequest = GL.request = function HttpRequest( url, params, callback, error, options ) -{ - var async = true; - if(options && options.async !== undefined) - async = options.async; - - if(params) - { - var params_str = null; - var params_arr = []; - for(var i in params) - params_arr.push(i + "=" + params[i]); - params_str = params_arr.join("&"); - url = url + "?" + params_str; - } - - var xhr = new XMLHttpRequest(); - xhr.open('GET', url, async); - xhr.onload = function(e) - { - var response = this.response; - var type = this.getResponseHeader("Content-Type"); - if(this.status != 200) - { - LEvent.trigger(xhr,"fail",this.status); - if(error) - error(this.status); - return; - } - - LEvent.trigger(xhr,"done",this.response); - if(callback) - callback(this.response); - return; - } - - xhr.onerror = function(err) - { - LEvent.trigger(xhr,"fail",err); - } - - if(options) - { - for(var i in options) - xhr[i] = options[i]; - if(options.binary) - xhr.responseType = "arraybuffer"; - } - - xhr.send(); - - return xhr; -} - -//cheap simple promises -if( global.XMLHttpRequest ) -{ - if( !XMLHttpRequest.prototype.hasOwnProperty("done") ) - Object.defineProperty( XMLHttpRequest.prototype, "done", { enumerable: false, value: function(callback) - { - LEvent.bind(this,"done", function(e,err) { callback(err); } ); - return this; - }}); - - if( !XMLHttpRequest.prototype.hasOwnProperty("fail") ) - Object.defineProperty( XMLHttpRequest.prototype, "fail", { enumerable: false, value: function(callback) - { - LEvent.bind(this,"fail", function(e,err) { callback(err); } ); - return this; - }}); -} - -global.getFileExtension = function getFileExtension(url) -{ - var question = url.indexOf("?"); - if(question != -1) - url = url.substr(0,question); - var point = url.lastIndexOf("."); - if(point == -1) - return ""; - return url.substr(point+1).toLowerCase(); -} - - -//allows to pack several (text)files inside one single file (useful for shaders) -//every file must start with \filename.ext or /filename.ext -global.loadFileAtlas = GL.loadFileAtlas = function loadFileAtlas(url, callback, sync) -{ - var deferred_callback = null; - - HttpRequest(url, null, function(data) { - var files = GL.processFileAtlas(data); - if(callback) - callback(files); - if(deferred_callback) - deferred_callback(files); - }, alert, sync); - - return { done: function(callback) { deferred_callback = callback; } }; -} - -//This parses a text file that contains several text files (they are separated by "\filename"), and returns an object with every file separatly -global.processFileAtlas = GL.processFileAtlas = function(data, skip_trim) -{ - var lines = data.split("\n"); - var files = {}; - - var current_file_lines = []; - var current_file_name = ""; - for(var i = 0, l = lines.length; i < l; i++) - { - var line = skip_trim ? lines[i] : lines[i].trim(); - if(!line.length) - continue; - if( line[0] != "\\") - { - current_file_lines.push(line); - continue; - } - - if( current_file_lines.length ) - files[ current_file_name ] = current_file_lines.join("\n"); - current_file_lines.length = 0; - current_file_name = line.substr(1); - } - - if( current_file_lines.length ) - files[ current_file_name ] = current_file_lines.join("\n"); - - return files; -} - - -/* -global.halfFloatToFloat = function( h ) -{ - function convertMantissa(i) { - if (i == 0) - return 0 - else if (i < 1024) - { - var m = i << 13; - var e = 0; - while (!(m & 0x00800000)) - { - e -= 0x00800000 - m = m << 1 - } - m &= ~0x00800000 - e += 0x38800000 - return m | e; - } - return 0x38000000 + ((i - 1024) << 13); - } - - function convertExponent(i) { - if (i == 0) - return 0; - else if (i >= 1 && i <= 31) - return i << 23; - else if (i == 31) - return 0x47800000; - else if (i == 32) - return 0x80000000; - else if (i >= 33 && i <= 63) - return 0x80000000 + ((i - 32) << 23); - return 0xC7800000; - } - - function convertOffset(i) { - if (i == 0 || i == 32) - return 0 - return 1024; - } - - var v = convertMantissa( convertOffset( h >> 10) + (h & 0x3ff) ) + convertExponent(h >> 10); - var a = new Uint32Array([v]); - return (new Float32Array(a.buffer))[0]; -} -*/ - -global.typedArrayToArray = function(array) -{ - var r = []; - r.length = array.length; - for(var i = 0; i < array.length; i++) - r[i] = array[i]; - return r; -} - -global.RGBToHex = function(r, g, b) { - r = Math.min(255, r*255)|0; - g = Math.min(255, g*255)|0; - b = Math.min(255, b*255)|0; - return "#" + ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1); -} - -global.HUEToRGB = function ( p, q, t ){ - if(t < 0) t += 1; - if(t > 1) t -= 1; - if(t < 1/6) return p + (q - p) * 6 * t; - if(t < 1/2) return q; - if(t < 2/3) return p + (q - p) * (2/3 - t) * 6; - return p; -} - -global.HSLToRGB = function( h, s, l, out ){ - var r, g, b; - out = out || vec3.create(); - if(s == 0){ - r = g = b = l; // achromatic - }else{ - var q = l < 0.5 ? l * (1 + s) : l + s - l * s; - var p = 2 * l - q; - r = HUEToRGB(p, q, h + 1/3); - g = HUEToRGB(p, q, h); - b = HUEToRGB(p, q, h - 1/3); - } - out[0] = r; - out[1] = g; - out[2] = b; - return out; -} - -global.hexColorToRGBA = (function() { - //to change the color: from http://www.w3schools.com/cssref/css_colorsfull.asp - var string_colors = { - white: [1,1,1], - black: [0,0,0], - gray: [0.501960813999176, 0.501960813999176, 0.501960813999176], - red: [1,0,0], - orange: [1, 0.6470588445663452, 0], - pink: [1, 0.7529411911964417, 0.7960784435272217], - green: [0, 0.501960813999176, 0], - lime: [0,1,0], - blue: [0,0,1], - violet: [0.9333333373069763, 0.5098039507865906, 0.9333333373069763], - magenta: [1,0,1], - cyan: [0,1,1], - yellow: [1,1,0], - brown: [0.6470588445663452, 0.16470588743686676, 0.16470588743686676], - silver: [0.7529411911964417, 0.7529411911964417, 0.7529411911964417], - gold: [1, 0.843137264251709, 0], - transparent: [0,0,0,0] - }; - - return function( hex, color, alpha ) - { - alpha = (alpha === undefined ? 1 : alpha); - color = color || new Float32Array(4); - color[3] = alpha; - - if(typeof(hex) != "string") - return color; - - - //for those hardcoded colors - var col = string_colors[hex]; - if( col !== undefined ) - { - color.set( col ); - if(color.length == 3) - color[3] = alpha; - else - color[3] *= alpha; - return color; - } - - //rgba colors - var pos = hex.indexOf("rgba("); - if(pos != -1) - { - var str = hex.substr(5,hex.length-2); - str = str.split(","); - color[0] = parseInt( str[0] ) / 255; - color[1] = parseInt( str[1] ) / 255; - color[2] = parseInt( str[2] ) / 255; - color[3] = parseFloat( str[3] ) * alpha; - return color; - } - - var pos = hex.indexOf("hsla("); - if(pos != -1) - { - var str = hex.substr(5,hex.length-2); - str = str.split(","); - HSLToRGB( parseInt( str[0] ) / 360, parseInt( str[1] ) / 100, parseInt( str[2] ) / 100, color ); - color[3] = parseFloat( str[3] ) * alpha; - return color; - } - - color[3] = alpha; - - //rgb colors - var pos = hex.indexOf("rgb("); - if(pos != -1) - { - var str = hex.substr(4,hex.length-2); - str = str.split(","); - color[0] = parseInt( str[0] ) / 255; - color[1] = parseInt( str[1] ) / 255; - color[2] = parseInt( str[2] ) / 255; - return color; - } - - var pos = hex.indexOf("hsl("); - if(pos != -1) - { - var str = hex.substr(4,hex.length-2); - str = str.split(","); - HSLToRGB( parseInt( str[0] ) / 360, parseInt( str[1] ) / 100, parseInt( str[2] ) / 100, color ); - return color; - } - - - //the rest - // Expand shorthand form (e.g. "03F") to full form (e.g. "0033FF") - var shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i; - hex = hex.replace( shorthandRegex, function(m, r, g, b) { - return r + r + g + g + b + b; - }); - - var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex); - if(!result) - return color; - - color[0] = parseInt(result[1], 16) / 255; - color[1] = parseInt(result[2], 16) / 255; - color[2] = parseInt(result[3], 16) / 255; - return color; - } -})(); -/** - * @fileoverview dds - Utilities for loading DDS texture files - * @author Brandon Jones - * @version 0.1 - */ - -/* - * Copyright (c) 2012 Brandon Jones - * - * This software is provided 'as-is', without any express or implied - * warranty. In no event will the authors be held liable for any damages - * arising from the use of this software. - * - * Permission is granted to anyone to use this software for any purpose, - * including commercial applications, and to alter it and redistribute it - * freely, subject to the following restrictions: - * - * 1. The origin of this software must not be misrepresented; you must not - * claim that you wrote the original software. If you use this software - * in a product, an acknowledgment in the product documentation would be - * appreciated but is not required. - * - * 2. Altered source versions must be plainly marked as such, and must not - * be misrepresented as being the original software. - * - * 3. This notice may not be removed or altered from any source - * distribution. - */ - -var DDS = (function () { - - "use strict"; - - // All values and structures referenced from: - // http://msdn.microsoft.com/en-us/library/bb943991.aspx/ - var DDS_MAGIC = 0x20534444; - - var DDSD_CAPS = 0x1, - DDSD_HEIGHT = 0x2, - DDSD_WIDTH = 0x4, - DDSD_PITCH = 0x8, - DDSD_PIXELFORMAT = 0x1000, - DDSD_MIPMAPCOUNT = 0x20000, - DDSD_LINEARSIZE = 0x80000, - DDSD_DEPTH = 0x800000; - - var DDSCAPS_COMPLEX = 0x8, - DDSCAPS_MIPMAP = 0x400000, - DDSCAPS_TEXTURE = 0x1000; - - var DDSCAPS2_CUBEMAP = 0x200, - DDSCAPS2_CUBEMAP_POSITIVEX = 0x400, - DDSCAPS2_CUBEMAP_NEGATIVEX = 0x800, - DDSCAPS2_CUBEMAP_POSITIVEY = 0x1000, - DDSCAPS2_CUBEMAP_NEGATIVEY = 0x2000, - DDSCAPS2_CUBEMAP_POSITIVEZ = 0x4000, - DDSCAPS2_CUBEMAP_NEGATIVEZ = 0x8000, - DDSCAPS2_VOLUME = 0x200000; - - var DDPF_ALPHAPIXELS = 0x1, - DDPF_ALPHA = 0x2, - DDPF_FOURCC = 0x4, - DDPF_RGB = 0x40, - DDPF_YUV = 0x200, - DDPF_LUMINANCE = 0x20000; - - function fourCCToInt32(value) { - return value.charCodeAt(0) + - (value.charCodeAt(1) << 8) + - (value.charCodeAt(2) << 16) + - (value.charCodeAt(3) << 24); - } - - function int32ToFourCC(value) { - return String.fromCharCode( - value & 0xff, - (value >> 8) & 0xff, - (value >> 16) & 0xff, - (value >> 24) & 0xff - ); - } - - var FOURCC_DXT1 = fourCCToInt32("DXT1"); - var FOURCC_DXT3 = fourCCToInt32("DXT3"); - var FOURCC_DXT5 = fourCCToInt32("DXT5"); - - var headerLengthInt = 31; // The header length in 32 bit ints - - // Offsets into the header array - var off_magic = 0; - - var off_size = 1; - var off_flags = 2; - var off_height = 3; - var off_width = 4; - - var off_mipmapCount = 7; - - var off_pfFlags = 20; - var off_pfFourCC = 21; - var off_caps = 27; - - // Little reminder for myself where the above values come from - /*DDS_PIXELFORMAT { - int32 dwSize; // offset: 19 - int32 dwFlags; - char[4] dwFourCC; - int32 dwRGBBitCount; - int32 dwRBitMask; - int32 dwGBitMask; - int32 dwBBitMask; - int32 dwABitMask; // offset: 26 - }; - - DDS_HEADER { - int32 dwSize; // 1 - int32 dwFlags; - int32 dwHeight; - int32 dwWidth; - int32 dwPitchOrLinearSize; - int32 dwDepth; - int32 dwMipMapCount; // offset: 7 - int32[11] dwReserved1; - DDS_PIXELFORMAT ddspf; // offset 19 - int32 dwCaps; // offset: 27 - int32 dwCaps2; - int32 dwCaps3; - int32 dwCaps4; - int32 dwReserved2; // offset 31 - };*/ - - /** - * Transcodes DXT into RGB565. - * Optimizations: - * 1. Use integer math to compute c2 and c3 instead of floating point - * math. Specifically: - * c2 = 5/8 * c0 + 3/8 * c1 - * c3 = 3/8 * c0 + 5/8 * c1 - * This is about a 40% performance improvement. It also appears to - * match what hardware DXT decoders do, as the colors produced - * by this integer math match what hardware produces, while the - * floating point in dxtToRgb565Unoptimized() produce slightly - * different colors (for one GPU this was tested on). - * 2. Unroll the inner loop. Another ~10% improvement. - * 3. Compute r0, g0, b0, r1, g1, b1 only once instead of twice. - * Another 10% improvement. - * 4. Use a Uint16Array instead of a Uint8Array. Another 10% improvement. - * @author Evan Parker - * @param {Uint16Array} src The src DXT bits as a Uint16Array. - * @param {number} srcByteOffset - * @param {number} width - * @param {number} height - * @return {Uint16Array} dst - */ - function dxtToRgb565(src, src16Offset, width, height) { - var c = new Uint16Array(4); - var dst = new Uint16Array(width * height); - var nWords = (width * height) / 4; - var m = 0; - var dstI = 0; - var i = 0; - var r0 = 0, g0 = 0, b0 = 0, r1 = 0, g1 = 0, b1 = 0; - - var blockWidth = width / 4; - var blockHeight = height / 4; - for (var blockY = 0; blockY < blockHeight; blockY++) { - for (var blockX = 0; blockX < blockWidth; blockX++) { - i = src16Offset + 4 * (blockY * blockWidth + blockX); - c[0] = src[i]; - c[1] = src[i + 1]; - r0 = c[0] & 0x1f; - g0 = c[0] & 0x7e0; - b0 = c[0] & 0xf800; - r1 = c[1] & 0x1f; - g1 = c[1] & 0x7e0; - b1 = c[1] & 0xf800; - // Interpolate between c0 and c1 to get c2 and c3. - // Note that we approximate 1/3 as 3/8 and 2/3 as 5/8 for - // speed. This also appears to be what the hardware DXT - // decoder in many GPUs does :) - c[2] = ((5 * r0 + 3 * r1) >> 3) - | (((5 * g0 + 3 * g1) >> 3) & 0x7e0) - | (((5 * b0 + 3 * b1) >> 3) & 0xf800); - c[3] = ((5 * r1 + 3 * r0) >> 3) - | (((5 * g1 + 3 * g0) >> 3) & 0x7e0) - | (((5 * b1 + 3 * b0) >> 3) & 0xf800); - m = src[i + 2]; - dstI = (blockY * 4) * width + blockX * 4; - dst[dstI] = c[m & 0x3]; - dst[dstI + 1] = c[(m >> 2) & 0x3]; - dst[dstI + 2] = c[(m >> 4) & 0x3]; - dst[dstI + 3] = c[(m >> 6) & 0x3]; - dstI += width; - dst[dstI] = c[(m >> 8) & 0x3]; - dst[dstI + 1] = c[(m >> 10) & 0x3]; - dst[dstI + 2] = c[(m >> 12) & 0x3]; - dst[dstI + 3] = c[(m >> 14)]; - m = src[i + 3]; - dstI += width; - dst[dstI] = c[m & 0x3]; - dst[dstI + 1] = c[(m >> 2) & 0x3]; - dst[dstI + 2] = c[(m >> 4) & 0x3]; - dst[dstI + 3] = c[(m >> 6) & 0x3]; - dstI += width; - dst[dstI] = c[(m >> 8) & 0x3]; - dst[dstI + 1] = c[(m >> 10) & 0x3]; - dst[dstI + 2] = c[(m >> 12) & 0x3]; - dst[dstI + 3] = c[(m >> 14)]; - } - } - return dst; - } - - function BGRtoRGB( byteArray ) - { - for(var j = 0, l = byteArray.length, tmp = 0; j < l; j+=4) //BGR fix - { - tmp = byteArray[j]; - byteArray[j] = byteArray[j+2]; - byteArray[j+2] = tmp; - } - } - - function flipDXT( width, blockBytes, byteArray ) - { - //TODO - //var row = Uint8Array(width); - } - - - /** - * Parses a DDS file from the given arrayBuffer and uploads it into the currently bound texture - * - * @param {WebGLRenderingContext} gl WebGL rendering context - * @param {WebGLCompressedTextureS3TC} ext WEBGL_compressed_texture_s3tc extension object - * @param {TypedArray} arrayBuffer Array Buffer containing the DDS files data - * @param {boolean} [loadMipmaps] If false only the top mipmap level will be loaded, otherwise all available mipmaps will be uploaded - * - * @returns {number} Number of mipmaps uploaded, 0 if there was an error - */ - function uploadDDSLevels(gl, ext, arrayBuffer, loadMipmaps) { - var header = new Int32Array(arrayBuffer, 0, headerLengthInt), - fourCC, blockBytes, internalFormat, - width, height, dataLength, dataOffset, is_cubemap, - rgb565Data, byteArray, mipmapCount, i, face; - - if(header[off_magic] != DDS_MAGIC) { - console.error("Invalid magic number in DDS header"); - return 0; - } - - if(!header[off_pfFlags] & DDPF_FOURCC) { - console.error("Unsupported format, must contain a FourCC code"); - return 0; - } - - fourCC = header[off_pfFourCC]; - switch(fourCC) { - case FOURCC_DXT1: - blockBytes = 8; - internalFormat = ext ? ext.COMPRESSED_RGB_S3TC_DXT1_EXT : null; - break; - - /* - case FOURCC_DXT1: - blockBytes = 8; - internalFormat = ext ? ext.COMPRESSED_RGBA_S3TC_DXT1_EXT : null; - break; - */ - - case FOURCC_DXT3: - blockBytes = 16; - internalFormat = ext ? ext.COMPRESSED_RGBA_S3TC_DXT3_EXT : null; - break; - - case FOURCC_DXT5: - blockBytes = 16; - internalFormat = ext ? ext.COMPRESSED_RGBA_S3TC_DXT5_EXT : null; - break; - - default: - blockBytes = 4; - fourCC = null; - internalFormat = gl.RGBA; - //console.error("Unsupported FourCC code:", int32ToFourCC(fourCC), fourCC); - //return null; - } - - mipmapCount = 1; - if(header[off_flags] & DDSD_MIPMAPCOUNT && loadMipmaps !== false) { - mipmapCount = Math.max(1, header[off_mipmapCount]); - } - - width = header[off_width]; - height = header[off_height]; - dataOffset = header[off_size] + 4; - is_cubemap = !!(header[off_caps+1] & DDSCAPS2_CUBEMAP); - - if(is_cubemap) - { - //console.error("Cubemaps not supported in DDS"); - //return null; - - for(face = 0; face < 6; ++face) - { - width = header[off_width]; - height = header[off_height]; - for(var i = 0; i < mipmapCount; ++i) { - if(fourCC) - { - dataLength = Math.max( 4, width )/4 * Math.max( 4, height )/4 * blockBytes; - byteArray = new Uint8Array(arrayBuffer, dataOffset, dataLength); - flipDXT( width, blockBytes, byteArray ); - gl.compressedTexImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + face, i, internalFormat, width, height, 0, byteArray); - } - else - { - gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false ); - dataLength = width * height * blockBytes; - byteArray = new Uint8Array(arrayBuffer, dataOffset, dataLength); - BGRtoRGB(byteArray); - gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + face, i, internalFormat, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, byteArray); - } - dataOffset += dataLength; - width *= 0.5; - height *= 0.5; - } - } - } - else //2d texture - { - if(ext) { - gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true ); - for(var i = 0; i < mipmapCount; ++i) { - if(fourCC) - { - dataLength = Math.max( 4, width )/4 * Math.max( 4, height )/4 * blockBytes; - byteArray = new Uint8Array(arrayBuffer, dataOffset, dataLength); - gl.compressedTexImage2D(gl.TEXTURE_2D, i, internalFormat, width, height, 0, byteArray); - } - else - { - dataLength = width * height * blockBytes; - byteArray = new Uint8Array(arrayBuffer, dataOffset, dataLength); - BGRtoRGB(byteArray); - gl.texImage2D(gl.TEXTURE_2D, i, internalFormat, width, height, 0, internalFormat, gl.UNSIGNED_BYTE, byteArray); - } - dataOffset += dataLength; - width *= 0.5; - height *= 0.5; - } - } else { - if(fourCC == FOURCC_DXT1) { - dataLength = Math.max( 4, width )/4 * Math.max( 4, height )/4 * blockBytes; - byteArray = new Uint16Array(arrayBuffer); - //Decompress - rgb565Data = dxtToRgb565(byteArray, dataOffset / 2, width, height); - gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, width, height, 0, gl.RGB, gl.UNSIGNED_SHORT_5_6_5, rgb565Data); - if(loadMipmaps) { - gl.generateMipmap(gl.TEXTURE_2D); - } - } else { - console.error("No manual decoder for", int32ToFourCC(fourCC), "and no native support"); - return 0; - } - } - } - - return mipmapCount; - } - - /** - * Parses a DDS file from the given arrayBuffer and uploads it into the currently bound texture - * - * @param {WebGLRenderingContext} gl WebGL rendering context - * @param {WebGLCompressedTextureS3TC} ext WEBGL_compressed_texture_s3tc extension object - * @param {TypedArray} arrayBuffer Array Buffer containing the DDS files data - * @param {boolean} [loadMipmaps] If false only the top mipmap level will be loaded, otherwise all available mipmaps will be uploaded - * - * @returns {number} Number of mipmaps uploaded, 0 if there was an error - */ - function getDDSLevels( arrayBuffer, compressed_not_supported ) - { - var header = new Int32Array(arrayBuffer, 0, headerLengthInt), - fourCC, blockBytes, internalFormat, - width, height, dataLength, dataOffset, is_cubemap, - rgb565Data, byteArray, mipmapCount, i, face; - - if(header[off_magic] != DDS_MAGIC) { - console.error("Invalid magic number in DDS header"); - return 0; - } - - if(!header[off_pfFlags] & DDPF_FOURCC) { - console.error("Unsupported format, must contain a FourCC code"); - return 0; - } - - fourCC = header[off_pfFourCC]; - switch(fourCC) { - case FOURCC_DXT1: - blockBytes = 8; - internalFormat = "COMPRESSED_RGB_S3TC_DXT1_EXT"; - break; - - case FOURCC_DXT3: - blockBytes = 16; - internalFormat = "COMPRESSED_RGBA_S3TC_DXT3_EXT"; - break; - - case FOURCC_DXT5: - blockBytes = 16; - internalFormat = "COMPRESSED_RGBA_S3TC_DXT5_EXT"; - break; - - default: - blockBytes = 4; - internalFormat = "RGBA"; - //console.error("Unsupported FourCC code:", int32ToFourCC(fourCC), fourCC); - //return null; - } - - mipmapCount = 1; - if(header[off_flags] & DDSD_MIPMAPCOUNT && loadMipmaps !== false) { - mipmapCount = Math.max(1, header[off_mipmapCount]); - } - - width = header[off_width]; - height = header[off_height]; - dataOffset = header[off_size] + 4; - is_cubemap = !!(header[off_caps+1] & DDSCAPS2_CUBEMAP); - - var buffers = []; - - if(is_cubemap) - { - for(var face = 0; face < 6; ++face) - { - width = header[off_width]; - height = header[off_height]; - for(var i = 0; i < mipmapCount; ++i) - { - if(fourCC) - { - dataLength = Math.max( 4, width )/4 * Math.max( 4, height )/4 * blockBytes; - byteArray = new Uint8Array(arrayBuffer, dataOffset, dataLength); - buffers.push({ tex: "TEXTURE_CUBE_MAP", face: face, mipmap: i, internalFormat: internalFormat, width: width, height: height, offset: 0, dataOffset: dataOffset, dataLength: dataLength }); - } - else - { - dataLength = width * height * blockBytes; - byteArray = new Uint8Array(arrayBuffer, dataOffset, dataLength); - BGRtoRGB(byteArray); - buffers.push({ tex: "TEXTURE_CUBE_MAP", face: face, mipmap: i, internalFormat: internalFormat, width: width, height: height, offset: 0, type: "UNSIGNED_BYTE", dataOffset: dataOffset, dataLength: dataLength }); - } - dataOffset += dataLength; - width *= 0.5; - height *= 0.5; - } - } - } - else //2d texture - { - if(!compressed_not_supported) - { - for(var i = 0; i < mipmapCount; ++i) { - dataLength = Math.max( 4, width )/4 * Math.max( 4, height )/4 * blockBytes; - byteArray = new Uint8Array(arrayBuffer, dataOffset, dataLength); - //gl.compressedTexImage2D(gl.TEXTURE_2D, i, internalFormat, width, height, 0, byteArray); - buffers.push({ tex: "TEXTURE_2D", mipmap: i, internalFormat: internalFormat, width: width, height: height, offset: 0, type: "UNSIGNED_BYTE", dataOffset: dataOffset, dataLength: dataLength }); - dataOffset += dataLength; - width *= 0.5; - height *= 0.5; - } - } else { - if(fourCC == FOURCC_DXT1) - { - dataLength = Math.max( 4, width )/4 * Math.max( 4, height )/4 * blockBytes; - byteArray = new Uint16Array(arrayBuffer); - rgb565Data = dxtToRgb565(byteArray, dataOffset / 2, width, height); - //gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, width, height, 0, gl.RGB, gl.UNSIGNED_SHORT_5_6_5, rgb565Data); - buffers.push({ tex: "TEXTURE_2D", mipmap: 0, internalFormat: "RGB", width: width, height: height, offset: 0, format:"RGB", type: "UNSIGNED_SHORT_5_6_5", data: rgb565Data }); - } else { - console.error("No manual decoder for", int32ToFourCC(fourCC), "and no native support"); - return 0; - } - } - } - - return buffers; - } - - /** - * Creates a texture from the DDS file at the given URL. Simple shortcut for the most common use case - * - * @param {WebGLRenderingContext} gl WebGL rendering context - * @param {WebGLCompressedTextureS3TC} ext WEBGL_compressed_texture_s3tc extension object - * @param {string} src URL to DDS file to be loaded - * @param {function} [callback] callback to be fired when the texture has finished loading - * - * @returns {WebGLTexture} New texture that will receive the DDS image data - */ - function loadDDSTextureEx(gl, ext, src, texture, loadMipmaps, callback) { - var xhr = new XMLHttpRequest(); - - xhr.open('GET', src, true); - xhr.responseType = "arraybuffer"; - xhr.onload = function() { - if(this.status == 200) { - var header = new Int32Array(this.response, 0, headerLengthInt) - var is_cubemap = !!(header[off_caps+1] & DDSCAPS2_CUBEMAP); - var tex_type = is_cubemap ? gl.TEXTURE_CUBE_MAP : gl.TEXTURE_2D; - gl.bindTexture(tex_type, texture); - var mipmaps = uploadDDSLevels(gl, ext, this.response, loadMipmaps); - gl.texParameteri(tex_type, gl.TEXTURE_MAG_FILTER, gl.LINEAR); - gl.texParameteri(tex_type, gl.TEXTURE_MIN_FILTER, mipmaps > 1 ? gl.LINEAR_MIPMAP_LINEAR : gl.LINEAR); - gl.bindTexture(tex_type, null); - texture.texture_type = tex_type; - texture.width = header[off_width]; - texture.height = header[off_height]; - } - - if(callback) { - callback(texture); - } - }; - xhr.send(null); - - return texture; - } - - /** - * Creates a texture from the DDS file at the given ArrayBuffer. - * - * @param {WebGLRenderingContext} gl WebGL rendering context - * @param {WebGLCompressedTextureS3TC} ext WEBGL_compressed_texture_s3tc extension object - * @param {ArrayBuffer} data containing the DDS file - * @param {Texture} texture from GL.Texture - * @returns {WebGLTexture} New texture that will receive the DDS image data - */ - function loadDDSTextureFromMemoryEx(gl, ext, data, texture, loadMipmaps) { - var header = new Int32Array(data, 0, headerLengthInt) - var is_cubemap = !!(header[off_caps+1] & DDSCAPS2_CUBEMAP); - var tex_type = is_cubemap ? gl.TEXTURE_CUBE_MAP : gl.TEXTURE_2D; - - var handler = texture.handler || texture; - - gl.bindTexture(tex_type, texture.handler); - - //upload data - var mipmaps = uploadDDSLevels(gl, ext, data, loadMipmaps); - - gl.texParameteri(tex_type, gl.TEXTURE_MAG_FILTER, gl.LINEAR); - gl.texParameteri(tex_type, gl.TEXTURE_MIN_FILTER, mipmaps > 1 ? gl.LINEAR_MIPMAP_LINEAR : gl.LINEAR); - if(is_cubemap) - { - gl.texParameteri(tex_type, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE ); - gl.texParameteri(tex_type, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE ); - } - - gl.bindTexture(tex_type, null); //unbind - if(texture.handler) - { - texture.texture_type = tex_type; - texture.width = header[off_width]; - texture.height = header[off_height]; - } - return texture; - } - - /** - * Extracts the texture info from a DDS file at the given ArrayBuffer. - * - * @param {ArrayBuffer} data containing the DDS file - * - * @returns {Object} contains mipmaps and properties - */ - function getDDSTextureFromMemoryEx(data) { - var header = new Int32Array(data, 0, headerLengthInt) - var is_cubemap = !!(header[off_caps+1] & DDSCAPS2_CUBEMAP); - var tex_type = is_cubemap ? "TEXTURE_CUBE_MAP" : "TEXTURE_2D"; - var buffers = getDDSLevels(data); - - var texture = { - type: tex_type, - buffers: buffers, - data: data, - width: header[off_width], - height: header[off_height] - }; - - return texture; - } - - /** - * Creates a texture from the DDS file at the given URL. Simple shortcut for the most common use case - * - * @param {WebGLRenderingContext} gl WebGL rendering context - * @param {WebGLCompressedTextureS3TC} ext WEBGL_compressed_texture_s3tc extension object - * @param {string} src URL to DDS file to be loaded - * @param {function} [callback] callback to be fired when the texture has finished loading - * - * @returns {WebGLTexture} New texture that will receive the DDS image data - */ - function loadDDSTexture(gl, ext, src, callback) { - var texture = gl.createTexture(); - var ext = gl.getExtension("WEBGL_compressed_texture_s3tc"); - loadDDSTextureEx(gl, ext, src, texture, true, callback); - return texture; - } - - return { - dxtToRgb565: dxtToRgb565, - uploadDDSLevels: uploadDDSLevels, - loadDDSTextureEx: loadDDSTextureEx, - loadDDSTexture: loadDDSTexture, - loadDDSTextureFromMemoryEx: loadDDSTextureFromMemoryEx, - getDDSTextureFromMemoryEx: getDDSTextureFromMemoryEx - }; - -})(); - -if(typeof(global) != "undefined") - global.DDS = DDS; - -/* this file adds some extra functions to gl-matrix library */ -if(typeof(glMatrix) == "undefined") - throw("You must include glMatrix on your project"); - -Math.clamp = function(v,a,b) { return (a > v ? a : (b < v ? b : v)); } - -var V3 = vec3.create; -var M4 = vec3.create; - - -vec3.ZERO = vec3.fromValues(0,0,0); -vec3.FRONT = vec3.fromValues(0,0,-1); -vec3.UP = vec3.fromValues(0,1,0); -vec3.RIGHT = vec3.fromValues(1,0,0); - -vec2.rotate = function(out,vec,angle_in_rad) -{ - var x = vec[0], y = vec[1]; - var cos = Math.cos(angle_in_rad); - var sin = Math.sin(angle_in_rad); - out[0] = x * cos - y * sin; - out[1] = x * sin + y * cos; - return out; -} - -vec3.zero = function(a) -{ - a[0] = a[1] = 0.0; - return a; -} - -//for signed angles -vec2.perpdot = function(a,b) -{ - return a[1] * b[0] + -a[0] * b[1]; -} - -vec2.computeSignedAngle = function( a, b ) -{ - return Math.atan2( vec2.perpdot(a,b), vec2.dot(a,b) ); -} - -vec2.random = function( vec, scale ) -{ - scale = scale || 1.0; - vec[0] = Math.random() * scale; - vec[1] = Math.random() * scale; - return vec; -} - -vec3.zero = function(a) -{ - a[0] = a[1] = a[2] = 0.0; - return a; -} - -vec3.minValue = function(a) -{ - if(a[0] < a[1] && a[0] < a[2]) return a[0]; - if(a[1] < a[2]) return a[1]; - return a[2]; -} - -vec3.maxValue = function(a) -{ - if(a[0] > a[1] && a[0] > a[2]) return a[0]; - if(a[1] > a[2]) return a[1]; - return a[2]; -} - -vec3.minValue = function(a) -{ - if(a[0] < a[1] && a[0] < a[2]) return a[0]; - if(a[1] < a[2]) return a[1]; - return a[2]; -} - -vec3.addValue = function(out,a,v) -{ - out[0] = a[0] + v; - out[1] = a[1] + v; - out[2] = a[2] + v; -} - -vec3.subValue = function(out,a,v) -{ - out[0] = a[0] - v; - out[1] = a[1] - v; - out[2] = a[2] - v; -} - -vec3.toArray = function(vec) -{ - return [vec[0],vec[1],vec[2]]; -} - -vec3.rotateX = function(out,vec,angle_in_rad) -{ - var y = vec[1], z = vec[2]; - var cos = Math.cos(angle_in_rad); - var sin = Math.sin(angle_in_rad); - - out[0] = vec[0]; - out[1] = y * cos - z * sin; - out[2] = y * sin + z * cos; - return out; -} - -vec3.rotateY = function(out,vec,angle_in_rad) -{ - var x = vec[0], z = vec[2]; - var cos = Math.cos(angle_in_rad); - var sin = Math.sin(angle_in_rad); - - out[0] = x * cos - z * sin; - out[1] = vec[1]; - out[2] = x * sin + z * cos; - return out; -} - -vec3.rotateZ = function(out,vec,angle_in_rad) -{ - var x = vec[0], y = vec[1]; - var cos = Math.cos(angle_in_rad); - var sin = Math.sin(angle_in_rad); - - out[0] = x * cos - y * sin; - out[1] = x * sin + y * cos; - out[2] = vec[2]; - return out; -} - -vec3.angle = function( a, b ) -{ - return Math.acos( vec3.dot(a,b) ); -} - -vec3.signedAngle = function(from, to, axis) -{ - var unsignedAngle = vec3.angle( from, to ); - var cross_x = from[1] * to[2] - from[2] * to[1]; - var cross_y = from[2] * to[0] - from[0] * to[2]; - var cross_z = from[0] * to[1] - from[1] * to[0]; - var sign = Math.sign(axis[0] * cross_x + axis[1] * cross_y + axis[2] * cross_z); - return unsignedAngle * sign; -} - -vec3.random = function(vec, scale) -{ - scale = scale || 1.0; - vec[0] = Math.random() * scale; - vec[1] = Math.random() * scale; - vec[2] = Math.random() * scale; - return vec; -} - -//converts a polar coordinate (radius, lat, long) to (x,y,z) -vec3.polarToCartesian = function(out, v) -{ - var r = v[0]; - var lat = v[1]; - var lon = v[2]; - out[0] = r * Math.cos(lat) * Math.sin(lon); - out[1] = r * Math.sin(lat); - out[2] = r * Math.cos(lat) * Math.cos(lon); - return out; -} - -vec3.reflect = function(out, v, n) -{ - var x = v[0]; var y = v[1]; var z = v[2]; - vec3.scale( out, n, -2 * vec3.dot(v,n) ); - out[0] += x; - out[1] += y; - out[2] += z; - return out; -} - -/* VEC4 */ -vec4.random = function(vec, scale) -{ - scale = scale || 1.0; - vec[0] = Math.random() * scale; - vec[1] = Math.random() * scale; - vec[2] = Math.random() * scale; - vec[3] = Math.random() * scale; - return vec; -} - -vec4.toArray = function(vec) -{ - return [vec[0],vec[1],vec[2],vec[3]]; -} - - -/** MATRIX ********************/ -mat3.IDENTITY = mat3.create(); -mat4.IDENTITY = mat4.create(); - -mat4.toArray = function(mat) -{ - return [mat[0],mat[1],mat[2],mat[3],mat[4],mat[5],mat[6],mat[7],mat[8],mat[9],mat[10],mat[11],mat[12],mat[13],mat[14],mat[15]]; -} - -mat4.setUpAndOrthonormalize = function(out, m, up) -{ - if(m != out) - mat4.copy(out,m); - var right = out.subarray(0,3); - vec3.normalize(out.subarray(4,7),up); - var front = out.subarray(8,11); - vec3.cross( right, up, front ); - vec3.normalize( right, right ); - vec3.cross( front, right, up ); - vec3.normalize( front, front ); -} - -mat4.multiplyVec3 = function(out, m, a) { - var x = a[0], y = a[1], z = a[2]; - out[0] = m[0] * x + m[4] * y + m[8] * z + m[12]; - out[1] = m[1] * x + m[5] * y + m[9] * z + m[13]; - out[2] = m[2] * x + m[6] * y + m[10] * z + m[14]; - return out; -}; - -//from https://github.com/hughsk/from-3d-to-2d/blob/master/index.js -//m should be a projection matrix (or a VP or MVP) -//projects vector from 3D to 2D and returns the value in normalized screen space -mat4.projectVec3 = function(out, m, a) -{ - var ix = a[0]; - var iy = a[1]; - var iz = a[2]; - - var ox = m[0] * ix + m[4] * iy + m[8] * iz + m[12]; - var oy = m[1] * ix + m[5] * iy + m[9] * iz + m[13]; - var oz = m[2] * ix + m[6] * iy + m[10] * iz + m[14]; - var ow = m[3] * ix + m[7] * iy + m[11] * iz + m[15]; - - out[0] = (ox / ow + 1) / 2; - out[1] = (oy / ow + 1) / 2; - out[2] = (oz / ow + 1) / 2; - return out; -}; - - -//from https://github.com/hughsk/from-3d-to-2d/blob/master/index.js -vec3.project = function(out, vec, mvp, viewport) { - viewport = viewport || gl.viewport_data; - - var m = mvp; - - var ix = vec[0]; - var iy = vec[1]; - var iz = vec[2]; - - var ox = m[0] * ix + m[4] * iy + m[8] * iz + m[12]; - var oy = m[1] * ix + m[5] * iy + m[9] * iz + m[13]; - var oz = m[2] * ix + m[6] * iy + m[10] * iz + m[14]; - var ow = m[3] * ix + m[7] * iy + m[11] * iz + m[15]; - - var projx = (ox / ow + 1) / 2; - var projy = 1 - (oy / ow + 1) / 2; - var projz = (oz / ow + 1) / 2; - - out[0] = projx * viewport[2] + viewport[0]; - out[1] = projy * viewport[3] + viewport[1]; - out[2] = projz; //ow - return out; -}; - -var unprojectMat = mat4.create(); -var unprojectVec = vec4.create(); - -vec3.unproject = function (out, vec, viewprojection, viewport) { - - var m = unprojectMat; - var v = unprojectVec; - - v[0] = (vec[0] - viewport[0]) * 2.0 / viewport[2] - 1.0; - v[1] = (vec[1] - viewport[1]) * 2.0 / viewport[3] - 1.0; - v[2] = 2.0 * vec[2] - 1.0; - v[3] = 1.0; - - if(!mat4.invert(m,viewprojection)) - return null; - - vec4.transformMat4(v, v, m); - if(v[3] === 0.0) - return null; - - out[0] = v[0] / v[3]; - out[1] = v[1] / v[3]; - out[2] = v[2] / v[3]; - - return out; -}; - -//without translation -mat4.rotateVec3 = function(out, m, a) { - var x = a[0], y = a[1], z = a[2]; - out[0] = m[0] * x + m[4] * y + m[8] * z; - out[1] = m[1] * x + m[5] * y + m[9] * z; - out[2] = m[2] * x + m[6] * y + m[10] * z; - return out; -}; - -mat4.fromTranslationFrontTop = function (out, pos, front, top) -{ - vec3.cross(out.subarray(0,3), front, top); - out.set(top,4); - out.set(front,8); - out.set(pos,12); - return out; -} - - -mat4.translationMatrix = function (v) -{ - var out = mat4.create(); - out[12] = v[0]; - out[13] = v[1]; - out[14] = v[2]; - return out; -} - -mat4.setTranslation = function (out, v) -{ - out[12] = v[0]; - out[13] = v[1]; - out[14] = v[2]; - return out; -} - - -mat4.getTranslation = function (out, matrix) -{ - out[0] = matrix[12]; - out[1] = matrix[13]; - out[2] = matrix[14]; - return out; -} - -//returns the matrix without rotation -mat4.toRotationMat4 = function (out, mat) { - mat4.copy(out,mat); - out[12] = out[13] = out[14] = 0.0; - return out; -}; - -mat4.swapRows = function(out, mat, row, row2) -{ - if(out != mat) - { - mat4.copy(out, mat); - out[4*row] = mat[4*row2]; - out[4*row+1] = mat[4*row2+1]; - out[4*row+2] = mat[4*row2+2]; - out[4*row+3] = mat[4*row2+3]; - out[4*row2] = mat[4*row]; - out[4*row2+1] = mat[4*row+1]; - out[4*row2+2] = mat[4*row+2]; - out[4*row2+3] = mat[4*row+3]; - return out; - } - - var temp = new Float32Array(matrix.subarray(row*4,row*5)); - matrix.set( matrix.subarray(row2*4,row2*5), row*4 ); - matrix.set( temp, row2*4 ); - return out; -} - -//used in skinning -mat4.scaleAndAdd = function(out, mat, mat2, v) -{ - out[0] = mat[0] + mat2[0] * v; out[1] = mat[1] + mat2[1] * v; out[2] = mat[2] + mat2[2] * v; out[3] = mat[3] + mat2[3] * v; - out[4] = mat[4] + mat2[4] * v; out[5] = mat[5] + mat2[5] * v; out[6] = mat[6] + mat2[6] * v; out[7] = mat[7] + mat2[7] * v; - out[8] = mat[8] + mat2[8] * v; out[9] = mat[9] + mat2[9] * v; out[10] = mat[10] + mat2[10] * v; out[11] = mat[11] + mat2[11] * v; - out[12] = mat[12] + mat2[12] * v; out[13] = mat[13] + mat2[13] * v; out[14] = mat[14] + mat2[14] * v; out[15] = mat[15] + mat2[15] * v; - return out; -} - -quat.fromAxisAngle = function(axis, rad) -{ - var out = quat.create(); - rad = rad * 0.5; - var s = Math.sin(rad); - out[0] = s * axis[0]; - out[1] = s * axis[1]; - out[2] = s * axis[2]; - out[3] = Math.cos(rad); - return out; -} - -//from https://answers.unity.com/questions/467614/what-is-the-source-code-of-quaternionlookrotation.html -quat.lookRotation = (function(){ - var vector = vec3.create(); - var vector2 = vec3.create(); - var vector3 = vec3.create(); - - return function( q, front, up ) - { - vec3.normalize(vector,front); - vec3.cross( vector2, up, vector ); - vec3.normalize(vector2,vector2); - vec3.cross( vector3, vector, vector2 ); - - var m00 = vector2[0]; - var m01 = vector2[1]; - var m02 = vector2[2]; - var m10 = vector3[0]; - var m11 = vector3[1]; - var m12 = vector3[2]; - var m20 = vector[0]; - var m21 = vector[1]; - var m22 = vector[2]; - - var num8 = (m00 + m11) + m22; - - if (num8 > 0) - { - var num = Math.sqrt(num8 + 1); - q[3] = num * 0.5; - num = 0.5 / num; - q[0] = (m12 - m21) * num; - q[1] = (m20 - m02) * num; - q[2] = (m01 - m10) * num; - return q; - } - if ((m00 >= m11) && (m00 >= m22)) - { - var num7 = Math.sqrt(((1 + m00) - m11) - m22); - var num4 = 0.5 / num7; - q[0] = 0.5 * num7; - q[1] = (m01 + m10) * num4; - q[2] = (m02 + m20) * num4; - q[3] = (m12 - m21) * num4; - return q; - } - if (m11 > m22) - { - var num6 = Math.sqrt(((1 + m11) - m00) - m22); - var num3 = 0.5 / num6; - q[0] = (m10+ m01) * num3; - q[1] = 0.5 * num6; - q[2] = (m21 + m12) * num3; - q[3] = (m20 - m02) * num3; - return q; - } - var num5 = Math.sqrt(((1 + m22) - m00) - m11); - var num2 = 0.5 / num5; - q[0] = (m20 + m02) * num2; - q[1] = (m21 + m12) * num2; - q[2] = 0.5 * num5; - q[3] = (m01 - m10) * num2; - return q; - }; -})(); - -/* -quat.toEuler = function(out, quat) { - var q = quat; - var heading, attitude, bank; - - if( (q[0]*q[1] + q[2]*q[3]) == 0.5 ) - { - heading = 2 * Math.atan2(q[0],q[3]); - bank = 0; - attitude = 0; //¿? - } - else if( (q[0]*q[1] + q[2]*q[3]) == 0.5 ) - { - heading = -2 * Math.atan2(q[0],q[3]); - bank = 0; - attitude = 0; //¿? - } - else - { - heading = Math.atan2( 2*(q[1]*q[3] - q[0]*q[2]) , 1 - 2 * (q[1]*q[1] - q[2]*q[2]) ); - attitude = Math.asin( 2*(q[0]*q[1] - q[2]*q[3]) ); - bank = Math.atan2( 2*(q[0]*q[3] - q[1]*q[2]), 1 - 2*(q[0]*q[0] - q[2]*q[2]) ); - } - - if(!out) - out = vec3.create(); - vec3.set(out, heading, attitude, bank); - return out; -} -*/ - -/* -//FROM https://en.wikipedia.org/wiki/Conversion_between_quaternions_and_Euler_angles -//doesnt work well -quat.toEuler = function(out, q) -{ - var yaw = Math.atan2(2*q[0]*q[3] + 2*q[1]*q[2], 1 - 2*q[2]*q[2] - 2*q[3]*q[3]); - var pitch = Math.asin(2*q[0]*q[2] - 2*q[3]*q[1]); - var roll = Math.atan2(2*q[0]*q[1] + 2*q[2]*q[3], 1 - 2*q[1]*q[1] - 2*q[2]*q[2]); - if(!out) - out = vec3.create(); - vec3.set(out, yaw, pitch, roll); - return out; -} - -quat.fromEuler = function(out, vec) { - var yaw = vec[0]; - var pitch = vec[1]; - var roll = vec[2]; - - var C1 = Math.cos(yaw*0.5); - var C2 = Math.cos(pitch*0.5); - var C3 = Math.cos(roll*0.5); - var S1 = Math.sin(yaw*0.5); - var S2 = Math.sin(pitch*0.5); - var S3 = Math.sin(roll*0.5); - - var x = C1*C2*C3 + S1*S2*S3; - var y = S1*C2*C3 - C1*S2*S3; - var z = C1*S2*C3 + S1*C2*S3; - var w = C1*C2*S3 - S1*S2*C3; - - quat.set(out, x,y,z,w ); - quat.normalize(out,out); //necessary? - return out; -} -*/ - -quat.toEuler = function(out, q) -{ - var heading = Math.atan2(2*q[1]*q[3] - 2*q[0]*q[2], 1 - 2*q[1]*q[1] - 2*q[2]*q[2]); - var attitude = Math.asin(2*q[0]*q[1] + 2*q[2]*q[3]); - var bank = Math.atan2(2*q[0]*q[3] - 2*q[1]*q[2], 1 - 2*q[0]*q[0] - 2*q[2]*q[2]); - if(!out) - out = vec3.create(); - vec3.set(out, heading, attitude, bank); - return out; -} - -quat.fromEuler = function(out, vec) { - var heading = vec[0]; - var attitude = vec[1]; - var bank = vec[2]; - - var C1 = Math.cos(heading); //yaw - var C2 = Math.cos(attitude); //pitch - var C3 = Math.cos(bank); //roll - var S1 = Math.sin(heading); - var S2 = Math.sin(attitude); - var S3 = Math.sin(bank); - - var w = Math.sqrt(1.0 + C1 * C2 + C1*C3 - S1 * S2 * S3 + C2*C3) * 0.5; - if(w == 0.0) - { - w = 0.000001; - //quat.set(out, 0,0,0,1 ); - //return out; - } - - var x = (C2 * S3 + C1 * S3 + S1 * S2 * C3) / (4.0 * w); - var y = (S1 * C2 + S1 * C3 + C1 * S2 * S3) / (4.0 * w); - var z = (-S1 * S3 + C1 * S2 * C3 + S2) /(4.0 * w); - quat.set(out, x,y,z,w ); - quat.normalize(out,out); - return out; -}; - - -//not tested -quat.fromMat4 = function(out,m) -{ - var trace = m[0] + m[5] + m[10]; - if ( trace > 0.0 ) - { - var s = Math.sqrt( trace + 1.0 ); - out[3] = s * 0.5;//w - var recip = 0.5 / s; - out[0] = ( m[9] - m[6] ) * recip; //2,1 1,2 - out[1] = ( m[2] - m[8] ) * recip; //0,2 2,0 - out[2] = ( m[4] - m[1] ) * recip; //1,0 0,1 - } - else - { - var i = 0; - if( m[5] > m[0] ) - i = 1; - if( m[10] > m[i*4+i] ) - i = 2; - var j = ( i + 1 ) % 3; - var k = ( j + 1 ) % 3; - var s = Math.sqrt( m[i*4+i] - m[j*4+j] - m[k*4+k] + 1.0 ); - out[i] = 0.5 * s; - var recip = 0.5 / s; - out[3] = ( m[k*4+j] - m[j*4+k] ) * recip;//w - out[j] = ( m[j*4+i] + m[i*4+j] ) * recip; - out[k] = ( m[k*4+i] + m[i*4+k] ) * recip; - } - quat.normalize(out,out); -} - -//col according to common matrix notation, here are stored as rows -vec3.getMat3Column = function(out, m, index ) -{ - out[0] = m[index*3]; - out[1] = m[index*3 + 1]; - out[2] = m[index*3 + 2]; - return out; -} - -mat3.setColumn = function(out, v, index ) -{ - out[index*3] = v[0]; - out[index*3+1] = v[1]; - out[index*3+2] = v[2]; - return out; -} - - -//http://matthias-mueller-fischer.ch/publications/stablePolarDecomp.pdf -//reusing the previous quaternion as an indicator to keep perpendicularity -quat.fromMat3AndQuat = (function(){ - var temp_mat3 = mat3.create(); - var temp_quat = quat.create(); - var Rcol0 = vec3.create(); - var Rcol1 = vec3.create(); - var Rcol2 = vec3.create(); - var Acol0 = vec3.create(); - var Acol1 = vec3.create(); - var Acol2 = vec3.create(); - var RAcross0 = vec3.create(); - var RAcross1 = vec3.create(); - var RAcross2 = vec3.create(); - var omega = vec3.create(); - var axis = mat3.create(); - - return function( q, A, max_iter ) - { - max_iter = max_iter || 25; - for (var iter = 0; iter < max_iter; ++iter) - { - var R = mat3.fromQuat( temp_mat3, q ); - vec3.getMat3Column(Rcol0,R,0); - vec3.getMat3Column(Rcol1,R,1); - vec3.getMat3Column(Rcol2,R,2); - vec3.getMat3Column(Acol0,A,0); - vec3.getMat3Column(Acol1,A,1); - vec3.getMat3Column(Acol2,A,2); - vec3.cross( RAcross0, Rcol0, Acol0 ); - vec3.cross( RAcross1, Rcol1, Acol1 ); - vec3.cross( RAcross2, Rcol2, Acol2 ); - vec3.add( omega, RAcross0, RAcross1 ); - vec3.add( omega, omega, RAcross2 ); - var d = 1.0 / Math.abs( vec3.dot(Rcol0,Acol0) + vec3.dot(Rcol1,Acol1) + vec3.dot(Rcol2,Acol2) ) + 1.0e-9; - vec3.scale( omega, omega, d ); - var w = vec3.length(omega); - if (w < 1.0e-9) - break; - vec3.scale(omega,omega,1/w); //normalize - quat.setAxisAngle( temp_quat, omega, w ); - quat.mul( q, temp_quat, q ); - quat.normalize(q,q); - } - return q; - }; -})(); - -//http://number-none.com/product/IK%20with%20Quaternion%20Joint%20Limits/ -quat.rotateToFrom = (function(){ - var tmp = vec3.create(); - return function(out, v1, v2) - { - out = out || quat.create(); - var axis = vec3.cross(tmp, v1, v2); - var dot = vec3.dot(v1, v2); - if( dot < -1 + 0.01){ - out[0] = 0; - out[1] = 1; - out[2] = 0; - out[3] = 0; - return out; - } - out[0] = axis[0] * 0.5; - out[1] = axis[1] * 0.5; - out[2] = axis[2] * 0.5; - out[3] = (1 + dot) * 0.5; - quat.normalize(out, out); - return out; - } -})(); - -quat.lookAt = (function(){ - var axis = vec3.create(); - - return function( out, forwardVector, up ) - { - var dot = vec3.dot( vec3.FRONT, forwardVector ); - - if ( Math.abs( dot - (-1.0)) < 0.000001 ) - { - out.set( vec3.UP ); - out[3] = Math.PI; - return out; - } - if ( Math.abs(dot - 1.0) < 0.000001 ) - { - return quat.identity( out ); - } - - var rotAngle = Math.acos( dot ); - vec3.cross( axis, vec3.FRONT, forwardVector ); - vec3.normalize( axis, axis ); - quat.setAxisAngle( out, axis, rotAngle ); - return out; - } -})(); - - - - -/** -* @namespace GL -*/ - -/** -* Indexer used to reuse vertices among a mesh -* @class Indexer -* @constructor -*/ -GL.Indexer = function Indexer() { - this.unique = []; - this.indices = []; - this.map = {}; -} -GL.Indexer.prototype = { - add: function(obj) { - var key = JSON.stringify(obj); - if (!(key in this.map)) { - this.map[key] = this.unique.length; - this.unique.push(obj); - } - return this.map[key]; - } -}; - -/** -* A data buffer to be stored in the GPU -* @class Buffer -* @constructor -* @param {Number} target gl.ARRAY_BUFFER, ELEMENT_ARRAY_BUFFER -* @param {ArrayBufferView} data the data in typed-array format -* @param {number} spacing number of numbers per component (3 per vertex, 2 per uvs...), default 3 -* @param {enum} stream_type default gl.STATIC_DRAW (other: gl.DYNAMIC_DRAW, gl.STREAM_DRAW -*/ -GL.Buffer = function Buffer( target, data, spacing, stream_type, gl ) { - if(GL.debug) - console.log("GL.Buffer created"); - - if(gl !== null) - gl = gl || global.gl; - this.gl = gl; - - this.buffer = null; //webgl buffer - this.target = target; //GL.ARRAY_BUFFER, GL.ELEMENT_ARRAY_BUFFER - this.attribute = null; //name of the attribute in the shader ("a_vertex","a_normal","a_coord",...) - - //optional - this.data = data; - this.spacing = spacing || 3; - - if(this.data && this.gl) - this.upload(stream_type); -} - -/** -* binds the buffer to a attrib location -* @method bind -* @param {number} location the location of the shader (from shader.attributes[ name ]) -*/ -GL.Buffer.prototype.bind = function( location, gl ) -{ - gl = gl || this.gl; - - gl.bindBuffer( gl.ARRAY_BUFFER, this.buffer ); - gl.enableVertexAttribArray( location ); - gl.vertexAttribPointer( location, this.spacing, this.buffer.gl_type, false, 0, 0); -} - -/** -* unbinds the buffer from an attrib location -* @method unbind -* @param {number} location the location of the shader -*/ -GL.Buffer.prototype.unbind = function( location, gl ) -{ - gl = gl || this.gl; - gl.disableVertexAttribArray( location ); -} - -/** -* Applies an action to every vertex in this buffer -* @method forEach -* @param {function} callback to be called for every vertex (or whatever is contained in the buffer) -*/ -GL.Buffer.prototype.forEach = function(callback) -{ - var d = this.data; - for (var i = 0, s = this.spacing, l = d.length; i < l; i += s) - { - callback(d.subarray(i,i+s),i); - } - return this; //to concatenate -} - -/** -* Applies a mat4 transform to every triplets in the buffer (assuming they are points) -* No upload is performed (to ensure efficiency in case there are several operations performed) -* @method applyTransform -* @param {mat4} mat -*/ -GL.Buffer.prototype.applyTransform = function(mat) -{ - var d = this.data; - for (var i = 0, s = this.spacing, l = d.length; i < l; i += s) - { - var v = d.subarray(i,i+s); - vec3.transformMat4(v,v,mat); - } - return this; //to concatenate -} - -/** -* Uploads the buffer data (stored in this.data) to the GPU -* @method upload -* @param {number} stream_type default gl.STATIC_DRAW (other: gl.DYNAMIC_DRAW, gl.STREAM_DRAW -*/ -GL.Buffer.prototype.upload = function( stream_type ) { //default gl.STATIC_DRAW (other: gl.DYNAMIC_DRAW, gl.STREAM_DRAW ) - var spacing = this.spacing || 3; //default spacing - var gl = this.gl; - if(!gl) - return; - - if(!this.data) - throw("No data supplied"); - - var data = this.data; - if(!data.buffer) - throw("Buffers must be typed arrays"); - - //I store some stuff inside the WebGL buffer instance, it is supported - this.buffer = this.buffer || gl.createBuffer(); - if(!this.buffer) - return; //if the context is lost... - - this.buffer.length = data.length; - this.buffer.spacing = spacing; - - //store the data format - switch( data.constructor ) - { - case Int8Array: this.buffer.gl_type = gl.BYTE; break; - case Uint8ClampedArray: - case Uint8Array: this.buffer.gl_type = gl.UNSIGNED_BYTE; break; - case Int16Array: this.buffer.gl_type = gl.SHORT; break; - case Uint16Array: this.buffer.gl_type = gl.UNSIGNED_SHORT; break; - case Int32Array: this.buffer.gl_type = gl.INT; break; - case Uint32Array: this.buffer.gl_type = gl.UNSIGNED_INT; break; - case Float32Array: this.buffer.gl_type = gl.FLOAT; break; - default: throw("unsupported buffer type"); - } - - if(this.target == gl.ARRAY_BUFFER && ( this.buffer.gl_type == gl.INT || this.buffer.gl_type == gl.UNSIGNED_INT )) - { - console.warn("WebGL does not support UINT32 or INT32 as vertex buffer types, converting to FLOAT"); - this.buffer.gl_type = gl.FLOAT; - data = new Float32Array(data); - } - - gl.bindBuffer(this.target, this.buffer); - gl.bufferData(this.target, data , stream_type || this.stream_type || gl.STATIC_DRAW); -}; -//legacy -GL.Buffer.prototype.compile = GL.Buffer.prototype.upload; - - -/** -* Assign data to buffer and uploads it (it allows range) -* @method setData -* @param {ArrayBufferView} data in Float32Array format usually -* @param {number} offset offset in bytes -*/ -GL.Buffer.prototype.setData = function( data, offset ) -{ - if(!data.buffer) - throw("Data must be typed array"); - offset = offset || 0; - - if(!this.data) - { - this.data = data; - this.upload(); - return; - } - else if( this.data.length < data.length ) - throw("buffer is not big enough, you cannot set data to a smaller buffer"); - - if(this.data != data) - { - if(this.data.length == data.length) - { - this.data.set( data ); - this.upload(); - return; - } - - //upload just part of it - var new_data_view = new Uint8Array( data.buffer, data.buffer.byteOffset, data.buffer.byteLength ); - var data_view = new Uint8Array( this.data.buffer ); - data_view.set( new_data_view, offset ); - this.uploadRange( offset, new_data_view.length ); - } - -}; - - -/** -* Uploads part of the buffer data (stored in this.data) to the GPU -* @method uploadRange -* @param {number} start offset in bytes -* @param {number} size sizes in bytes -*/ -GL.Buffer.prototype.uploadRange = function(start, size) -{ - if(!this.data) - throw("No data stored in this buffer"); - - var data = this.data; - if(!data.buffer) - throw("Buffers must be typed arrays"); - - //cut fragment to upload (no way to avoid GC here, no function to specify the size in WebGL 1.0, but there is one in WebGL 2.0) - var view = new Uint8Array( this.data.buffer, start, size ); - - var gl = this.gl; - gl.bindBuffer(this.target, this.buffer); - gl.bufferSubData(this.target, start, view ); -}; - -/** -* Clones one buffer (it allows to share the same data between both buffers) -* @method clone -* @param {boolean} share if you want that both buffers share the same data (default false) -* return {GL.Buffer} buffer cloned -*/ -GL.Buffer.prototype.clone = function(share) -{ - var buffer = new GL.Buffer(); - if(share) - { - for(var i in this) - buffer[i] = this[i]; - } - else - { - if(this.target) - buffer.target = this.target; - if(this.gl) - buffer.gl = this.gl; - if(this.spacing) - buffer.spacing = this.spacing; - if(this.data) //clone data - { - buffer.data = new global[ this.data.constructor ]( this.data ); - buffer.upload(); - } - } - return buffer; -} - - -GL.Buffer.prototype.toJSON = function() -{ - if(!this.data) - { - console.error("cannot serialize a mesh without data"); - return null; - } - - return { - data_type: getClassName(this.data), - data: this.data.toJSON(), - target: this.target, - attribute: this.attribute, - spacing: this.spacing - }; -} - -GL.Buffer.prototype.fromJSON = function(o) -{ - var data_type = global[ o.data_type ] || Float32Array; - this.data = new data_type( o.data ); //cloned - this.target = o.target; - this.spacing = o.spacing || 3; - this.attribute = o.attribute; - this.upload( GL.STATIC_DRAW ); -} - -/** -* Deletes the content from the GPU and destroys the handler -* @method delete -*/ -GL.Buffer.prototype.delete = function() -{ - var gl = this.gl; - gl.deleteBuffer( this.buffer ); - this.buffer = null; -} - -/** -* Base class for meshes, it wraps several buffers and some global info like the bounding box -* @class Mesh -* @param {Object} vertexBuffers object with all the vertex streams -* @param {Object} indexBuffers object with all the indices streams -* @param {Object} options -* @param {WebGLContext} gl [Optional] gl context where to create the mesh -* @constructor -*/ -global.Mesh = GL.Mesh = function Mesh( vertexbuffers, indexbuffers, options, gl ) -{ - if(GL.debug) - console.log("GL.Mesh created"); - - if( gl !== null ) - { - gl = gl || global.gl; - this.gl = gl; - } - - //used to avoid problems with resources moving between different webgl context - this._context_id = gl.context_id; - - this.vertexBuffers = {}; - this.indexBuffers = {}; - - //here you can store extra info, like groups, which is an array of { name, start, length, material } - this.info = { - groups: [] - }; - this._bounding = BBox.create(); //here you can store a AABB in BBox format - - if(vertexbuffers || indexbuffers) - this.addBuffers( vertexbuffers, indexbuffers, options ? options.stream_type : null ); - - if(options) - for(var i in options) - this[i] = options[i]; -}; - -Mesh.common_buffers = { - "vertices": { spacing:3, attribute: "a_vertex"}, - "vertices2D": { spacing:2, attribute: "a_vertex2D"}, - "normals": { spacing:3, attribute: "a_normal"}, - "coords": { spacing:2, attribute: "a_coord"}, - "coords1": { spacing:2, attribute: "a_coord1"}, - "coords2": { spacing:2, attribute: "a_coord2"}, - "colors": { spacing:4, attribute: "a_color"}, - "tangents": { spacing:3, attribute: "a_tangent"}, - "bone_indices": { spacing:4, attribute: "a_bone_indices", type: Uint8Array }, - "weights": { spacing:4, attribute: "a_weights"}, - "extra": { spacing:1, attribute: "a_extra"}, - "extra2": { spacing:2, attribute: "a_extra2"}, - "extra3": { spacing:3, attribute: "a_extra3"}, - "extra4": { spacing:4, attribute: "a_extra4"} -}; - -Mesh.default_datatype = Float32Array; - -Object.defineProperty( Mesh.prototype, "bounding", { - set: function(v) - { - if(!v) - return; - if(v.length < 13) - throw("Bounding must use the BBox bounding format of 13 floats: center, halfsize, min, max, radius"); - this._bounding.set(v); - }, - get: function() - { - return this._bounding; - } -}); - -/** -* Adds buffer to mesh -* @method addBuffer -* @param {string} name -* @param {Buffer} buffer -*/ - -Mesh.prototype.addBuffer = function(name, buffer) -{ - if(buffer.target == gl.ARRAY_BUFFER) - this.vertexBuffers[name] = buffer; - else - this.indexBuffers[name] = buffer; - - if(!buffer.attribute) - { - var info = GL.Mesh.common_buffers[name]; - if(info) - buffer.attribute = info.attribute; - } -} - - -/** -* Adds vertex and indices buffers to a mesh -* @method addBuffers -* @param {Object} vertexBuffers object with all the vertex streams -* @param {Object} indexBuffers object with all the indices streams -* @param {enum} stream_type default gl.STATIC_DRAW (other: gl.DYNAMIC_DRAW, gl.STREAM_DRAW ) -*/ -Mesh.prototype.addBuffers = function( vertexbuffers, indexbuffers, stream_type ) -{ - var num_vertices = 0; - - if(this.vertexBuffers["vertices"]) - num_vertices = this.vertexBuffers["vertices"].data.length / 3; - - for(var i in vertexbuffers) - { - var data = vertexbuffers[i]; - if(!data) - continue; - - if( data.constructor == GL.Buffer ) - { - data = data.data; - } - else if( typeof(data[0]) != "number") //linearize: (transform Arrays in typed arrays) - { - var newdata = []; - for (var j = 0, chunk = 10000; j < data.length; j += chunk) { - newdata = Array.prototype.concat.apply(newdata, data.slice(j, j + chunk)); - } - data = newdata; - } - - var stream_info = GL.Mesh.common_buffers[i]; - - //cast to typed float32 if no type is specified - if(data.constructor === Array) - { - var datatype = GL.Mesh.default_datatype; - if(stream_info && stream_info.type) - datatype = stream_info.type; - data = new datatype( data ); - } - - //compute spacing - if(i == "vertices") - num_vertices = data.length / 3; - var spacing = data.length / num_vertices; - if(stream_info && stream_info.spacing) - spacing = stream_info.spacing; - - //add and upload - var attribute = "a_" + i; - if(stream_info && stream_info.attribute) - attribute = stream_info.attribute; - - if( this.vertexBuffers[i] ) - this.updateVertexBuffer( i, attribute, spacing, data, stream_type ); - else - this.createVertexBuffer( i, attribute, spacing, data, stream_type ); - } - - if(indexbuffers) - for(var i in indexbuffers) - { - var data = indexbuffers[i]; - if(!data) continue; - - if( data.constructor == GL.Buffer ) - { - data = data.data; - } - if( typeof(data[0]) != "number") //linearize - { - newdata = []; - for (var i = 0, chunk = 10000; i < data.length; i += chunk) { - newdata = Array.prototype.concat.apply(newdata, data.slice(i, i + chunk)); - } - data = newdata; - } - - //cast to typed - if(data.constructor === Array) - { - var datatype = Uint16Array; - if(num_vertices > 256*256) - datatype = Uint32Array; - data = new datatype( data ); - } - - this.createIndexBuffer( i, data ); - } -} - -/** -* Creates a new empty buffer and attachs it to this mesh -* @method createVertexBuffer -* @param {String} name "vertices","normals"... -* @param {String} attribute name of the stream in the shader "a_vertex","a_normal",... [optional, if omitted is used the common_buffers] -* @param {number} spacing components per vertex [optional, if ommited is used the common_buffers, if not found then uses 3 ] -* @param {ArrayBufferView} buffer_data the data in typed array format [optional, if ommited it created an empty array of getNumVertices() * spacing] -* @param {enum} stream_type [optional, default = gl.STATIC_DRAW (other: gl.DYNAMIC_DRAW, gl.STREAM_DRAW ) ] -*/ - -Mesh.prototype.createVertexBuffer = function( name, attribute, buffer_spacing, buffer_data, stream_type ) { - - var common = GL.Mesh.common_buffers[name]; //generic info about a buffer with the same name - - if (!attribute && common) - attribute = common.attribute; - - if (!attribute) - throw("Buffer added to mesh without attribute name"); - - if (!buffer_spacing && common) - { - if(common && common.spacing) - buffer_spacing = common.spacing; - else - buffer_spacing = 3; - } - - if(!buffer_data) - { - var num = this.getNumVertices(); - if(!num) - throw("Cannot create an empty buffer in a mesh without vertices (vertices are needed to know the size)"); - buffer_data = new (GL.Mesh.default_datatype)(num * buffer_spacing); - } - - if(!buffer_data.buffer) - throw("Buffer data MUST be typed array"); - - //used to ensure the buffers are held in the same gl context as the mesh - var buffer = this.vertexBuffers[name] = new GL.Buffer( gl.ARRAY_BUFFER, buffer_data, buffer_spacing, stream_type, this.gl ); - buffer.name = name; - buffer.attribute = attribute; - - return buffer; -} - -/** -* Updates a vertex buffer -* @method updateVertexBuffer -* @param {String} name the name of the buffer -* @param {String} attribute the name of the attribute in the shader -* @param {number} spacing number of numbers per component (3 per vertex, 2 per uvs...), default 3 -* @param {*} data the array with all the data -* @param {enum} stream_type default gl.STATIC_DRAW (other: gl.DYNAMIC_DRAW, gl.STREAM_DRAW -*/ -Mesh.prototype.updateVertexBuffer = function( name, attribute, buffer_spacing, buffer_data, stream_type ) { - var buffer = this.vertexBuffers[name]; - if(!buffer) - { - console.log("buffer not found: ",name); - return; - } - - if(!buffer_data.length) - return; - - buffer.attribute = attribute; - buffer.spacing = buffer_spacing; - buffer.data = buffer_data; - buffer.upload( stream_type ); -} - - -/** -* Removes a vertex buffer from the mesh -* @method removeVertexBuffer -* @param {String} name "vertices","normals"... -* @param {Boolean} free if you want to remove the data from the GPU -*/ -Mesh.prototype.removeVertexBuffer = function(name, free) { - var buffer = this.vertexBuffers[name]; - if(!buffer) - return; - if(free) - buffer.delete(); - delete this.vertexBuffers[name]; -} - -/** -* Returns a vertex buffer -* @method getVertexBuffer -* @param {String} name of vertex buffer -* @return {Buffer} the buffer -*/ -Mesh.prototype.getVertexBuffer = function(name) -{ - return this.vertexBuffers[name]; -} - - -/** -* Creates a new empty index buffer and attachs it to this mesh -* @method createIndexBuffer -* @param {String} name -* @param {Typed array} data -* @param {enum} stream_type gl.STATIC_DRAW, gl.DYNAMIC_DRAW, gl.STREAM_DRAW -*/ -Mesh.prototype.createIndexBuffer = function(name, buffer_data, stream_type) { - //(target, data, spacing, stream_type, gl) - - //cast to typed - if(buffer_data.constructor === Array) - { - var datatype = Uint16Array; - var vertices = this.vertexBuffers["vertices"]; - if(vertices) - { - var num_vertices = vertices.data.length / 3; - if(num_vertices > 256*256) - datatype = Uint32Array; - buffer_data = new datatype( buffer_data ); - } - } - - var buffer = this.indexBuffers[name] = new GL.Buffer(gl.ELEMENT_ARRAY_BUFFER, buffer_data, 0, stream_type, this.gl ); - return buffer; -} - -/** -* Returns a vertex buffer -* @method getBuffer -* @param {String} name of vertex buffer -* @return {Buffer} the buffer -*/ -Mesh.prototype.getBuffer = function(name) -{ - return this.vertexBuffers[name]; -} - -/** -* Returns a index buffer -* @method getIndexBuffer -* @param {String} name of index buffer -* @return {Buffer} the buffer -*/ -Mesh.prototype.getIndexBuffer = function(name) -{ - return this.indexBuffers[name]; -} - -/** -* Removes an index buffer from the mesh -* @method removeIndexBuffer -* @param {String} name "vertices","normals"... -* @param {Boolean} free if you want to remove the data from the GPU -*/ -Mesh.prototype.removeIndexBuffer = function(name, free) { - var buffer = this.indexBuffers[name]; - if(!buffer) - return; - if(free) - buffer.delete(); - delete this.indexBuffers[name]; -} - - -/** -* Uploads data inside buffers to VRAM. -* @method upload -* @param {number} buffer_type gl.STATIC_DRAW, gl.DYNAMIC_DRAW, gl.STREAM_DRAW -*/ -Mesh.prototype.upload = function(buffer_type) { - for (var attribute in this.vertexBuffers) { - var buffer = this.vertexBuffers[attribute]; - //buffer.data = this[buffer.name]; - buffer.upload(buffer_type); - } - - for (var name in this.indexBuffers) { - var buffer = this.indexBuffers[name]; - //buffer.data = this[name]; - buffer.upload(); - } -} - -//LEGACY, plz remove -Mesh.prototype.compile = Mesh.prototype.upload; - - -Mesh.prototype.deleteBuffers = function() -{ - for(var i in this.vertexBuffers) - { - var buffer = this.vertexBuffers[i]; - buffer.delete(); - } - this.vertexBuffers = {}; - - for(var i in this.indexBuffers) - { - var buffer = this.indexBuffers[i]; - buffer.delete(); - } - this.indexBuffers = {}; -} - -Mesh.prototype.delete = Mesh.prototype.deleteBuffers; - -Mesh.prototype.bindBuffers = function( shader ) -{ - // enable attributes as necessary. - for (var name in this.vertexBuffers) - { - var buffer = this.vertexBuffers[ name ]; - var attribute = buffer.attribute || name; - var location = shader.attributes[ attribute ]; - if (location == null || !buffer.buffer) - continue; - gl.bindBuffer(gl.ARRAY_BUFFER, buffer.buffer); - gl.enableVertexAttribArray(location); - gl.vertexAttribPointer(location, buffer.buffer.spacing, buffer.buffer.gl_type, false, 0, 0); - } -} - -Mesh.prototype.unbindBuffers = function( shader ) -{ - // disable attributes - for (var name in this.vertexBuffers) - { - var buffer = this.vertexBuffers[ name ]; - var attribute = buffer.attribute || name; - var location = shader.attributes[ attribute ]; - if (location == null || !buffer.buffer) - continue; //ignore this buffer - gl.disableVertexAttribArray( shader.attributes[attribute] ); - } -} - -/** -* Creates a clone of the mesh, the datarrays are cloned too -* @method clone -*/ -Mesh.prototype.clone = function( gl ) -{ - var gl = gl || global.gl; - var vbs = {}; - var ibs = {}; - - for(var i in this.vertexBuffers) - { - var b = this.vertexBuffers[i]; - vbs[i] = new b.data.constructor( b.data ); //clone - } - for(var i in this.indexBuffers) - { - var b = this.indexBuffers[i]; - ibs[i] = new b.data.constructor( b.data ); //clone - } - - return new GL.Mesh( vbs, ibs, undefined, gl ); -} - -/** -* Creates a clone of the mesh, but the data-arrays are shared between both meshes (useful for sharing a mesh between contexts) -* @method clone -*/ -Mesh.prototype.cloneShared = function( gl ) -{ - var gl = gl || global.gl; - return new GL.Mesh( this.vertexBuffers, this.indexBuffers, undefined, gl ); -} - - -/** -* Creates an object with the info of the mesh (useful to transfer to workers) -* @method toObject -*/ -Mesh.prototype.toObject = function() -{ - var vbs = {}; - var ibs = {}; - - for(var i in this.vertexBuffers) - { - var b = this.vertexBuffers[i]; - vbs[i] = { - spacing: b.spacing, - data: new b.data.constructor( b.data ) //clone - }; - } - for(var i in this.indexBuffers) - { - var b = this.indexBuffers[i]; - ibs[i] = { - data: new b.data.constructor( b.data ) //clone - } - } - - return { - vertexBuffers: vbs, - indexBuffers: ibs, - info: this.info ? cloneObject( this.info ) : null, - bounding: this._bounding.toJSON() - }; -} - - -Mesh.prototype.toJSON = function() -{ - var r = { - vertexBuffers: {}, - indexBuffers: {}, - info: this.info ? cloneObject( this.info ) : null, - bounding: this._bounding.toJSON() - }; - - for(var i in this.vertexBuffers) - r.vertexBuffers[i] = this.vertexBuffers[i].toJSON(); - - for(var i in this.indexBuffers) - r.indexBuffers[i] = this.indexBuffers[i].toJSON(); - - return r; -} - -Mesh.prototype.fromJSON = function(o) -{ - this.vertexBuffers = {}; - this.indexBuffers = {}; - - for(var i in o.vertexBuffers) - { - if(!o.vertexBuffers[i]) - continue; - var buffer = new GL.Buffer(); - buffer.fromJSON( o.vertexBuffers[i] ); - if(!buffer.attribute && GL.Mesh.common_buffers[i]) - buffer.attribute = GL.Mesh.common_buffers[i].attribute; - this.vertexBuffers[i] = buffer; - } - - for(var i in o.indexBuffers) - { - if(!o.indexBuffers[i]) - continue; - var buffer = new GL.Buffer(); - buffer.fromJSON( o.indexBuffers[i] ); - this.indexBuffers[i] = buffer; - } - - if(o.info) - this.info = cloneObject( o.info ); - if(o.bounding) - this.bounding = o.bounding; //setter does the job -} - - -/** -* Computes some data about the mesh -* @method generateMetadata -*/ -Mesh.prototype.generateMetadata = function() -{ - var metadata = {}; - - var vertices = this.vertexBuffers["vertices"].data; - var triangles = this.indexBuffers["triangles"].data; - - metadata.vertices = vertices.length / 3; - if(triangles) - metadata.faces = triangles.length / 3; - else - metadata.faces = vertices.length / 9; - - metadata.indexed = !!this.metadata.faces; - this.metadata = metadata; -} - -//never tested -/* -Mesh.prototype.draw = function(shader, mode, range_start, range_length) -{ - if(range_length == 0) return; - - // Create and enable attribute pointers as necessary. - var length = 0; - for (var attribute in this.vertexBuffers) { - var buffer = this.vertexBuffers[attribute]; - var location = shader.attributes[attribute] || - gl.getAttribLocation(shader.program, attribute); - if (location == -1 || !buffer.buffer) continue; - shader.attributes[attribute] = location; - gl.bindBuffer(gl.ARRAY_BUFFER, buffer.buffer); - gl.enableVertexAttribArray(location); - gl.vertexAttribPointer(location, buffer.buffer.spacing, gl.FLOAT, false, 0, 0); - length = buffer.buffer.length / buffer.buffer.spacing; - } - - //range rendering - var offset = 0; - if(arguments.length > 3) //render a polygon range - offset = range_start * (this.indexBuffer ? this.indexBuffer.constructor.BYTES_PER_ELEMENT : 1); //in bytes (Uint16 == 2 bytes) - - if(arguments.length > 4) - length = range_length; - else if (this.indexBuffer) - length = this.indexBuffer.buffer.length - offset; - - // Disable unused attribute pointers. - for (var attribute in shader.attributes) { - if (!(attribute in this.vertexBuffers)) { - gl.disableVertexAttribArray(shader.attributes[attribute]); - } - } - - // Draw the geometry. - if (length && (!this.indexBuffer || indexBuffer.buffer)) { - if (this.indexBuffer) { - gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer.buffer); - gl.drawElements(mode, length, gl.UNSIGNED_SHORT, offset); - } else { - gl.drawArrays(mode, offset, length); - } - } - - return this; -} -*/ - -/** -* Creates a new index stream with wireframe -* @method computeWireframe -*/ -Mesh.prototype.computeWireframe = function() { - var index_buffer = this.indexBuffers["triangles"]; - - var vertices = this.vertexBuffers["vertices"].data; - var num_vertices = (vertices.length/3); - - if(!index_buffer) //unindexed - { - var num_triangles = num_vertices / 3; - var buffer = num_vertices > 256*256 ? new Uint32Array( num_triangles * 6 ) : new Uint16Array( num_triangles * 6 ); - for(var i = 0; i < num_vertices; i += 3) - { - buffer[i*2] = i; - buffer[i*2+1] = i+1; - buffer[i*2+2] = i+1; - buffer[i*2+3] = i+2; - buffer[i*2+4] = i+2; - buffer[i*2+5] = i; - } - - } - else //indexed - { - var data = index_buffer.data; - - var indexer = new GL.Indexer(); - for (var i = 0; i < data.length; i+=3) { - var t = data.subarray(i,i+3); - for (var j = 0; j < t.length; j++) { - var a = t[j], b = t[(j + 1) % t.length]; - indexer.add([Math.min(a, b), Math.max(a, b)]); - } - } - - //linearize - var unique = indexer.unique; - var buffer = num_vertices > 256*256 ? new Uint32Array( unique.length * 2 ) : new Uint16Array( unique.length * 2 ); - for(var i = 0, l = unique.length; i < l; ++i) - buffer.set(unique[i],i*2); - } - - //create stream - this.createIndexBuffer('wireframe', buffer); - return this; -} - - -/** -* Multiplies every normal by -1 and uploads it -* @method flipNormals -* @param {enum} stream_type default gl.STATIC_DRAW (other: gl.DYNAMIC_DRAW, gl.STREAM_DRAW) -*/ -Mesh.prototype.flipNormals = function( stream_type ) { - var normals_buffer = this.vertexBuffers["normals"]; - if(!normals_buffer) - return; - var data = normals_buffer.data; - var l = data.length; - for(var i = 0; i < l; ++i) - data[i] *= -1; - normals_buffer.upload( stream_type ); - - //reverse indices too - if( !this.indexBuffers["triangles"] ) - this.computeIndices(); //create indices - - var triangles_buffer = this.indexBuffers["triangles"]; - var data = triangles_buffer.data; - var l = data.length; - for(var i = 0; i < l; i += 3) - { - var tmp = data[i]; - data[i] = data[i+1]; - data[i+1] = tmp; - //the [i+2] stays the same - } - triangles_buffer.upload( stream_type ); -} - - -/** -* Compute indices for a mesh where vertices are shared -* @method computeIndices -*/ -Mesh.prototype.computeIndices = function() { - - //cluster by distance - var new_vertices = []; - var new_normals = []; - var new_coords = []; - - var indices = []; - - var old_vertices_buffer = this.vertexBuffers["vertices"]; - var old_normals_buffer = this.vertexBuffers["normals"]; - var old_coords_buffer = this.vertexBuffers["coords"]; - - var old_vertices_data = old_vertices_buffer.data; - - var old_normals_data = null; - if( old_normals_buffer ) - old_normals_data = old_normals_buffer.data; - - var old_coords_data = null; - if( old_coords_buffer ) - old_coords_data = old_coords_buffer.data; - - - var indexer = {}; - - var l = old_vertices_data.length / 3; - for(var i = 0; i < l; ++i) - { - var v = old_vertices_data.subarray( i*3,(i+1)*3 ); - var key = (v[0] * 1000)|0; - - //search in new_vertices - var j = 0; - var candidates = indexer[key]; - if(candidates) - { - var l2 = candidates.length; - for(; j < l2; j++) - { - var v2 = new_vertices[ candidates[j] ]; - //same vertex - if( vec3.sqrDist( v, v2 ) < 0.01 ) - { - indices.push(j); - break; - } - } - } - - /* - var l2 = new_vertices.length; - for(var j = 0; j < l2; j++) - { - //same vertex - if( vec3.sqrDist( v, new_vertices[j] ) < 0.001 ) - { - indices.push(j); - break; - } - } - */ - - if(candidates && j != l2) - continue; - - var index = j; - new_vertices.push(v); - if( indexer[ key ] ) - indexer[ key ].push( index ); - else - indexer[ key ] = [ index ]; - - if(old_normals_data) - new_normals.push( old_normals_data.subarray(i*3, (i+1)*3) ); - if(old_coords_data) - new_coords.push( old_coords_data.subarray(i*2, (i+1)*2) ); - indices.push(index); - } - - this.vertexBuffers = {}; //erase all - - //new buffers - this.createVertexBuffer( 'vertices', GL.Mesh.common_buffers["vertices"].attribute, 3, linearizeArray( new_vertices ) ); - if(old_normals_data) - this.createVertexBuffer( 'normals', GL.Mesh.common_buffers["normals"].attribute, 3, linearizeArray( new_normals ) ); - if(old_coords_data) - this.createVertexBuffer( 'coords', GL.Mesh.common_buffers["coords"].attribute, 2, linearizeArray( new_coords ) ); - - this.createIndexBuffer( "triangles", indices ); -} - -/** -* Breaks the indices -* @method explodeIndices -*/ -Mesh.prototype.explodeIndices = function( buffer_name ) { - - buffer_name = buffer_name || "triangles"; - - var indices_buffer = this.getIndexBuffer( buffer_name ); - if(!indices_buffer) - return; - - var indices = indices_buffer.data; - - var new_buffers = {}; - for(var i in this.vertexBuffers) - { - var info = GL.Mesh.common_buffers[i]; - new_buffers[i] = new (info.type || Float32Array)( info.spacing * indices.length ); - } - - for(var i = 0, l = indices.length; i < l; ++i) - { - var index = indices[i]; - for(var j in this.vertexBuffers) - { - var buffer = this.vertexBuffers[j]; - var info = GL.Mesh.common_buffers[j]; - var spacing = buffer.spacing || info.spacing; - var new_buffer = new_buffers[j]; - new_buffer.set( buffer.data.subarray( index*spacing, index*spacing + spacing ), i*spacing ); - } - } - - /* - //cluster by distance - var new_vertices = new Float32Array(indices.length * 3); - var new_normals = null; - var new_coords = null; - - var old_vertices_buffer = this.vertexBuffers["vertices"]; - var old_vertices = old_vertices_buffer.data; - - var old_normals_buffer = this.vertexBuffers["normals"]; - var old_normals = null; - if(old_normals_buffer) - { - old_normals = old_normals_buffer.data; - new_normals = new Float32Array(indices.length * 3); - } - - var old_coords_buffer = this.vertexBuffers["coords"]; - var old_coords = null; - if( old_coords_buffer ) - { - old_coords = old_coords_buffer.data; - new_coords = new Float32Array(indices.length * 2); - } - - for(var i = 0, l = indices.length; i < l; ++i) - { - var index = indices[i]; - new_vertices.set( old_vertices.subarray( index*3, index*3 + 3 ), i*3 ); - if(old_normals) - new_normals.set( old_normals.subarray( index*3, index*3 + 3 ), i*3 ); - if(old_coords) - new_coords.set( old_coords.subarray( index*2, index*2 + 2 ), i*2 ); - } - - //erase all - this.vertexBuffers = {}; - - //new buffers - this.createVertexBuffer( 'vertices', GL.Mesh.common_buffers["vertices"].attribute, 3, new_vertices ); - if(new_normals) - this.createVertexBuffer( 'normals', GL.Mesh.common_buffers["normals"].attribute, 3, new_normals ); - if(new_coords) - this.createVertexBuffer( 'coords', GL.Mesh.common_buffers["coords"].attribute, 2, new_coords ); - */ - - for(var i in new_buffers) - { - var old = this.vertexBuffers[i]; - this.createVertexBuffer( i, old.attribute, old.spacing, new_buffers[i] ); - } - - delete this.indexBuffers[ buffer_name ]; -} - - - -/** -* Creates a stream with the normals -* @method computeNormals -* @param {enum} stream_type default gl.STATIC_DRAW (other: gl.DYNAMIC_DRAW, gl.STREAM_DRAW) -*/ -Mesh.prototype.computeNormals = function( stream_type ) { - var vertices_buffer = this.vertexBuffers["vertices"]; - if(!vertices_buffer) - return console.error("Cannot compute normals of a mesh without vertices"); - - var vertices = this.vertexBuffers["vertices"].data; - var num_vertices = vertices.length / 3; - - //create because it is faster than filling it with zeros - var normals = new Float32Array( vertices.length ); - - var triangles = null; - if(this.indexBuffers["triangles"]) - triangles = this.indexBuffers["triangles"].data; - - var temp = GL.temp_vec3; - var temp2 = GL.temp2_vec3; - - var i1,i2,i3,v1,v2,v3,n1,n2,n3; - - //compute the plane normal - var l = triangles ? triangles.length : vertices.length; - for (var a = 0; a < l; a+=3) - { - if(triangles) - { - i1 = triangles[a]; - i2 = triangles[a+1]; - i3 = triangles[a+2]; - - v1 = vertices.subarray(i1*3,i1*3+3); - v2 = vertices.subarray(i2*3,i2*3+3); - v3 = vertices.subarray(i3*3,i3*3+3); - - n1 = normals.subarray(i1*3,i1*3+3); - n2 = normals.subarray(i2*3,i2*3+3); - n3 = normals.subarray(i3*3,i3*3+3); - } - else - { - v1 = vertices.subarray(a*3,a*3+3); - v2 = vertices.subarray(a*3+3,a*3+6); - v3 = vertices.subarray(a*3+6,a*3+9); - - n1 = normals.subarray(a*3,a*3+3); - n2 = normals.subarray(a*3+3,a*3+6); - n3 = normals.subarray(a*3+6,a*3+9); - } - - vec3.sub( temp, v2, v1 ); - vec3.sub( temp2, v3, v1 ); - vec3.cross( temp, temp, temp2 ); - vec3.normalize(temp,temp); - - //save - vec3.add( n1, n1, temp ); - vec3.add( n2, n2, temp ); - vec3.add( n3, n3, temp ); - } - - //normalize if vertices are shared - if(triangles) - for (var a = 0, l = normals.length; a < l; a+=3) - { - var n = normals.subarray(a,a+3); - vec3.normalize(n,n); - } - - var normals_buffer = this.vertexBuffers["normals"]; - - if(normals_buffer) - { - normals_buffer.data = normals; - normals_buffer.upload( stream_type ); - } - else - return this.createVertexBuffer('normals', GL.Mesh.common_buffers["normals"].attribute, 3, normals ); - return normals_buffer; -} - - -/** -* Creates a new stream with the tangents -* @method computeTangents -*/ -Mesh.prototype.computeTangents = function() -{ - var vertices_buffer = this.vertexBuffers["vertices"]; - if(!vertices_buffer) - return console.error("Cannot compute tangents of a mesh without vertices"); - - var normals_buffer = this.vertexBuffers["normals"]; - if(!normals_buffer) - return console.error("Cannot compute tangents of a mesh without normals"); - - var uvs_buffer = this.vertexBuffers["coords"]; - if(!uvs_buffer) - return console.error("Cannot compute tangents of a mesh without uvs"); - - var triangles_buffer = this.indexBuffers["triangles"]; - if(!triangles_buffer) - return console.error("Cannot compute tangents of a mesh without indices"); - - var vertices = vertices_buffer.data; - var normals = normals_buffer.data; - var uvs = uvs_buffer.data; - var triangles = triangles_buffer.data; - - if(!vertices || !normals || !uvs) return; - - var num_vertices = vertices.length / 3; - - var tangents = new Float32Array(num_vertices * 4); - - //temporary (shared) - var tan1 = new Float32Array(num_vertices*3*2); - var tan2 = tan1.subarray(num_vertices*3); - - var a,l; - var sdir = vec3.create(); - var tdir = vec3.create(); - var temp = vec3.create(); - var temp2 = vec3.create(); - - for (a = 0, l = triangles.length; a < l; a+=3) - { - var i1 = triangles[a]; - var i2 = triangles[a+1]; - var i3 = triangles[a+2]; - - var v1 = vertices.subarray(i1*3,i1*3+3); - var v2 = vertices.subarray(i2*3,i2*3+3); - var v3 = vertices.subarray(i3*3,i3*3+3); - - var w1 = uvs.subarray(i1*2,i1*2+2); - var w2 = uvs.subarray(i2*2,i2*2+2); - var w3 = uvs.subarray(i3*2,i3*2+2); - - var x1 = v2[0] - v1[0]; - var x2 = v3[0] - v1[0]; - var y1 = v2[1] - v1[1]; - var y2 = v3[1] - v1[1]; - var z1 = v2[2] - v1[2]; - var z2 = v3[2] - v1[2]; - - var s1 = w2[0] - w1[0]; - var s2 = w3[0] - w1[0]; - var t1 = w2[1] - w1[1]; - var t2 = w3[1] - w1[1]; - - var r; - var den = (s1 * t2 - s2 * t1); - if ( Math.abs(den) < 0.000000001 ) - r = 0.0; - else - r = 1.0 / den; - - vec3.copy(sdir, [(t2 * x1 - t1 * x2) * r, (t2 * y1 - t1 * y2) * r, (t2 * z1 - t1 * z2) * r] ); - vec3.copy(tdir, [(s1 * x2 - s2 * x1) * r, (s1 * y2 - s2 * y1) * r, (s1 * z2 - s2 * z1) * r] ); - - vec3.add( tan1.subarray( i1*3, i1*3+3), tan1.subarray( i1*3, i1*3+3), sdir); - vec3.add( tan1.subarray( i2*3, i2*3+3), tan1.subarray( i2*3, i2*3+3), sdir); - vec3.add( tan1.subarray( i3*3, i3*3+3), tan1.subarray( i3*3, i3*3+3), sdir); - - vec3.add( tan2.subarray( i1*3, i1*3+3), tan2.subarray( i1*3, i1*3+3), tdir); - vec3.add( tan2.subarray( i2*3, i2*3+3), tan2.subarray( i2*3, i2*3+3), tdir); - vec3.add( tan2.subarray( i3*3, i3*3+3), tan2.subarray( i3*3, i3*3+3), tdir); - } - - for (a = 0, l = vertices.length; a < l; a+=3) - { - var n = normals.subarray(a,a+3); - var t = tan1.subarray(a,a+3); - - // Gram-Schmidt orthogonalize - vec3.subtract(temp, t, vec3.scale(temp, n, vec3.dot(n, t) ) ); - vec3.normalize(temp,temp); - - // Calculate handedness - var w = ( vec3.dot( vec3.cross(temp2, n, t), tan2.subarray(a,a+3) ) < 0.0) ? -1.0 : 1.0; - tangents.set([temp[0], temp[1], temp[2], w],(a/3)*4); - } - - this.createVertexBuffer('tangents', Mesh.common_buffers["tangents"].attribute, 4, tangents ); -} - -/** -* Creates texture coordinates using a triplanar aproximation -* @method computeTextureCoordinates -*/ -Mesh.prototype.computeTextureCoordinates = function( stream_type ) -{ - var vertices_buffer = this.vertexBuffers["vertices"]; - if(!vertices_buffer) - return console.error("Cannot compute uvs of a mesh without vertices"); - - this.explodeIndices( "triangles" ); - - vertices_buffer = this.vertexBuffers["vertices"]; - var vertices = vertices_buffer.data; - var num_vertices = vertices.length / 3; - - var uvs_buffer = this.vertexBuffers["coords"]; - var uvs = new Float32Array( num_vertices * 2 ); - - var triangles_buffer = this.indexBuffers["triangles"]; - var triangles = null; - if( triangles_buffer ) - triangles = triangles_buffer.data; - - var plane_normal = vec3.create(); - var side1 = vec3.create(); - var side2 = vec3.create(); - - var bbox = this.getBoundingBox(); - var bboxcenter = BBox.getCenter( bbox ); - var bboxhs = vec3.create(); - bboxhs.set( BBox.getHalfsize( bbox ) ); //careful, this is a reference - vec3.scale( bboxhs, bboxhs, 2 ); - - var num = triangles ? triangles.length : vertices.length/3; - - for (var a = 0; a < num; a+=3) - { - if(triangles) - { - var i1 = triangles[a]; - var i2 = triangles[a+1]; - var i3 = triangles[a+2]; - - var v1 = vertices.subarray(i1*3,i1*3+3); - var v2 = vertices.subarray(i2*3,i2*3+3); - var v3 = vertices.subarray(i3*3,i3*3+3); - - var uv1 = uvs.subarray(i1*2,i1*2+2); - var uv2 = uvs.subarray(i2*2,i2*2+2); - var uv3 = uvs.subarray(i3*2,i3*2+2); - } - else - { - var v1 = vertices.subarray((a)*3,(a)*3+3); - var v2 = vertices.subarray((a+1)*3,(a+1)*3+3); - var v3 = vertices.subarray((a+2)*3,(a+2)*3+3); - - var uv1 = uvs.subarray((a)*2,(a)*2+2); - var uv2 = uvs.subarray((a+1)*2,(a+1)*2+2); - var uv3 = uvs.subarray((a+2)*2,(a+2)*2+2); - } - - vec3.sub(side1, v1, v2 ); - vec3.sub(side2, v1, v3 ); - vec3.cross( plane_normal, side1, side2 ); - //vec3.normalize( plane_normal, plane_normal ); //not necessary - - plane_normal[0] = Math.abs( plane_normal[0] ); - plane_normal[1] = Math.abs( plane_normal[1] ); - plane_normal[2] = Math.abs( plane_normal[2] ); - - if( plane_normal[0] > plane_normal[1] && plane_normal[0] > plane_normal[2]) - { - //X - uv1[0] = (v1[2] - bboxcenter[2]) / bboxhs[2]; - uv1[1] = (v1[1] - bboxcenter[1]) / bboxhs[1]; - uv2[0] = (v2[2] - bboxcenter[2]) / bboxhs[2]; - uv2[1] = (v2[1] - bboxcenter[1]) / bboxhs[1]; - uv3[0] = (v3[2] - bboxcenter[2]) / bboxhs[2]; - uv3[1] = (v3[1] - bboxcenter[1]) / bboxhs[1]; - } - else if ( plane_normal[1] > plane_normal[2]) - { - //Y - uv1[0] = (v1[0] - bboxcenter[0]) / bboxhs[0]; - uv1[1] = (v1[2] - bboxcenter[2]) / bboxhs[2]; - uv2[0] = (v2[0] - bboxcenter[0]) / bboxhs[0]; - uv2[1] = (v2[2] - bboxcenter[2]) / bboxhs[2]; - uv3[0] = (v3[0] - bboxcenter[0]) / bboxhs[0]; - uv3[1] = (v3[2] - bboxcenter[2]) / bboxhs[2]; - } - else - { - //Z - uv1[0] = (v1[0] - bboxcenter[0]) / bboxhs[0]; - uv1[1] = (v1[1] - bboxcenter[1]) / bboxhs[1]; - uv2[0] = (v2[0] - bboxcenter[0]) / bboxhs[0]; - uv2[1] = (v2[1] - bboxcenter[1]) / bboxhs[1]; - uv3[0] = (v3[0] - bboxcenter[0]) / bboxhs[0]; - uv3[1] = (v3[1] - bboxcenter[1]) / bboxhs[1]; - } - } - - if(uvs_buffer) - { - uvs_buffer.data = uvs; - uvs_buffer.upload( stream_type ); - } - else - this.createVertexBuffer('coords', Mesh.common_buffers["coords"].attribute, 2, uvs ); -} - - -/** -* Computes the number of vertices -* @method getVertexNumber -*/ -Mesh.prototype.getNumVertices = function() { - var b = this.vertexBuffers["vertices"]; - if(!b) - return 0; - return b.data.length / b.spacing; -} - -/** -* Computes the number of triangles (takes into account indices) -* @method getNumTriangles -*/ -Mesh.prototype.getNumTriangles = function() { - var indices_buffer = this.getIndexBuffer("triangles"); - if(!indices_buffer) - return this.getNumVertices() / 3; - return indices_buffer.data.length / 3; -} - - -/** -* Computes bounding information -* @method Mesh.computeBoundingBox -* @param {typed Array} vertices array containing all the vertices -* @param {BBox} bb where to store the bounding box -* @param {Array} mask [optional] to specify which vertices must be considered when creating the bbox, used to create BBox of a submesh -*/ -Mesh.computeBoundingBox = function( vertices, bb, mask ) { - - if(!vertices) - return; - - var start = 0; - - if(mask) - { - for(var i = 0; i < mask.length; ++i) - if( mask[i] ) - { - start = i; - break; - } - if(start == mask.length) - { - console.warn("mask contains only zeros, no vertices marked"); - return; - } - } - - var min = vec3.clone( vertices.subarray( start*3, start*3 + 3) ); - var max = vec3.clone( vertices.subarray( start*3, start*3 + 3) ); - var v; - - for(var i = start*3; i < vertices.length; i+=3) - { - if( mask && !mask[i/3] ) - continue; - v = vertices.subarray(i,i+3); - vec3.min( min,v, min); - vec3.max( max,v, max); - } - - if( isNaN(min[0]) || isNaN(min[1]) || isNaN(min[2]) || - isNaN(max[0]) || isNaN(max[1]) || isNaN(max[2]) ) - { - min[0] = min[1] = min[2] = 0; - max[0] = max[1] = max[2] = 0; - console.warn("Warning: GL.Mesh has NaN values in vertices"); - } - - var center = vec3.add( vec3.create(), min,max ); - vec3.scale( center, center, 0.5); - var half_size = vec3.subtract( vec3.create(), max, center ); - - return BBox.setCenterHalfsize( bb || BBox.create(), center, half_size ); -} - -/** -* returns the bounding box, if it is not computed, then computes it -* @method getBoundingBox -* @return {BBox} bounding box -*/ -Mesh.prototype.getBoundingBox = function() -{ - if(this._bounding) - return this._bounding; - - this.updateBoundingBox(); - return this._bounding; -} - -/** -* Update bounding information of this mesh -* @method updateBoundingBox -*/ -Mesh.prototype.updateBoundingBox = function() { - var vertices = this.vertexBuffers["vertices"]; - if(!vertices) - return; - GL.Mesh.computeBoundingBox( vertices.data, this._bounding ); - if(this.info && this.info.groups && this.info.groups.length) - this.computeGroupsBoundingBoxes(); -} - -/** -* Update bounding information for every group submesh -* @method computeGroupsBoundingBoxes -*/ -Mesh.prototype.computeGroupsBoundingBoxes = function() -{ - var indices = null; - var indices_buffer = this.getIndexBuffer("triangles"); - if( indices_buffer ) - indices = indices_buffer.data; - - var vertices_buffer = this.getVertexBuffer("vertices"); - if(!vertices_buffer) - return false; - var vertices = vertices_buffer.data; - if(!vertices.length) - return false; - - var groups = this.info.groups; - for(var i = 0; i < groups.length; ++i) - { - var group = groups[i]; - group.bounding = group.bounding || BBox.create(); - var submesh_vertices = null; - if( indices ) - { - var mask = new Uint8Array( vertices.length / 3 ); - var s = group.start; - for( var j = 0, l = group.length; j < l; j += 3 ) - { - mask[ indices[s+j] ] = 1; - mask[ indices[s+j+1] ] = 1; - mask[ indices[s+j+2] ] = 1; - } - GL.Mesh.computeBoundingBox( vertices, group.bounding, mask ); - } - else - { - submesh_vertices = vertices.subarray( group.start * 3, ( group.start + group.length) * 3 ); - GL.Mesh.computeBoundingBox( submesh_vertices, group.bounding ); - } - } - return true; -} - - - -/** -* forces a bounding box to be set -* @method setBoundingBox -* @param {vec3} center center of the bounding box -* @param {vec3} half_size vector from the center to positive corner -*/ -Mesh.prototype.setBoundingBox = function( center, half_size ) { - BBox.setCenterHalfsize( this._bounding, center, half_size ); -} - - -/** -* Remove all local memory from the streams (leaving it only in the VRAM) to save RAM -* @method freeData -*/ -Mesh.prototype.freeData = function() -{ - for (var attribute in this.vertexBuffers) - { - this.vertexBuffers[attribute].data = null; - delete this[ this.vertexBuffers[attribute].name ]; //delete from the mesh itself - } - for (var name in this.indexBuffers) - { - this.indexBuffers[name].data = null; - delete this[ this.indexBuffers[name].name ]; //delete from the mesh itself - } -} - -Mesh.prototype.configure = function( o, options ) -{ - var vertex_buffers = {}; - var index_buffers = {}; - options = options || {}; - - for(var j in o) - { - if(!o[j]) - continue; - - if(j == "vertexBuffers" || j == "vertex_buffers") //HACK: legacy code - { - for(i in o[j]) - vertex_buffers[i] = o[j][i]; - continue; - } - - if(j == "indexBuffers" || j == "index_buffers") - { - for(i in o[j]) - index_buffers[i] = o[j][i]; - continue; - } - - if(j == "indices" || j == "lines" || j == "wireframe" || j == "triangles") - index_buffers[j] = o[j]; - else if( GL.Mesh.common_buffers[j]) - vertex_buffers[j] = o[j]; - else //global data like bounding, info of groups, etc - { - options[j] = o[j]; - } - } - - this.addBuffers( vertex_buffers, index_buffers, options.stream_type ); - - for(var i in options) - this[i] = options[i]; - - if(!options.bounding) - this.updateBoundingBox(); -} - -/** -* Returns the amount of memory used by this mesh in bytes (sum of all buffers) -* @method getMemory -* @return {number} bytes -*/ -Mesh.prototype.totalMemory = function() -{ - var num = 0|0; - - for (var name in this.vertexBuffers) - num += this.vertexBuffers[name].data.buffer.byteLength; - for (var name in this.indexBuffers) - num += this.indexBuffers[name].data.buffer.byteLength; - - return num; -} - -Mesh.prototype.slice = function(start, length) -{ - var new_vertex_buffers = {}; - - var indices_buffer = this.indexBuffers["triangles"]; - if(!indices_buffer) - { - console.warn("splice in not indexed not supported yet"); - return null; - } - - var indices = indices_buffer.data; - - var new_triangles = []; - var reindex = new Int32Array( indices.length ); - reindex.fill(-1); - - var end = start + length; - if(end >= indices.length) - end = indices.length; - - var last_index = 0; - for(var j = start; j < end; ++j) - { - var index = indices[j]; - if( reindex[index] != -1 ) - { - new_triangles.push(reindex[index]); - continue; - } - - //new vertex - var new_index = last_index++; - reindex[index] = new_index; - new_triangles.push(new_index); - - for( var i in this.vertexBuffers ) - { - var buffer = this.vertexBuffers[i]; - var data = buffer.data; - var spacing = buffer.spacing; - if(!new_vertex_buffers[i]) - new_vertex_buffers[i] = []; - var new_buffer = new_vertex_buffers[i]; - for(var k = 0; k < spacing; ++k) - new_buffer.push( data[k + index*spacing] ); - } - } - - var new_mesh = new GL.Mesh( new_vertex_buffers, {triangles: new_triangles}, null,gl); - new_mesh.updateBoundingBox(); - return new_mesh; -} - - -/** -* returns a low poly version of the mesh that takes much less memory (but breaks tiling of uvs and smoothing groups) -* @method simplify -* @return {Mesh} simplified mesh -*/ -Mesh.prototype.simplify = function() -{ - //compute bounding box - var bb = this.getBoundingBox(); - var min = BBox.getMin( bb ); - var halfsize = BBox.getHalfsize( bb ); - var range = vec3.scale( vec3.create(), halfsize, 2 ); - - var newmesh = new GL.Mesh(); - var temp = vec3.create(); - - for(var i in this.vertexBuffers) - { - //take every vertex and normalize it to the bounding box - var buffer = this.vertexBuffers[i]; - var data = buffer.data; - - var new_data = new Float32Array( data.length ); - - if(i == "vertices") - { - for(var j = 0, l = data.length; j < l; j+=3 ) - { - var v = data.subarray(j,j+3); - vec3.sub( temp, v, min ); - vec3.div( temp, temp, range ); - temp[0] = Math.round(temp[0] * 256) / 256; - temp[1] = Math.round(temp[1] * 256) / 256; - temp[2] = Math.round(temp[2] * 256) / 256; - vec3.mul( temp, temp, range ); - vec3.add( temp, temp, min ); - new_data.set( temp, j ); - } - } - else - { - } - - newmesh.addBuffer(); - } - - //search for repeated vertices - //compute the average normal and coord - //reindex the triangles - //return simplified mesh -} - -/** -* Static method for the class Mesh to create a mesh from a list of common streams -* @method Mesh.load -* @param {Object} buffers object will all the buffers -* @param {Object} options [optional] -* @param {Mesh} output_mesh [optional] mesh to store the mesh, otherwise is created -* @param {WebGLContext} gl [optional] if omitted, the global.gl is used -*/ -Mesh.load = function( buffers, options, output_mesh, gl ) { - options = options || {}; - if(options.no_gl) - gl = null; - var mesh = output_mesh || new GL.Mesh(null,null,null,gl); - mesh.configure( buffers, options ); - return mesh; -} - -/** -* Returns a mesh with all the meshes merged (you can apply transforms individually to every buffer) -* @method Mesh.mergeMeshes -* @param {Array} meshes array containing object like { mesh:, matrix:, texture_matrix: } -* @param {Object} options { only_data: to get the mesh data without uploading it } -* @return {GL.Mesh|Object} the mesh in GL.Mesh format or Object format (if options.only_data is true) -*/ -Mesh.mergeMeshes = function( meshes, options ) -{ - options = options || {}; - - var vertex_buffers = {}; - var index_buffers = {}; - var offsets = {}; //tells how many positions indices must be offseted - var vertex_offsets = []; - var current_vertex_offset = 0; - var groups = []; - - //vertex buffers - //compute size - for(var i = 0; i < meshes.length; ++i) - { - var mesh_info = meshes[i]; - var mesh = mesh_info.mesh; - var offset = current_vertex_offset; - vertex_offsets.push( offset ); - var length = mesh.vertexBuffers["vertices"].data.length / 3; - current_vertex_offset += length; - - for(var j in mesh.vertexBuffers) - { - if(!vertex_buffers[j]) - vertex_buffers[j] = mesh.vertexBuffers[j].data.length; - else - vertex_buffers[j] += mesh.vertexBuffers[j].data.length; - } - - for(var j in mesh.indexBuffers) - { - if(!index_buffers[j]) - index_buffers[j] = mesh.indexBuffers[j].data.length; - else - index_buffers[j] += mesh.indexBuffers[j].data.length; - } - - //groups - var group = { - name: "mesh_" + i, - start: offset, - length: length, - material: "" - }; - - groups.push( group ); - } - - //allocate - for(var j in vertex_buffers) - { - var datatype = options[j]; - if(datatype === null) - { - delete vertex_buffers[j]; - continue; - } - - if(!datatype) - datatype = Float32Array; - - vertex_buffers[j] = new datatype( vertex_buffers[j] ); - offsets[j] = 0; - } - - for(var j in index_buffers) - { - index_buffers[j] = new Uint32Array( index_buffers[j] ); - offsets[j] = 0; - } - - //store - for(var i = 0; i < meshes.length; ++i) - { - var mesh_info = meshes[i]; - var mesh = mesh_info.mesh; - var offset = 0; - var length = 0; - - for(var j in mesh.vertexBuffers) - { - if(!vertex_buffers[j]) - continue; - - if(j == "vertices") - length = mesh.vertexBuffers[j].data.length / 3; - - vertex_buffers[j].set( mesh.vertexBuffers[j].data, offsets[j] ); - - //apply transform - if(mesh_info[ j + "_matrix"] ) - { - var matrix = mesh_info[ j + "_matrix" ]; - if(matrix.length == 16) - apply_transform( vertex_buffers[j], offsets[j], mesh.vertexBuffers[j].data.length, matrix ) - else if(matrix.length == 9) - apply_transform2D( vertex_buffers[j], offsets[j], mesh.vertexBuffers[j].data.length, matrix ) - } - - offsets[j] += mesh.vertexBuffers[j].data.length; - } - - for(var j in mesh.indexBuffers) - { - index_buffers[j].set( mesh.indexBuffers[j].data, offsets[j] ); - apply_offset( index_buffers[j], offsets[j], mesh.indexBuffers[j].data.length, vertex_offsets[i] ); - offsets[j] += mesh.indexBuffers[j].data.length; - } - } - - //useful functions - function apply_transform( array, start, length, matrix ) - { - var l = start + length; - for(var i = start; i < l; i+=3) - { - var v = array.subarray(i,i+3); - vec3.transformMat4( v, v, matrix ); - } - } - - function apply_transform2D( array, start, length, matrix ) - { - var l = start + length; - for(var i = start; i < l; i+=2) - { - var v = array.subarray(i,i+2); - vec2.transformMat3( v, v, matrix ); - } - } - - function apply_offset( array, start, length, offset ) - { - var l = start + length; - for(var i = start; i < l; ++i) - array[i] += offset; - } - - var extra = { info: { groups: groups } }; - - //return - if( typeof(gl) != "undefined" || options.only_data ) - return new GL.Mesh( vertex_buffers,index_buffers, extra ); - return { - vertexBuffers: vertex_buffers, - indexBuffers: index_buffers, - info: { groups: groups } - }; -} - - - -//Here we store all basic mesh parsers (OBJ, STL) and encoders -Mesh.parsers = {}; -Mesh.encoders = {}; -Mesh.binary_file_formats = {}; //extensions that must be downloaded in binary -Mesh.compressors = {}; //used to compress binary meshes -Mesh.decompressors = {}; //used to decompress binary meshes - -/** -* Returns am empty mesh and loads a mesh and parses it using the Mesh.parsers, by default only OBJ is supported -* @method Mesh.fromOBJ -* @param {Array} meshes array containing all the meshes -*/ -Mesh.fromURL = function(url, on_complete, gl, options) -{ - options = options || {}; - gl = gl || global.gl; - - var mesh = new GL.Mesh(undefined,undefined,undefined,gl); - mesh.ready = false; - - var pos = url.lastIndexOf("."); - var extension = url.substr(pos+1).toLowerCase(); - options.binary = Mesh.binary_file_formats[ extension ]; - - HttpRequest( url, null, function(data) { - mesh.parse( data, extension ); - delete mesh["ready"]; - if(on_complete) - on_complete.call(mesh,mesh, url); - }, function(err){ - if(on_complete) - on_complete(null); - }, options ); - return mesh; -} - -/** -* given some data an information about the format, it search for a parser in Mesh.parsers and tries to extract the mesh information -* Only obj supported now -* @method parse -* @param {*} data could be string or ArrayBuffer -* @param {String} format parser file format name (p.e. "obj") -* @return {?} depending on the parser -*/ -Mesh.prototype.parse = function( data, format ) -{ - format = format.toLowerCase(); - var parser = GL.Mesh.parsers[ format ]; - if(parser) - return parser.call(null, data, {mesh: this}); - throw("GL.Mesh.parse: no parser found for format " + format ); -} - -/** -* It returns the mesh data encoded in the format specified -* Only obj supported now -* @method encode -* @param {String} format to encode the data to (p.e. "obj") -* @return {?} String with the info -*/ -Mesh.prototype.encode = function( format, options ) -{ - format = format.toLowerCase(); - var encoder = GL.Mesh.encoders[ format ]; - if(encoder) - return encoder.call(null, this, options ); - throw("GL.Mesh.encode: no encoder found for format " + format ); -} - -/** -* Returns a shared mesh containing a quad to be used when rendering to the screen -* Reusing the same quad helps not filling the memory -* @method getScreenQuad -* @return {GL.Mesh} the screen quad -*/ -Mesh.getScreenQuad = function(gl) -{ - gl = gl || global.gl; - var mesh = gl.meshes[":screen_quad"]; - if(mesh) - return mesh; - - var vertices = new Float32Array([0,0,0, 1,1,0, 0,1,0, 0,0,0, 1,0,0, 1,1,0 ]); - var coords = new Float32Array([0,0, 1,1, 0,1, 0,0, 1,0, 1,1 ]); - mesh = new GL.Mesh({ vertices: vertices, coords: coords}, undefined, undefined, gl); - return gl.meshes[":screen_quad"] = mesh; -} - -function linearizeArray( array, typed_array_class ) -{ - if(array.constructor === typed_array_class) - return array; - if(array.constructor !== Array) - { - typed_array_class = typed_array_class || Float32Array; - return new typed_array_class(array); - } - - typed_array_class = typed_array_class || Float32Array; - var components = array[0].length; - var size = array.length * components; - var buffer = new typed_array_class(size); - - for (var i=0; i < array.length;++i) - for(var j=0; j < components; ++j) - buffer[i*components + j] = array[i][j]; - return buffer; -} - -GL.linearizeArray = linearizeArray; - -/* BINARY MESHES */ -//Add some functions to the classes in LiteGL to allow store in binary -GL.Mesh.EXTENSION = "wbin"; -GL.Mesh.enable_wbin_compression = true; - -//this is used when a mesh is dynamic and constantly changes -function DynamicMesh( size, normals, coords, colors, gl ) -{ - size = size || 1024; - - if(GL.debug) - console.log("GL.Mesh created"); - - if( gl !== null ) - { - gl = gl || global.gl; - this.gl = gl; - } - - //used to avoid problems with resources moving between different webgl context - this._context_id = gl.context_id; - - this.vertexBuffers = {}; - this.indexBuffers = {}; - - //here you can store extra info, like groups, which is an array of { name, start, length, material } - this.info = { - groups: [] - }; - this._bounding = BBox.create(); //here you can store a AABB in BBox format - - this.resize( size ); -} - -DynamicMesh.DEFAULT_NORMAL = vec3.fromValues(0,1,0); -DynamicMesh.DEFAULT_COORD = vec2.fromValues(0.5,0.5); -DynamicMesh.DEFAULT_COLOR = vec4.fromValues(1,1,1,1); - -DynamicMesh.prototype.resize = function( size ) -{ - var buffers = {}; - - this._vertex_data = new Float32Array( size * 3 ); - buffers.vertices = this._vertex_data; - - if( normals ) - buffers.normals = this._normal_data = new Float32Array( size * 3 ); - if( coords ) - buffers.coords = this._coord_data = new Float32Array( size * 2 ); - if( colors ) - buffers.colors = this._color_data = new Float32Array( size * 4 ); - - this.addBuffers( buffers ); - - this.current_pos = 0; - this.max_size = size; - this._must_update = true; -} - -DynamicMesh.prototype.clear = function() -{ - this.current_pos = 0; -} - -DynamicMesh.prototype.addPoint = function( vertex, normal, coord, color ) -{ - if (pos >= this.max_size) - { - console.warn("DynamicMesh: not enough space, reserve more"); - return false; - } - var pos = this.current_pos++; - - this._vertex_data.set( vertex, pos*3 ); - - if(this._normal_data) - this._normal_data.set( normal || DynamicMesh.DEFAULT_NORMAL, pos*3 ); - if(this._coord_data) - this._coord_data.set( coord || DynamicMesh.DEFAULT_COORD, pos*2 ); - if(this._color_data) - this._color_data.set( color || DynamicMesh.DEFAULT_COLOR, pos*4 ); - - this._must_update = true; - return true; -} - -DynamicMesh.prototype.update = function( force ) -{ - if(!this._must_update && !force) - return this.current_pos; - this._must_update = false; - - this.getBuffer("vertices").upload( gl.STREAM_DRAW ); - if(this._normal_data) - this.getBuffer("normal").upload( gl.STREAM_DRAW ); - if(this._coord_data) - this.getBuffer("coord").upload( gl.STREAM_DRAW ); - if(this._color_data) - this.getBuffer("color").upload( gl.STREAM_DRAW ); - return this.current_pos; -} - -extendClass( DynamicMesh, Mesh ); - -/** -* @class Mesh -*/ - -/** -* Returns a planar mesh (you can choose how many subdivisions) -* @method Mesh.plane -* @param {Object} options valid options: detail, detailX, detailY, size, width, heigth, xz (horizontal plane) -*/ -Mesh.plane = function(options, gl) { - options = options || {}; - options.triangles = []; - var mesh = {}; - var detailX = options.detailX || options.detail || 1; - var detailY = options.detailY || options.detail || 1; - var width = options.width || options.size || 1; - var height = options.height || options.size || 1; - var xz = options.xz; - width *= 0.5; - height *= 0.5; - - var triangles = []; - var vertices = []; - var coords = []; - var normals = []; - - var N = vec3.fromValues(0,0,1); - if(xz) - N.set([0,1,0]); - - for (var y = 0; y <= detailY; y++) { - var t = y / detailY; - for (var x = 0; x <= detailX; x++) { - var s = x / detailX; - if(xz) - vertices.push((2 * s - 1) * width, 0, -(2 * t - 1) * height); - else - vertices.push((2 * s - 1) * width, (2 * t - 1) * height, 0); - coords.push(s, t); - normals.push(N[0],N[1],N[2]); - if (x < detailX && y < detailY) { - var i = x + y * (detailX + 1); - if(xz) //horizontal - { - triangles.push(i + 1, i + detailX + 1, i); - triangles.push(i + 1, i + detailX + 2, i + detailX + 1); - } - else //vertical - { - triangles.push(i, i + 1, i + detailX + 1); - triangles.push(i + detailX + 1, i + 1, i + detailX + 2); - } - } - } - } - - var bounding = BBox.fromCenterHalfsize( [0,0,0], xz ? [width,0,height] : [width,height,0] ); - var mesh_info = {vertices:vertices, normals: normals, coords: coords, triangles: triangles }; - return GL.Mesh.load( mesh_info, { bounding: bounding }, gl); -}; - -/** -* Returns a 2D Mesh (be careful, stream is vertices2D, used for 2D engines ) -* @method Mesh.plane2D -*/ -Mesh.plane2D = function(options, gl) { - var vertices = new Float32Array([-1,1, 1,-1, 1,1, -1,1, -1,-1, 1,-1]); - var coords = new Float32Array([0,1, 1,0, 1,1, 0,1, 0,0, 1,0]); - - if(options && options.size) - { - var s = options.size * 0.5; - for(var i = 0; i < vertices.length; ++i) - vertices[i] *= s; - } - return new GL.Mesh( {vertices2D: vertices, coords: coords },null,gl ); -}; - -/** -* Returns a point mesh -* @method Mesh.point -* @param {Object} options no options -*/ -Mesh.point = function(options) { - return new GL.Mesh( {vertices: [0,0,0]} ); -} - -/** -* Returns a cube mesh -* @method Mesh.cube -* @param {Object} options valid options: size -*/ -Mesh.cube = function(options, gl) { - options = options || {}; - var halfsize = (options.size || 1) * 0.5; - - var buffers = {}; - //[[-1,1,-1],[-1,-1,+1],[-1,1,1],[-1,1,-1],[-1,-1,-1],[-1,-1,+1],[1,1,-1],[1,1,1],[1,-1,+1],[1,1,-1],[1,-1,+1],[1,-1,-1],[-1,1,1],[1,-1,1],[1,1,1],[-1,1,1],[-1,-1,1],[1,-1,1],[-1,1,-1],[1,1,-1],[1,-1,-1],[-1,1,-1],[1,-1,-1],[-1,-1,-1],[-1,1,-1],[1,1,1],[1,1,-1],[-1,1,-1],[-1,1,1],[1,1,1],[-1,-1,-1],[1,-1,-1],[1,-1,1],[-1,-1,-1],[1,-1,1],[-1,-1,1]] - buffers.vertices = new Float32Array([-1,1,-1,-1,-1,+1, -1,1,1,-1,1,-1, -1,-1,-1,-1,-1,+1, 1,1,-1,1,1,1,1,-1,+1,1,1,-1,1,-1,+1,1,-1,-1,-1,1,1,1,-1,1,1,1,1,-1,1,1,-1,-1,1,1,-1,1,-1,1,-1,1,1,-1,1,-1,-1,-1,1,-1,1,-1,-1,-1,-1,-1,-1,1,-1,1,1,1,1,1,-1,-1,1,-1,-1,1,1,1,1,1,-1,-1,-1,1,-1,-1,1,-1,1,-1,-1,-1,1,-1,1,-1,-1,1]); - for(var i = 0, l = buffers.vertices.length; i < l; ++i) - buffers.vertices[i] *= halfsize; - - //[[-1,0,0],[-1,0,0],[-1,0,0],[-1,0,0],[-1,0,0],[-1,0,0],[1,0,0],[1,0,0],[1,0,0],[1,0,0],[1,0,0],[1,0,0],[0,0,1],[0,0,1],[0,0,1],[0,0,1],[0,0,1],[0,0,1],[0,0,-1],[0,0,-1],[0,0,-1],[0,0,-1],[0,0,-1],[0,0,-1],[0,1,0],[0,1,0],[0,1,0],[0,1,0],[0,1,0],[0,1,0],[0,-1,0],[0,-1,0],[0,-1,0],[0,-1,0],[0,-1,0],[0,-1,0]] - //[[0,1],[1,0],[1,1],[0,1],[0,0],[1,0],[1,1],[0,1],[0,0],[1,1],[0,0],[1,0],[0,1],[1,0],[1,1],[0,1],[0,0],[1,0],[1,1],[0,1],[0,0],[1,1],[0,0],[1,0],[0,1],[1,0],[1,1],[0,1],[0,0],[1,0],[1,1],[0,1],[0,0],[1,1],[0,0],[1,0]]; - buffers.normals = new Float32Array([-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0]); - buffers.coords = new Float32Array([0,1,1,0,1,1,0,1,0,0,1,0,1,1,0,1,0,0,1,1,0,0,1,0,0,1,1,0,1,1,0,1,0,0,1,0,1,1,0,1,0,0,1,1,0,0,1,0,0,1,1,0,1,1,0,1,0,0,1,0,1,1,0,1,0,0,1,1,0,0,1,0]); - - if(options.wireframe) - buffers.wireframe = new Uint16Array([0,2, 2,5, 5,4, 4,0, 6,7, 7,10, 10,11, 11,6, 0,6, 2,7, 5,10, 4,11 ]); - options.bounding = BBox.fromCenterHalfsize( [0,0,0], [halfsize,halfsize,halfsize] ); - return GL.Mesh.load(buffers, options, gl); -} - - -/** -* Returns a cube mesh of a given size -* @method Mesh.cube -* @param {Object} options valid options: size, sizex, sizey, sizez -*/ -Mesh.box = function(options, gl) { - options = options || {}; - var sizex = options.sizex || 1; - var sizey = options.sizey || 1; - var sizez = options.sizez || 1; - sizex *= 0.5; - sizey *= 0.5; - sizez *= 0.5; - - var buffers = {}; - //[[-1,1,-1],[-1,-1,+1],[-1,1,1],[-1,1,-1],[-1,-1,-1],[-1,-1,+1],[1,1,-1],[1,1,1],[1,-1,+1],[1,1,-1],[1,-1,+1],[1,-1,-1],[-1,1,1],[1,-1,1],[1,1,1],[-1,1,1],[-1,-1,1],[1,-1,1],[-1,1,-1],[1,1,-1],[1,-1,-1],[-1,1,-1],[1,-1,-1],[-1,-1,-1],[-1,1,-1],[1,1,1],[1,1,-1],[-1,1,-1],[-1,1,1],[1,1,1],[-1,-1,-1],[1,-1,-1],[1,-1,1],[-1,-1,-1],[1,-1,1],[-1,-1,1]] - buffers.vertices = new Float32Array([-1,1,-1,-1,-1,+1,-1,1,1,-1,1,-1,-1,-1,-1,-1,-1,+1,1,1,-1,1,1,1,1,-1,+1,1,1,-1,1,-1,+1,1,-1,-1,-1,1,1,1,-1,1,1,1,1,-1,1,1,-1,-1,1,1,-1,1,-1,1,-1,1,1,-1,1,-1,-1,-1,1,-1,1,-1,-1,-1,-1,-1,-1,1,-1,1,1,1,1,1,-1,-1,1,-1,-1,1,1,1,1,1,-1,-1,-1,1,-1,-1,1,-1,1,-1,-1,-1,1,-1,1,-1,-1,1]); - //for(var i in options.vertices) for(var j in options.vertices[i]) options.vertices[i][j] *= size; - for(var i = 0, l = buffers.vertices.length; i < l; i+=3) - { - buffers.vertices[i] *= sizex; - buffers.vertices[i+1] *= sizey; - buffers.vertices[i+2] *= sizez; - } - - //[[-1,0,0],[-1,0,0],[-1,0,0],[-1,0,0],[-1,0,0],[-1,0,0],[1,0,0],[1,0,0],[1,0,0],[1,0,0],[1,0,0],[1,0,0],[0,0,1],[0,0,1],[0,0,1],[0,0,1],[0,0,1],[0,0,1],[0,0,-1],[0,0,-1],[0,0,-1],[0,0,-1],[0,0,-1],[0,0,-1],[0,1,0],[0,1,0],[0,1,0],[0,1,0],[0,1,0],[0,1,0],[0,-1,0],[0,-1,0],[0,-1,0],[0,-1,0],[0,-1,0],[0,-1,0]] - //[[0,1],[1,0],[1,1],[0,1],[0,0],[1,0],[1,1],[0,1],[0,0],[1,1],[0,0],[1,0],[0,1],[1,0],[1,1],[0,1],[0,0],[1,0],[1,1],[0,1],[0,0],[1,1],[0,0],[1,0],[0,1],[1,0],[1,1],[0,1],[0,0],[1,0],[1,1],[0,1],[0,0],[1,1],[0,0],[1,0]]; - buffers.normals = new Float32Array([-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0]); - buffers.coords = new Float32Array([0,1,1,0,1,1,0,1,0,0,1,0,1,1,0,1,0,0,1,1,0,0,1,0,0,1,1,0,1,1,0,1,0,0,1,0,1,1,0,1,0,0,1,1,0,0,1,0,0,1,1,0,1,1,0,1,0,0,1,0,1,1,0,1,0,0,1,1,0,0,1,0]); - - if(options.wireframe) - buffers.wireframe = new Uint16Array([0,2, 2,5, 5,4, 4,0, 6,7, 7,10, 10,11, 11,6, 0,6, 2,7, 5,10, 4,11 ]); - - options.bounding = BBox.fromCenterHalfsize( [0,0,0], [sizex,sizey,sizez] ); - - return GL.Mesh.load(buffers, options, gl); -} - -/** -* Returns a circle mesh -* @method Mesh.circle -* @param {Object} options valid options: size,radius, xz = in xz plane, otherwise xy plane -*/ -Mesh.circle = function( options, gl ) { - options = options || {}; - var size = options.size || options.radius || 1; - var slices = Math.ceil(options.slices || 24); - var xz = options.xz || false; - var empty = options.empty || false; - if(slices < 3) slices = 3; - var delta = (2 * Math.PI) / slices; - - var center = vec3.create(); - var A = vec3.create(); - var N = vec3.fromValues(0,0,1); - var uv_center = vec2.fromValues(0.5,0.5); - var uv = vec2.create(); - - if(xz) N.set([0,1,0]); - - var index = xz ? 2 : 1; - - var vertices = new Float32Array(3 * (slices + 1)); - var normals = new Float32Array(3 * (slices + 1)); - var coords = new Float32Array(2 * (slices + 1)); - var triangles = null; - - //the center is always the same - vertices.set(center, 0); - normals.set(N, 0); - coords.set(uv_center, 0); - - var sin = 0; - var cos = 0; - - //compute vertices - for(var i = 0; i < slices; ++i ) - { - sin = Math.sin( delta * i ); - cos = Math.cos( delta * i ); - - A[0] = sin * size; - A[index] = cos * size; - uv[0] = sin * 0.5 + 0.5; - uv[1] = cos * 0.5 + 0.5; - vertices.set(A, i * 3 + 3); - normals.set(N, i * 3 + 3); - coords.set(uv, i * 2 + 2); - } - - if(empty) - { - vertices = vertices.subarray(3); - normals = vertices.subarray(3); - coords = vertices.subarray(2); - triangles = null; - } - else - { - var triangles = new Uint16Array(3 * slices); - var offset = 2; - var offset2 = 1; - if(xz) - { - offset = 1; - offset2 = 2; - } - - //compute indices - for(var i = 0; i < slices-1; ++i ) - { - triangles[i*3] = 0; - triangles[i*3+1] = i+offset; - triangles[i*3+2] = i+offset2; - } - - triangles[i*3] = 0; - if(xz) - { - triangles[i*3+1] = i+1; - triangles[i*3+2] = 1; - } - else - { - triangles[i*3+1] = 1; - triangles[i*3+2] = i+1; - } - } - - options.bounding = BBox.fromCenterHalfsize( [0,0,0], xz ? [size,0,size] : [size,size,0] ); - - var buffers = {vertices: vertices, normals: normals, coords: coords, triangles: triangles}; - - if(options.wireframe) - { - var wireframe = new Uint16Array(slices*2); - for(var i = 0; i < slices; i++) - { - wireframe[i*2] = i; - wireframe[i*2+1] = i+1; - } - wireframe[0] = slices; - buffers.wireframe = wireframe; - } - - return GL.Mesh.load( buffers, options, gl ); -} - -/** -* Returns a cube mesh -* @method Mesh.cylinder -* @param {Object} options valid options: radius, height, subdivisions -*/ -Mesh.cylinder = function( options, gl ) { - options = options || {}; - var radius = options.radius || options.size || 1; - var height = options.height || options.size || 2; - var subdivisions = options.subdivisions || 64; - - var vertices = new Float32Array(subdivisions * 6 * 3 * 2 ); - var normals = new Float32Array(subdivisions * 6 * 3 * 2 ); - var coords = new Float32Array(subdivisions * 6 * 2 * 2 ); - //not indexed because caps have different normals and uvs so... - - var delta = 2*Math.PI / subdivisions; - var normal = null; - for(var i = 0; i < subdivisions; ++i) - { - var angle = i * delta; - - normal = [ Math.sin(angle), 0, Math.cos(angle)]; - vertices.set([ normal[0]*radius, height*0.5, normal[2]*radius], i*6*3); - normals.set(normal, i*6*3 ); - coords.set([i/subdivisions,1], i*6*2 ); - - normal = [ Math.sin(angle), 0, Math.cos(angle)]; - vertices.set([ normal[0]*radius, height*-0.5, normal[2]*radius], i*6*3 + 3); - normals.set(normal, i*6*3 + 3); - coords.set([i/subdivisions,0], i*6*2 + 2); - - normal = [ Math.sin(angle+delta), 0, Math.cos(angle+delta)]; - vertices.set([ normal[0]*radius, height*-0.5, normal[2]*radius], i*6*3 + 6); - normals.set(normal, i*6*3 + 6); - coords.set([(i+1)/subdivisions,0], i*6*2 + 4); - - normal = [ Math.sin(angle+delta), 0, Math.cos(angle+delta)]; - vertices.set([ normal[0]*radius, height*0.5, normal[2]*radius], i*6*3 + 9); - normals.set(normal, i*6*3 + 9); - coords.set([(i+1)/subdivisions,1], i*6*2 + 6); - - normal = [ Math.sin(angle), 0, Math.cos(angle)]; - vertices.set([ normal[0]*radius, height*0.5, normal[2]*radius], i*6*3 + 12); - normals.set(normal, i*6*3 + 12); - coords.set([i/subdivisions,1], i*6*2 + 8); - - normal = [ Math.sin(angle+delta), 0, Math.cos(angle+delta)]; - vertices.set([ normal[0]*radius, height*-0.5, normal[2]*radius], i*6*3 + 15); - normals.set(normal, i*6*3 + 15); - coords.set([(i+1)/subdivisions,0], i*6*2 + 10); - } - - var pos = i*6*3; - var pos_uv = i*6*2; - var caps_start = pos; - - //caps - if( options.caps === false ) - { - //finalize arrays - vertices = vertices.subarray(0,pos); - normals = normals.subarray(0,pos); - coords = coords.subarray(0,pos_uv); - } - else - { - var top_center = vec3.fromValues(0,height*0.5,0); - var bottom_center = vec3.fromValues(0,height*-0.5,0); - var up = vec3.fromValues(0,1,0); - var down = vec3.fromValues(0,-1,0); - for(var i = 0; i < subdivisions; ++i) - { - var angle = i * delta; - - var uv = vec3.fromValues( Math.sin(angle), 0, Math.cos(angle) ); - var uv2 = vec3.fromValues( Math.sin(angle+delta), 0, Math.cos(angle+delta) ); - - vertices.set([ uv[0]*radius, height*0.5, uv[2]*radius], pos + i*6*3); - normals.set(up, pos + i*6*3 ); - coords.set( [ -uv[0] * 0.5 + 0.5,uv[2] * 0.5 + 0.5], pos_uv + i*6*2 ); - - vertices.set([ uv2[0]*radius, height*0.5, uv2[2]*radius], pos + i*6*3 + 3); - normals.set(up, pos + i*6*3 + 3 ); - coords.set( [ -uv2[0] * 0.5 + 0.5,uv2[2] * 0.5 + 0.5], pos_uv + i*6*2 + 2 ); - - vertices.set( top_center, pos + i*6*3 + 6 ); - normals.set(up, pos + i*6*3 + 6); - coords.set([0.5,0.5], pos_uv + i*6*2 + 4); - - //bottom - vertices.set([ uv2[0]*radius, height*-0.5, uv2[2]*radius], pos + i*6*3 + 9); - normals.set(down, pos + i*6*3 + 9); - coords.set( [ uv2[0] * 0.5 + 0.5,uv2[2] * 0.5 + 0.5], pos_uv + i*6*2 + 6); - - vertices.set([ uv[0]*radius, height*-0.5, uv[2]*radius], pos + i*6*3 + 12); - normals.set(down, pos + i*6*3 + 12 ); - coords.set( [ uv[0] * 0.5 + 0.5,uv[2] * 0.5 + 0.5], pos_uv + i*6*2 + 8 ); - - vertices.set( bottom_center, pos + i*6*3 + 15 ); - normals.set( down, pos + i*6*3 + 15); - coords.set( [0.5,0.5], pos_uv + i*6*2 + 10); - } - } - - var buffers = { - vertices: vertices, - normals: normals, - coords: coords - } - options.bounding = BBox.fromCenterHalfsize( [0,0,0], [radius,height*0.5,radius] ); - options.info = { groups: [] }; - - if(options.caps !== false) - { - options.info.groups.push({ name:"side", start: 0, length: caps_start / 3}); - options.info.groups.push({ name:"caps", start: caps_start / 3, length: (vertices.length - caps_start) / 3}); - } - - return Mesh.load( buffers, options, gl ); -} - -/** -* Returns a cone mesh -* @method Mesh.cone -* @param {Object} options valid options: radius, height, subdivisions -*/ -Mesh.cone = function( options, gl ) { - options = options || {}; - var radius = options.radius || options.size || 1; - var height = options.height || options.size || 2; - var subdivisions = options.subdivisions || 64; - - var vertices = new Float32Array(subdivisions * 3 * 3 * 2); - var normals = new Float32Array(subdivisions * 3 * 3 * 2); - var coords = new Float32Array(subdivisions * 2 * 3 * 2); - //not indexed because caps have different normals and uvs so... - - var delta = 2*Math.PI / subdivisions; - var normal = null; - var normal_y = radius / height; - var up = [0,1,0]; - - for(var i = 0; i < subdivisions; ++i) - { - var angle = i * delta; - - normal = [ Math.sin(angle+delta*0.5), normal_y, Math.cos(angle+delta*0.5)]; - vec3.normalize(normal,normal); - //normal = up; - vertices.set([ 0, height, 0] , i*6*3); - normals.set(normal, i*6*3 ); - coords.set([i/subdivisions,1], i*6*2 ); - - normal = [ Math.sin(angle), normal_y, Math.cos(angle)]; - vertices.set([ normal[0]*radius, 0, normal[2]*radius], i*6*3 + 3); - vec3.normalize(normal,normal); - normals.set(normal, i*6*3 + 3); - coords.set([i/subdivisions,0], i*6*2 + 2); - - normal = [ Math.sin(angle+delta), normal_y, Math.cos(angle+delta)]; - vertices.set([ normal[0]*radius, 0, normal[2]*radius], i*6*3 + 6); - vec3.normalize(normal,normal); - normals.set(normal, i*6*3 + 6); - coords.set([(i+1)/subdivisions,0], i*6*2 + 4); - } - - var pos = 0;//i*3*3; - var pos_uv = 0;//i*3*2; - - //cap - var bottom_center = vec3.fromValues(0,0,0); - var down = vec3.fromValues(0,-1,0); - for(var i = 0; i < subdivisions; ++i) - { - var angle = i * delta; - - var uv = vec3.fromValues( Math.sin(angle), 0, Math.cos(angle) ); - var uv2 = vec3.fromValues( Math.sin(angle+delta), 0, Math.cos(angle+delta) ); - - //bottom - vertices.set([ uv2[0]*radius, 0, uv2[2]*radius], pos + i*6*3 + 9); - normals.set(down, pos + i*6*3 + 9); - coords.set( [ uv2[0] * 0.5 + 0.5,uv2[2] * 0.5 + 0.5], pos_uv + i*6*2 + 6); - - vertices.set([ uv[0]*radius, 0, uv[2]*radius], pos + i*6*3 + 12); - normals.set(down, pos + i*6*3 + 12 ); - coords.set( [ uv[0] * 0.5 + 0.5,uv[2] * 0.5 + 0.5], pos_uv + i*6*2 + 8 ); - - vertices.set( bottom_center, pos + i*6*3 + 15 ); - normals.set( down, pos + i*6*3 + 15); - coords.set( [0.5,0.5], pos_uv + i*6*2 + 10); - } - - var buffers = { - vertices: vertices, - normals: normals, - coords: coords - } - options.bounding = BBox.fromCenterHalfsize( [0,height*0.5,0], [radius,height*0.5,radius] ); - - return Mesh.load( buffers, options, gl ); -} - -/** -* Returns a sphere mesh -* @method Mesh.sphere -* @param {Object} options valid options: radius, lat, long, subdivisions, hemi -*/ -Mesh.sphere = function( options, gl ) { - options = options || {}; - var radius = options.radius || options.size || 1; - var latitudeBands = options.lat || options.subdivisions || 16; - var longitudeBands = options["long"] || options.subdivisions || 16; - - var vertexPositionData = new Float32Array( (latitudeBands+1)*(longitudeBands+1)*3 ); - var normalData = new Float32Array( (latitudeBands+1)*(longitudeBands+1)*3 ); - var textureCoordData = new Float32Array( (latitudeBands+1)*(longitudeBands+1)*2 ); - var indexData = new Uint16Array( latitudeBands*longitudeBands*6 ); - var latRange = options.hemi ? Math.PI * 0.5 : Math.PI; - - var i = 0, iuv = 0; - for (var latNumber = 0; latNumber <= latitudeBands; latNumber++) - { - var theta = latNumber * latRange / latitudeBands; - var sinTheta = Math.sin(theta); - var cosTheta = Math.cos(theta); - - for (var longNumber = 0; longNumber <= longitudeBands; longNumber++) - { - var phi = longNumber * 2 * Math.PI / longitudeBands; - var sinPhi = Math.sin(phi); - var cosPhi = Math.cos(phi); - - var x = cosPhi * sinTheta; - var y = cosTheta; - var z = sinPhi * sinTheta; - var u = 1- (longNumber / longitudeBands); - var v = (1 - latNumber / latitudeBands); - - vertexPositionData.set([radius * x,radius * y,radius * z],i); - normalData.set([x,y,z],i); - textureCoordData.set([u,v], iuv ); - i += 3; - iuv += 2; - } - } - - i=0; - for (var latNumber = 0; latNumber < latitudeBands; latNumber++) - { - for (var longNumber = 0; longNumber < longitudeBands; longNumber++) - { - var first = (latNumber * (longitudeBands + 1)) + longNumber; - var second = first + longitudeBands + 1; - - indexData.set([second,first,first + 1], i); - indexData.set([second + 1,second,first + 1], i+3); - i += 6; - } - } - - var buffers = { - vertices: vertexPositionData, - normals: normalData, - coords: textureCoordData, - triangles: indexData - }; - - if(options.wireframe) - { - var wireframe = new Uint16Array(longitudeBands*latitudeBands*4); - var pos = 0; - for(var i = 0; i < latitudeBands; i++) - { - for(var j = 0; j < longitudeBands; j++) - { - wireframe[pos] = i*(longitudeBands+1) + j; - wireframe[pos + 1] = i*(longitudeBands+1) + j + 1; - pos += 2; - } - wireframe[pos - longitudeBands*2] = i*(longitudeBands+1) + j; - } - - for(var i = 0; i < longitudeBands; i++) - for(var j = 0; j < latitudeBands; j++) - { - wireframe[pos] = j*(longitudeBands+1) + i; - wireframe[pos + 1] = (j+1)*(longitudeBands+1) + i; - pos += 2; - } - buffers.wireframe = wireframe; - } - - if(options.hemi) - options.bounding = BBox.fromCenterHalfsize( [0,radius*0.5,0], [radius,radius*0.5,radius], radius ); - else - options.bounding = BBox.fromCenterHalfsize( [0,0,0], [radius,radius,radius], radius ); - return GL.Mesh.load( buffers, options, gl ); -} - -/** -* Returns a grid mesh (must be rendered using gl.LINES) -* @method Mesh.grid -* @param {Object} options valid options: size, lines -*/ -Mesh.grid = function( options, gl ) -{ - options = options || {}; - var num_lines = options.lines || 11; - if(num_lines < 0) - num_lines = 1; - var size = options.size || 10; - - var vertexPositionData = new Float32Array( num_lines*2*2*3 ); - var hsize = size * 0.5; - var pos = 0; - var x = -hsize; - var delta = size / (num_lines-1); - - for(var i = 0; i < num_lines; i++) - { - vertexPositionData[ pos ] = x; - vertexPositionData[ pos+2 ] = -hsize; - vertexPositionData[ pos+3 ] = x; - vertexPositionData[ pos+5 ] = hsize; - - vertexPositionData[ pos+6 ] = hsize; - vertexPositionData[ pos+8 ] = x - vertexPositionData[ pos+9 ] = -hsize; - vertexPositionData[ pos+11 ] = x - - x += delta; - pos += 12; - } - - return new GL.Mesh({vertices: vertexPositionData}, options, gl ); -} - - -/** -* Returns a icosahedron mesh (useful to create spheres by subdivision) -* @method Mesh.icosahedron -* @param {Object} options valid options: radius, subdivisions (max: 6) -*/ -Mesh.icosahedron = function( options, gl ) { - options = options || {}; - var radius = options.radius || options.size || 1; - var subdivisions = options.subdivisions === undefined ? 0 : options.subdivisions; - if(subdivisions > 6) //dangerous - subdivisions = 6; - - var t = (1.0 + Math.sqrt(5)) / 2.0; - var vertices = [-1,t,0, 1,t,0, -1,-t,0, 1,-t,0, - 0,-1,t, 0,1,t, 0,-1,-t, 0,1,-t, - t,0,-1, t,0,1, -t,0,-1, -t,0,1]; - var normals = []; - var coords = []; - var indices = [0,11,5, 0,5,1, 0,1,7, 0,7,10, 0,10,11, 1,5,9, 5,11,4, 11,10,2, 10,7,6, 7,1,8, 3,9,4, 3,4,2, 3,2,6, 3,6,8, 3,8,9, 4,9,5, 2,4,11, 6,2,10, 8,6,7, 9,8,1 ]; - - //normalize - var l = vertices.length; - for(var i = 0; i < l; i+=3) - { - var mod = Math.sqrt( vertices[i]*vertices[i] + vertices[i+1]*vertices[i+1] + vertices[i+2]*vertices[i+2] ); - var normalx = vertices[i] / mod; - var normaly = vertices[i+1] / mod; - var normalz = vertices[i+2] / mod; - normals.push( normalx, normaly, normalz ); - coords.push( Math.atan2( normalx, normalz ), Math.acos( normaly ) ); - vertices[i] *= radius/mod; - vertices[i+1] *= radius/mod; - vertices[i+2] *= radius/mod; - } - - var middles = {}; - - //A,B = index of vertex in vertex array - function middlePoint( A, B ) - { - var key = indices[A] < indices[B] ? indices[A] + ":"+indices[B] : indices[B]+":"+indices[A]; - var r = middles[key]; - if(r) - return r; - var index = vertices.length / 3; - vertices.push(( vertices[ indices[A]*3] + vertices[ indices[B]*3 ]) * 0.5, - (vertices[ indices[A]*3+1] + vertices[ indices[B]*3+1 ]) * 0.5, - (vertices[ indices[A]*3+2] + vertices[ indices[B]*3+2 ]) * 0.5); - - var mod = Math.sqrt( vertices[index*3]*vertices[index*3] + vertices[index*3+1]*vertices[index*3+1] + vertices[index*3+2]*vertices[index*3+2] ); - var normalx = vertices[index*3] / mod; - var normaly = vertices[index*3+1] / mod; - var normalz = vertices[index*3+2] / mod; - normals.push( normalx, normaly, normalz ); - coords.push( (Math.atan2( normalx, normalz ) / Math.PI) * 0.5, (Math.acos( normaly ) / Math.PI) ); - vertices[index*3] *= radius/mod; - vertices[index*3+1] *= radius/mod; - vertices[index*3+2] *= radius/mod; - - middles[key] = index; - return index; - } - - for (var iR = 0; iR < subdivisions; ++iR ) - { - var new_indices = []; - var l = indices.length; - for(var i = 0; i < l; i+=3) - { - var MA = middlePoint( i, i+1 ); - var MB = middlePoint( i+1, i+2); - var MC = middlePoint( i+2, i); - new_indices.push(indices[i], MA, MC); - new_indices.push(indices[i+1], MB, MA); - new_indices.push(indices[i+2], MC, MB); - new_indices.push(MA, MB, MC); - } - indices = new_indices; - } - - options.bounding = BBox.fromCenterHalfsize( [0,0,0], [radius,radius,radius], radius ); - - return new GL.Mesh.load({vertices: vertices, coords: coords, normals: normals, triangles: indices},options,gl); -} -/** -* @namespace GL -*/ - -/** -* Texture class to upload images to the GPU, default is gl.TEXTURE_2D, gl.RGBA of gl.UNSIGNED_BYTE with filters set to gl.LINEAR and wrap to gl.CLAMP_TO_EDGE
    - There is a list of options
    - ==========================
    - - texture_type: gl.TEXTURE_2D, gl.TEXTURE_CUBE_MAP, default gl.TEXTURE_2D
    - - format: gl.RGB, gl.RGBA, gl.DEPTH_COMPONENT, default gl.RGBA
    - - type: gl.UNSIGNED_BYTE, gl.UNSIGNED_SHORT, gl.HALF_FLOAT_OES, gl.FLOAT, default gl.UNSIGNED_BYTE
    - - filter: filtering for mag and min: gl.NEAREST or gl.LINEAR, default gl.NEAREST
    - - magFilter: magnifying filter: gl.NEAREST, gl.LINEAR, default gl.NEAREST
    - - minFilter: minifying filter: gl.NEAREST, gl.LINEAR, gl.LINEAR_MIPMAP_LINEAR, default gl.NEAREST
    - - wrap: texture wrapping: gl.CLAMP_TO_EDGE, gl.REPEAT, gl.MIRROR, default gl.CLAMP_TO_EDGE (also accepts wrapT and wrapS for separate settings)
    - - pixel_data: ArrayBufferView with the pixel data to upload to the texture, otherwise the texture will be black (if cubemaps then pass an array[6] with the data for every face)
    - - premultiply_alpha : multiply the color by the alpha value when uploading, default FALSE
    - - no_flip : do not flip in Y, default TRUE
    - - anisotropic : number of anisotropic fetches, default 0
    - - check for more info about formats: https://developer.mozilla.org/en-US/docs/Web/API/WebGLRenderingContext/texImage2D - -* @class Texture -* @param {number} width texture width (any supported but Power of Two allows to have mipmaps), 0 means no memory reserved till its filled -* @param {number} height texture height (any supported but Power of Two allows to have mipmaps), 0 means no memory reserved till its filled -* @param {Object} options Check the list in the description -* @constructor -*/ - -global.Texture = GL.Texture = function Texture( width, height, options, gl ) { - options = options || {}; - - //used to avoid problems with resources moving between different webgl context - gl = gl || global.gl; - this.gl = gl; - this._context_id = gl.context_id; - - //round sizes - width = parseInt(width); - height = parseInt(height); - - if(GL.debug) - console.log("GL.Texture created: ",width,height); - - //create texture handler - this.handler = gl.createTexture(); - - //set settings - this.width = width; - this.height = height; - if(options.depth) //for texture_3d - this.depth = options.depth; - this.texture_type = options.texture_type || gl.TEXTURE_2D; //or gl.TEXTURE_CUBE_MAP - this.format = options.format || Texture.DEFAULT_FORMAT; //gl.RGBA (if gl.DEPTH_COMPONENT remember type: gl.UNSIGNED_SHORT) - this.internalFormat = options.internalFormat; //LUMINANCE, and weird formats with bits - this.type = options.type || Texture.DEFAULT_TYPE; //gl.UNSIGNED_BYTE, gl.UNSIGNED_SHORT, gl.FLOAT or gl.HALF_FLOAT_OES (or gl.HIGH_PRECISION_FORMAT which could be half or float) - this.magFilter = options.magFilter || options.filter || Texture.DEFAULT_MAG_FILTER; - this.minFilter = options.minFilter || options.filter || Texture.DEFAULT_MIN_FILTER; - this.wrapS = options.wrap || options.wrapS || Texture.DEFAULT_WRAP_S; - this.wrapT = options.wrap || options.wrapT || Texture.DEFAULT_WRAP_T; - this.data = null; //where the data came from - - //precompute the max amount of texture units - if(!Texture.MAX_TEXTURE_IMAGE_UNITS) - Texture.MAX_TEXTURE_IMAGE_UNITS = gl.getParameter( gl.MAX_TEXTURE_IMAGE_UNITS ); - - this.has_mipmaps = false; - - if( this.format == gl.DEPTH_COMPONENT && gl.webgl_version == 1 && !gl.extensions["WEBGL_depth_texture"] ) - throw("Depth Texture not supported"); - if( this.type == gl.FLOAT && !gl.extensions["OES_texture_float"] && gl.webgl_version == 1 ) - throw("Float Texture not supported"); - if( this.type == gl.HALF_FLOAT_OES) - { - if( !gl.extensions["OES_texture_half_float"] && gl.webgl_version == 1 ) - throw("Half Float Texture extension not supported."); - else if( gl.webgl_version > 1 ) - { - console.warn("using HALF_FLOAT_OES in WebGL2 is deprecated, suing HALF_FLOAT instead"); - this.type = this.format == gl.RGB ? gl.RGB16F : gl.RGBA16F; - } - } - if( (!isPowerOfTwo(this.width) || !isPowerOfTwo(this.height)) && //non power of two - ( (this.minFilter != gl.NEAREST && this.minFilter != gl.LINEAR) || //uses mipmaps - (this.wrapS != gl.CLAMP_TO_EDGE || this.wrapT != gl.CLAMP_TO_EDGE) ) ) //uses wrap - { - if(!options.ignore_pot) - throw("Cannot use texture-wrap or mipmaps in Non-Power-of-Two textures"); - else - { - this.minFilter = this.magFilter = gl.LINEAR; - this.wrapS = this.wrapT = gl.CLAMP_TO_EDGE; - } - } - - //empty textures are allowed to be created - if(!width || !height) - return; - - //because sometimes the internal format is not so obvious - if(!this.internalFormat) - this.computeInternalFormat(); - - //this is done because in some cases the user binds a texture to slot 0 and then creates a new one, which overrides slot 0 - gl.activeTexture( gl.TEXTURE0 + Texture.MAX_TEXTURE_IMAGE_UNITS - 1); - //I use an invalid gl enum to say this texture is a depth texture, ugly, I know... - gl.bindTexture( this.texture_type, this.handler); - gl.texParameteri( this.texture_type, gl.TEXTURE_MAG_FILTER, this.magFilter ); - gl.texParameteri( this.texture_type, gl.TEXTURE_MIN_FILTER, this.minFilter ); - gl.texParameteri( this.texture_type, gl.TEXTURE_WRAP_S, this.wrapS ); - gl.texParameteri( this.texture_type, gl.TEXTURE_WRAP_T, this.wrapT ); - - if(options.anisotropic && gl.extensions["EXT_texture_filter_anisotropic"]) - gl.texParameterf( GL.TEXTURE_2D, gl.extensions["EXT_texture_filter_anisotropic"].TEXTURE_MAX_ANISOTROPY_EXT, options.anisotropic); - - var type = this.type; - var pixel_data = options.pixel_data; - if(pixel_data && !pixel_data.buffer) - { - if( this.texture_type == GL.TEXTURE_CUBE_MAP ) - { - if(pixel_data[0].constructor === Number) //special case, specify just one face and copy it - { - pixel_data = toTypedArray( pixel_data ); - pixel_data = [pixel_data,pixel_data,pixel_data,pixel_data,pixel_data,pixel_data]; - } - else - for(var i = 0; i < pixel_data.length; ++i) - pixel_data[i] = toTypedArray( pixel_data[i] ); - } - else - pixel_data = toTypedArray( pixel_data ); - this.data = pixel_data; - } - - function toTypedArray( data ) - { - if(data.constructor !== Array) - return data; - if( type == GL.FLOAT) - return new Float32Array( data ); - if( type == GL.HALF_FLOAT_OES) - return new Uint16Array( data ); - return new Uint8Array( data ); - } - - //gl.TEXTURE_1D is not supported by WebGL... - - //here we create all ********************************** - if(this.texture_type == GL.TEXTURE_2D) - { - //create the texture - gl.texImage2D( GL.TEXTURE_2D, 0, this.internalFormat, width, height, 0, this.format, this.type, pixel_data || null ); - - //generate empty mipmaps (necessary?) - if ( GL.isPowerOfTwo(width) && GL.isPowerOfTwo(height) && options.minFilter && options.minFilter != gl.NEAREST && options.minFilter != gl.LINEAR) - { - gl.generateMipmap( this.texture_type ); - this.has_mipmaps = true; - } - } - else if(this.texture_type == GL.TEXTURE_CUBE_MAP) - { - for(var i = 0; i < 6; ++i) - gl.texImage2D( gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, this.internalFormat, this.width, this.height, 0, this.format, this.type, pixel_data ? pixel_data[i] : null ); - } - else if(this.texture_type == GL.TEXTURE_3D) - { - if(this.gl.webgl_version == 1) - throw("TEXTURE_3D not supported in WebGL 1. Enable WebGL 2 in the context by passing webgl2:true to the context"); - if(!options.depth) - throw("3d texture depth must be set in the options.depth"); - gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false ); //standard does not allow this flags for 3D textures - gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false ); - gl.texImage3D( GL.TEXTURE_3D, 0, this.internalFormat, width, height, options.depth, 0, this.format, this.type, pixel_data || null ); - } - gl.bindTexture(this.texture_type, null); //disable - gl.activeTexture(gl.TEXTURE0); -} - -Texture.DEFAULT_TYPE = GL.UNSIGNED_BYTE; -Texture.DEFAULT_FORMAT = GL.RGBA; -Texture.DEFAULT_MAG_FILTER = GL.LINEAR; -Texture.DEFAULT_MIN_FILTER = GL.LINEAR; -Texture.DEFAULT_WRAP_S = GL.CLAMP_TO_EDGE; -Texture.DEFAULT_WRAP_T = GL.CLAMP_TO_EDGE; -Texture.EXTENSION = "png"; //used when saving it to file - -//used for render to FBOs -Texture.framebuffer = null; -Texture.renderbuffer = null; -Texture.loading_color = new Uint8Array([0,0,0,0]); -Texture.use_renderbuffer_pool = true; //should improve performance - -//because usually you dont want to specify the internalFormat, this tries to guess it from its format -//check https://webgl2fundamentals.org/webgl/lessons/webgl-data-textures.html for more info -Texture.prototype.computeInternalFormat = function() -{ - this.internalFormat = this.format; //default - - //automatic selection of internal format for depth textures to avoid problems between webgl1 and 2 - if( this.format == GL.DEPTH_COMPONENT ) - { - this.minFilter = this.magFilter = GL.NEAREST; - - if( gl.webgl_version == 2 ) - { - if( this.type == GL.UNSIGNED_SHORT ) - this.internalFormat = GL.DEPTH_COMPONENT16; - else if( this.type == GL.UNSIGNED_INT ) - this.internalFormat = GL.DEPTH_COMPONENT24; - else if( this.type == GL.FLOAT ) - this.internalFormat = GL.DEPTH_COMPONENT32F; - else - throw("unsupported type for a depth texture"); - } - else if( gl.webgl_version == 1 ) - { - if( this.type == GL.FLOAT ) - throw("WebGL 1.0 does not support float depth textures"); - this.internalFormat = GL.DEPTH_COMPONENT; - } - } - else if( this.format == gl.RGBA ) - { - if( gl.webgl_version == 2 ) - { - if( this.type == GL.FLOAT ) - this.internalFormat = GL.RGBA32F; - else if( this.type == GL.HALF_FLOAT ) - this.internalFormat = GL.RGBA16F; - else if( this.type == GL.HALF_FLOAT_OES ) - { - console.warn("webgl 2 does not use HALF_FLOAT_OES, converting to HALF_FLOAT") - this.type = GL.HALF_FLOAT; - this.internalFormat = GL.RGBA16F; - } - /* - else if( this.type == GL.UNSIGNED_SHORT ) - { - this.internalFormat = GL.RGBA16UI; - this.format = gl.RGBA_INTEGER; - } - else if( this.type == GL.UNSIGNED_INT ) - { - this.internalFormat = GL.RGBA32UI; - this.format = gl.RGBA_INTEGER; - } - */ - } - else if( gl.webgl_version == 1 ) - { - if( this.type == GL.HALF_FLOAT ) - { - console.warn("webgl 1 does not use HALF_FLOAT, converting to HALF_FLOAT_OES") - this.type = GL.HALF_FLOAT_OES; - } - } - } -} - -/** -* Free the texture memory from the GPU, sets the texture handler to null -* @method delete -*/ -Texture.prototype.delete = function() -{ - gl.deleteTexture( this.handler ); - this.handler = null; -} - -Texture.prototype.getProperties = function() -{ - return { - width: this.width, - height: this.height, - type: this.type, - format: this.format, - texture_type: this.texture_type, - magFilter: this.magFilter, - minFilter: this.minFilter, - wrapS: this.wrapS, - wrapT: this.wrapT - }; -} - -Texture.prototype.hasSameProperties = function(t) -{ - if(!t) - return false; - return t.width == this.width && - t.height == this.height && - t.type == this.type && - t.format == this.format && - t.texture_type == this.texture_type; -} - -Texture.prototype.hasSameSize = function(t) -{ - if(!t) - return false; - return t.width == this.width && t.height == this.height; -} -//textures cannot be stored in JSON -Texture.prototype.toJSON = function() -{ - return ""; -} - - -/** -* Returns if depth texture is supported by the GPU -* @method isDepthSupported -* @return {Boolean} true if supported -*/ -Texture.isDepthSupported = function() -{ - return gl.extensions["WEBGL_depth_texture"] != null; -} - -/** -* Binds the texture to one texture unit -* @method bind -* @param {number} unit texture unit -* @return {number} returns the texture unit -*/ -Texture.prototype.bind = function( unit ) { - if(unit == undefined) - unit = 0; - var gl = this.gl; - - //TODO: if the texture is not uploaded, must be upload now - - //bind - gl.activeTexture(gl.TEXTURE0 + unit); - gl.bindTexture( this.texture_type, this.handler ); - return unit; -} - -/** -* Unbinds the texture -* @method unbind -* @param {number} unit texture unit -* @return {number} returns the texture unit -*/ -Texture.prototype.unbind = function(unit) { - if(unit === undefined) - unit = 0; - var gl = this.gl; - gl.activeTexture(gl.TEXTURE0 + unit ); - gl.bindTexture(this.texture_type, null); -} - - -Texture.prototype.setParameter = function(param,value) { - this.bind(0); - this.gl.texParameteri( this.texture_type, param, value ); - switch(param) - { - case this.gl.TEXTURE_MAG_FILTER: this.magFilter = value; break; - case this.gl.TEXTURE_MIN_FILTER: this.minFilter = value; break; - case this.gl.TEXTURE_WRAP_S: this.wrapS = value; break; - case this.gl.TEXTURE_WRAP_T: this.wrapT = value; break; - } -} - -/** -* Unbinds the texture -* @method Texture.setUploadOptions -* @param {Object} options a list of options to upload the texture -* - premultiply_alpha : multiply the color by the alpha value, default FALSE -* - no_flip : do not flip in Y, default TRUE -*/ -Texture.setUploadOptions = function(options, gl) -{ - gl = gl || global.gl; - - if(options) //options that are not stored in the texture should be passed again to avoid reusing unknown state - { - gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, !!(options.premultiply_alpha) ); - gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, !(options.no_flip) ); - } - else - { - gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false ); - gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true ); - } - gl.pixelStorei(gl.UNPACK_ALIGNMENT, 1); -} - -/** -* Given an Image/Canvas/Video it uploads it to the GPU -* @method uploadImage -* @param {Image} img -* @param {Object} options [optional] upload options (premultiply_alpha, no_flip) -*/ -Texture.prototype.uploadImage = function( image, options ) -{ - this.bind(); - var gl = this.gl; - if(!image) - throw("uploadImage parameter must be Image"); - - Texture.setUploadOptions(options, gl); - - try { - gl.texImage2D( gl.TEXTURE_2D, 0, this.format, this.format, this.type, image ); - this.width = image.videoWidth || image.width; - this.height = image.videoHeight || image.height; - this.data = image; - } catch (e) { - if (location.protocol == 'file:') { - throw 'image not loaded for security reasons (serve this page over "http://" instead)'; - } else { - throw 'image not loaded for security reasons (image must originate from the same ' + - 'domain as this page or use Cross-Origin Resource Sharing)'; - } - } - - //TODO: add expand transparent pixels option - - //generate mipmaps - if (this.minFilter && this.minFilter != gl.NEAREST && this.minFilter != gl.LINEAR) { - gl.generateMipmap(this.texture_type); - this.has_mipmaps = true; - } - gl.bindTexture(this.texture_type, null); //disable -} - -/** -* Uploads data to the GPU (data must have the appropiate size) -* @method uploadData -* @param {ArrayBuffer} data -* @param {Object} options [optional] upload options (premultiply_alpha, no_flip, cubemap_face, mipmap_level) -*/ -Texture.prototype.uploadData = function( data, options, skip_mipmaps ) -{ - options = options || {}; - if(!data) - throw("no data passed"); - var gl = this.gl; - this.bind(); - Texture.setUploadOptions(options, gl); - var mipmap_level = options.mipmap_level || 0; - var width = this.width; - var height = this.height; - width = width >> mipmap_level; - height = height >> mipmap_level; - var internal_format = this.internalFormat || this.format; - - if( this.type == GL.HALF_FLOAT_OES && data.constructor === Float32Array ) - console.warn("cannot uploadData to a HALF_FLOAT texture from a Float32Array, must be Uint16Array. To upload it we recomment to create a FLOAT texture, upload data there and copy to your HALF_FLOAT."); - - if( this.texture_type == GL.TEXTURE_2D ) - { - if(gl.webgl_version == 1) - { - if(data.buffer && data.buffer.constructor == ArrayBuffer) - gl.texImage2D(this.texture_type, mipmap_level, internal_format, width, height, 0, this.format, this.type, data); - else - gl.texImage2D(this.texture_type, mipmap_level, internal_format, this.format, this.type, data); - } - else if(gl.webgl_version == 2) //webgl forces to use width and height - { - if(data.buffer && data.buffer.constructor == ArrayBuffer) - gl.texImage2D(this.texture_type, mipmap_level, internal_format, width, height, 0, this.format, this.type, data); - else - gl.texImage2D(this.texture_type, mipmap_level, internal_format, width, height, 0, this.format, this.type, data); - } - } - else if( this.texture_type == GL.TEXTURE_3D ) - { - gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false ); //standard does not allow this flags for 3D textures - gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false ); - gl.texImage3D( this.texture_type, mipmap_level, internal_format, width, height, this.depth >> mipmap_level, 0, this.format, this.type, data); - } - else if( this.texture_type == GL.TEXTURE_CUBE_MAP ) - gl.texImage2D( gl.TEXTURE_CUBE_MAP_POSITIVE_X + (options.cubemap_face || 0), mipmap_level, internal_format, width, height, 0, this.format, this.type, data); - else - throw("cannot uploadData for this texture type"); - - this.data = data; //should I clone it? - - if (!skip_mipmaps && this.minFilter && this.minFilter != gl.NEAREST && this.minFilter != gl.LINEAR) { - gl.generateMipmap(this.texture_type); - this.has_mipmaps = true; - } - gl.bindTexture(this.texture_type, null); //disable -} - -//When creating cubemaps this is helpful - -/*THIS WORKS old -Texture.cubemap_camera_parameters = [ - { type:"posX", dir: vec3.fromValues(-1,0,0), up: vec3.fromValues(0,1,0), right: vec3.fromValues(0,0,-1) }, - { type:"negX", dir: vec3.fromValues(1,0,0), up: vec3.fromValues(0,1,0), right: vec3.fromValues(0,0,1) }, - { type:"posY", dir: vec3.fromValues(0,-1,0), up: vec3.fromValues(0,0,-1), right: vec3.fromValues(1,0,0) }, - { type:"negY", dir: vec3.fromValues(0,1,0), up: vec3.fromValues(0,0,1), right: vec3.fromValues(-1,0,0) }, - { type:"posZ", dir: vec3.fromValues(0,0,-1), up: vec3.fromValues(0,1,0), right: vec3.fromValues(1,0,0) }, - { type:"negZ", dir: vec3.fromValues(0,0,1), up: vec3.fromValues(0,1,0), right: vec3.fromValues(-1,0,0) } -]; -*/ - -//THIS works -Texture.cubemap_camera_parameters = [ - { type:"posX", dir: vec3.fromValues(1,0,0), up: vec3.fromValues(0,1,0), right: vec3.fromValues(0,0,-1) }, - { type:"negX", dir: vec3.fromValues(-1,0,0), up: vec3.fromValues(0,1,0), right: vec3.fromValues(0,0,1) }, - { type:"posY", dir: vec3.fromValues(0,1,0), up: vec3.fromValues(0,0,-1), right: vec3.fromValues(1,0,0) }, - { type:"negY", dir: vec3.fromValues(0,-1,0), up: vec3.fromValues(0,0,1), right: vec3.fromValues(1,0,0) }, - { type:"posZ", dir: vec3.fromValues(0,0,1), up: vec3.fromValues(0,1,0), right: vec3.fromValues(1,0,0) }, - { type:"negZ", dir: vec3.fromValues(0,0,-1), up: vec3.fromValues(0,1,0), right: vec3.fromValues(-1,0,0) } -]; - - - -/** -* Render to texture using FBO, just pass the callback to a rendering function and the content of the texture will be updated -* If the texture is a cubemap, the callback will be called six times, once per face, the number of the face is passed as a second parameter -* for further info about how to set up the propper cubemap camera, check the GL.Texture.cubemap_camera_parameters with the direction and up vector for every face. -* -* Keep in mind that it tries to reuse the last renderbuffer for the depth, and if it cannot (different size) it creates a new one (throwing the old) -* @method drawTo -* @param {Function} callback function that does all the rendering inside this texture -*/ -Texture.prototype.drawTo = function(callback, params) -{ - var gl = this.gl; - - //if(this.format == gl.DEPTH_COMPONENT) - // throw("cannot use drawTo in depth textures, use Texture.drawToColorAndDepth"); - - var v = gl.getViewport(); - var now = GL.getTime(); - - var old_fbo = gl.getParameter( gl.FRAMEBUFFER_BINDING ); - - var framebuffer = gl._framebuffer = gl._framebuffer || gl.createFramebuffer(); - gl.bindFramebuffer( gl.FRAMEBUFFER, framebuffer ); - - //this code allows to reuse old renderbuffers instead of creating and destroying them for every frame - var renderbuffer = null; - - if( Texture.use_renderbuffer_pool ) //create a renderbuffer pool - { - if(!gl._renderbuffers_pool) - gl._renderbuffers_pool = {}; - //generate unique key for this renderbuffer - var key = this.width + ":" + this.height; - - //reuse or create new one - if( gl._renderbuffers_pool[ key ] ) //Reuse old - { - renderbuffer = gl._renderbuffers_pool[ key ]; - renderbuffer.time = now; - gl.bindRenderbuffer(gl.RENDERBUFFER, renderbuffer ); - } - else - { - //create temporary buffer - gl._renderbuffers_pool[ key ] = renderbuffer = gl.createRenderbuffer(); - renderbuffer.time = now; - renderbuffer.width = this.width; - renderbuffer.height = this.height; - gl.bindRenderbuffer( gl.RENDERBUFFER, renderbuffer ); - - //destroy after one minute - setTimeout( inner_check_destroy.bind(renderbuffer), 1000*60 ); - } - } - else - { - renderbuffer = gl._renderbuffer = gl._renderbuffer || gl.createRenderbuffer(); - renderbuffer.width = this.width; - renderbuffer.height = this.height; - gl.bindRenderbuffer( gl.RENDERBUFFER, renderbuffer ); - } - - - //bind render buffer for depth or color - if( this.format === gl.DEPTH_COMPONENT ) - gl.renderbufferStorage( gl.RENDERBUFFER, gl.RGBA4, this.width, this.height); - else - gl.renderbufferStorage( gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, this.width, this.height); - - - //clears memory from unused buffer - function inner_check_destroy() - { - if( GL.getTime() - this.time >= 1000*60 ) - { - //console.log("Buffer cleared"); - gl.deleteRenderbuffer( gl._renderbuffers_pool[ key ] ); - delete gl._renderbuffers_pool[ key ]; - } - else - setTimeout( inner_check_destroy.bind(this), 1000*60 ); - } - - - //create to store depth - /* - if (this.width != renderbuffer.width || this.height != renderbuffer.height ) { - renderbuffer.width = this.width; - renderbuffer.height = this.height; - gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, this.width, this.height); - } - */ - - gl.viewport(0, 0, this.width, this.height); - - //if(gl._current_texture_drawto) - // throw("Texture.drawTo: Cannot use drawTo from inside another drawTo"); - - gl._current_texture_drawto = this; - gl._current_fbo_color = framebuffer; - gl._current_fbo_depth = renderbuffer; - - if(this.texture_type == gl.TEXTURE_2D) - { - if( this.format !== gl.DEPTH_COMPONENT ) - { - gl.framebufferTexture2D( gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, this.handler, 0 ); - gl.framebufferRenderbuffer( gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, renderbuffer ); - } - else - { - gl.framebufferRenderbuffer( gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, renderbuffer ); - gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.TEXTURE_2D, this.handler, 0); - } - callback(this, params); - } - else if(this.texture_type == gl.TEXTURE_CUBE_MAP) - { - //bind the fixed ones out of the loop to save calls - if( this.format !== gl.DEPTH_COMPONENT ) - gl.framebufferRenderbuffer( gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, renderbuffer ); - else - gl.framebufferRenderbuffer( gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, renderbuffer ); - - //for every face of the cubemap - for(var i = 0; i < 6; i++) - { - if( this.format !== gl.DEPTH_COMPONENT ) - gl.framebufferTexture2D( gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, this.handler, 0); - else - gl.framebufferTexture2D( gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, this.handler, 0 ); - callback(this,i, params); - } - } - - this.data = null; - - gl._current_texture_drawto = null; - gl._current_fbo_color = null; - gl._current_fbo_depth = null; - - gl.bindFramebuffer( gl.FRAMEBUFFER, old_fbo ); - gl.bindRenderbuffer(gl.RENDERBUFFER, null); - gl.viewport(v[0], v[1], v[2], v[3]); - - return this; -} - -/** -* Static version of drawTo meant to be used with several buffers -* @method drawToColorAndDepth -* @param {Texture} color_texture -* @param {Texture} depth_texture -* @param {Function} callback -*/ -Texture.drawTo = function( color_textures, callback, depth_texture ) -{ - var w = -1, - h = -1, - type = null; - - if(!color_textures && !depth_texture) - throw("Textures missing in drawTo"); - - if(color_textures && color_textures.length) - { - for(var i = 0; i < color_textures.length; i++) - { - var t = color_textures[i]; - if(w == -1) - w = t.width; - else if(w != t.width) - throw("Cannot use Texture.drawTo if textures have different dimensions"); - if(h == -1) - h = t.height; - else if(h != t.height) - throw("Cannot use Texture.drawTo if textures have different dimensions"); - if(type == null) //first one defines the type - type = t.type; - else if (type != t.type) - throw("Cannot use Texture.drawTo if textures have different data type, all must have the same type"); - } - } - else - { - w = depth_texture.width; - h = depth_texture.height; - } - - var ext = gl.extensions["WEBGL_draw_buffers"]; - if(!ext && color_textures && color_textures.length > 1) - throw("Rendering to several textures not supported"); - - var v = gl.getViewport(); - gl._framebuffer = gl._framebuffer || gl.createFramebuffer(); - gl.bindFramebuffer( gl.FRAMEBUFFER, gl._framebuffer ); - - gl.viewport( 0, 0, w, h ); - - var renderbuffer = null; - if( depth_texture && depth_texture.format !== gl.DEPTH_COMPONENT || depth_texture.type != gl.UNSIGNED_INT ) - throw("Depth texture must be of format: gl.DEPTH_COMPONENT and type: gl.UNSIGNED_INT"); - - if( depth_texture ) - { - gl.framebufferTexture2D( gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.TEXTURE_2D, depth_texture.handler, 0); - } - else //create a temporary depth renderbuffer - { - //create renderbuffer for depth - renderbuffer = gl._renderbuffer = gl._renderbuffer || gl.createRenderbuffer(); - renderbuffer.width = w; - renderbuffer.height = h; - gl.bindRenderbuffer(gl.RENDERBUFFER, renderbuffer ); - gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, w, h); - - gl.framebufferRenderbuffer( gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, renderbuffer ); - } - - if( color_textures ) - { - var order = []; //draw_buffers request the use of an array with the order of the attachments - for(var i = 0; i < color_textures.length; i++) - { - var t = color_textures[i]; - gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0 + i, gl.TEXTURE_2D, t.handler, 0); - order.push( gl.COLOR_ATTACHMENT0 + i ); - } - - if(color_textures.length > 1) - ext.drawBuffersWEBGL( order ); - } - else //create temporary color render buffer - { - var color_renderbuffer = this._color_renderbuffer = this._color_renderbuffer || gl.createRenderbuffer(); - color_renderbuffer.width = w; - color_renderbuffer.height = h; - - gl.bindRenderbuffer( gl.RENDERBUFFER, color_renderbuffer ); - gl.renderbufferStorage( gl.RENDERBUFFER, gl.RGBA4, w, h ); - - gl.framebufferRenderbuffer( gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, color_renderbuffer ); - } - - var complete = gl.checkFramebufferStatus( gl.FRAMEBUFFER ); - if(complete !== gl.FRAMEBUFFER_COMPLETE) - throw("FBO not complete: " + complete); - - callback(); - - //clear data - if(color_textures.length) - for(var i = 0; i < color_textures.length; ++i) - color_textures[i].data = null; - - gl.bindFramebuffer(gl.FRAMEBUFFER, null); - - gl.viewport(v[0], v[1], v[2], v[3]); -} - -/** -* Similar to drawTo but it also stores the depth in a depth texture -* @method drawToColorAndDepth -* @param {Texture} color_texture -* @param {Texture} depth_texture -* @param {Function} callback -*/ -Texture.drawToColorAndDepth = function( color_texture, depth_texture, callback ) { - var gl = color_texture.gl; //static function - - if(depth_texture.width != color_texture.width || depth_texture.height != color_texture.height) - throw("Different size between color texture and depth texture"); - - var v = gl.getViewport(); - - gl._framebuffer = gl._framebuffer || gl.createFramebuffer(); - - gl.bindFramebuffer( gl.FRAMEBUFFER, gl._framebuffer); - - gl.viewport(0, 0, color_texture.width, color_texture.height); - - gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, color_texture.handler, 0); - gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.TEXTURE_2D, depth_texture.handler, 0); - - callback(); - - color_texture.data = null; - depth_texture.data = null; - - gl.bindFramebuffer(gl.FRAMEBUFFER, null); - - gl.viewport(v[0], v[1], v[2], v[3]); -} - - - -/** -* Copy content of one texture into another -* TODO: check using copyTexImage2D -* @method copyTo -* @param {GL.Texture} target_texture -* @param {GL.Shader} [shader=null] optional shader to apply while copying -* @param {Object} [uniforms=null] optional uniforms for the shader -*/ -Texture.prototype.copyTo = function( target_texture, shader, uniforms ) { - var that = this; - var gl = this.gl; - if(!target_texture) - throw("target_texture required"); - - //save state - var previous_fbo = gl.getParameter( gl.FRAMEBUFFER_BINDING ); - var viewport = gl.getViewport(); - - if(!shader) - shader = this.texture_type == gl.TEXTURE_2D ? GL.Shader.getScreenShader() : GL.Shader.getCubemapCopyShader(); - - //render - gl.disable( gl.BLEND ); - gl.disable( gl.DEPTH_TEST ); - if(shader && uniforms) - shader.uniforms( uniforms ); - - //reuse fbo - var fbo = gl.__copy_fbo; - if(!fbo) - fbo = gl.__copy_fbo = gl.createFramebuffer(); - gl.bindFramebuffer( gl.FRAMEBUFFER, fbo ); - - gl.viewport(0,0,target_texture.width, target_texture.height); - if(this.texture_type == gl.TEXTURE_2D) - { - //regular color texture - if(this.format !== gl.DEPTH_COMPONENT && this.format !== gl.DEPTH_STENCIL ) - { - /* doesnt work - if( this.width == target_texture.width && this.height == target_texture.height && this.format == target_texture.format) - { - gl.framebufferTexture2D( gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, this.handler, 0); - gl.bindTexture( target_texture.texture_type, target_texture.handler ); - gl.copyTexImage2D( target_texture.texture_type, 0, this.format, 0, 0, target_texture.width, target_texture.height, 0); - } - else - */ - { - gl.framebufferTexture2D( gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, target_texture.handler, 0); - this.toViewport( shader ); - } - } - else //copying a depth texture is harder - { - var color_renderbuffer = gl._color_renderbuffer = gl._color_renderbuffer || gl.createRenderbuffer(); - var w = color_renderbuffer.width = target_texture.width; - var h = color_renderbuffer.height = target_texture.height; - - //attach color render buffer - gl.bindRenderbuffer( gl.RENDERBUFFER, color_renderbuffer ); - gl.renderbufferStorage( gl.RENDERBUFFER, gl.RGBA4, w, h ); - gl.framebufferRenderbuffer( gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, color_renderbuffer ); - - //attach depth texture - var attachment_point = target_texture.format == gl.DEPTH_STENCIL ? gl.DEPTH_STENCIL_ATTACHMENT : gl.DEPTH_ATTACHMENT; - gl.framebufferTexture2D( gl.FRAMEBUFFER, attachment_point, gl.TEXTURE_2D, target_texture.handler, 0); - - var complete = gl.checkFramebufferStatus( gl.FRAMEBUFFER ); - if(complete !== gl.FRAMEBUFFER_COMPLETE) - throw("FBO not complete: " + complete); - - //enable depth test? - gl.enable( gl.DEPTH_TEST ); - gl.depthFunc( gl.ALWAYS ); - gl.colorMask( false,false,false,false ); - //call shader that overwrites depth values - shader = GL.Shader.getCopyDepthShader(); - this.toViewport( shader ); - gl.colorMask( true,true,true,true ); - gl.disable( gl.DEPTH_TEST ); - gl.depthFunc( gl.LEQUAL ); - gl.framebufferRenderbuffer( gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, null ); - gl.framebufferTexture2D( gl.FRAMEBUFFER, attachment_point, gl.TEXTURE_2D, null, 0); - } - } - else if(this.texture_type == gl.TEXTURE_CUBE_MAP) - { - shader.uniforms({u_texture: 0}); - var rot_matrix = GL.temp_mat3; - for(var i = 0; i < 6; i++) - { - gl.framebufferTexture2D( gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, target_texture.handler, 0); - var face_info = GL.Texture.cubemap_camera_parameters[ i ]; - mat3.identity( rot_matrix ); - rot_matrix.set( face_info.right, 0 ); - rot_matrix.set( face_info.up, 3 ); - rot_matrix.set( face_info.dir, 6 ); - //mat3.invert(rot_matrix,rot_matrix); - this.toViewport( shader,{ u_rotation: rot_matrix }); - } - } - - //restore previous state - gl.setViewport(viewport); //restore viewport - gl.bindFramebuffer( gl.FRAMEBUFFER, previous_fbo ); //restore fbo - - //generate mipmaps when needed - if (target_texture.minFilter && target_texture.minFilter != gl.NEAREST && target_texture.minFilter != gl.LINEAR) { - target_texture.bind(); - gl.generateMipmap(target_texture.texture_type); - target_texture.has_mipmaps = true; - } - - target_texture.data = null; - gl.bindTexture( target_texture.texture_type, null ); //disable - return this; -} - - -/** -* Similar to CopyTo, but more specific, only for color texture_2D. It doesnt change the blend flag -* @method blit -* @param {GL.Texture} target_texture -* @param {GL.Shader} [shader=null] optional shader to apply while copying -* @param {Object} [uniforms=null] optional uniforms for the shader -*/ -Texture.prototype.blit = (function(){ - var viewport = new Float32Array(4); - - return function( target_texture, shader, uniforms ) { - var that = this; - var gl = this.gl; - - if ( this.texture_type != gl.TEXTURE_2D || this.format === gl.DEPTH_COMPONENT || this.format === gl.DEPTH_STENCIL ) - throw("blit only support TEXTURE_2D of RGB or RGBA. use copyTo instead"); - - //save state - var previous_fbo = gl.getParameter( gl.FRAMEBUFFER_BINDING ); - viewport.set( gl.viewport_data ); - - shader = shader || GL.Shader.getScreenShader(); - if(shader && uniforms) - shader.uniforms( uniforms ); - - //reuse fbo - var fbo = gl.__copy_fbo; - if(!fbo) - fbo = gl.__copy_fbo = gl.createFramebuffer(); - gl.bindFramebuffer( gl.FRAMEBUFFER, fbo ); - - gl.viewport(0,0,target_texture.width, target_texture.height); - gl.framebufferTexture2D( gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, target_texture.handler, 0); - - this.bind(0); - shader.draw( GL.Mesh.getScreenQuad(), gl.TRIANGLES ); - - //restore previous state - gl.setViewport(viewport); //restore viewport - gl.bindFramebuffer( gl.FRAMEBUFFER, previous_fbo ); //restore fbo - - target_texture.data = null; - gl.bindTexture( target_texture.texture_type, null ); //disable - return this; - } -})(); - -/** -* Render texture in a quad to full viewport size -* @method toViewport -* @param {Shader} shader to apply, otherwise a default textured shader is applied [optional] -* @param {Object} uniforms for the shader if needed [optional] -*/ -Texture.prototype.toViewport = function(shader, uniforms) -{ - shader = shader || Shader.getScreenShader(); - var mesh = Mesh.getScreenQuad(); - this.bind(0); - //shader.uniforms({u_texture: 0}); //never changes - if(uniforms) - shader.uniforms(uniforms); - shader.draw( mesh, gl.TRIANGLES ); -} - -/** -* Fills the texture with a constant color (uses gl.clear) -* @method fill -* @param {vec4} color rgba -* @param {boolean} skip_mipmaps if true the mipmaps wont be updated -*/ -Texture.prototype.fill = function(color, skip_mipmaps ) -{ - var old_color = gl.getParameter( gl.COLOR_CLEAR_VALUE ); - gl.clearColor( color[0], color[1], color[2], color[3] ); - this.drawTo( function() { - gl.clear( gl.COLOR_BUFFER_BIT ); - }); - gl.clearColor( old_color[0], old_color[1], old_color[2], old_color[3] ); - - if (!skip_mipmaps && this.minFilter && this.minFilter != gl.NEAREST && this.minFilter != gl.LINEAR ) { - this.bind(); - gl.generateMipmap( this.texture_type ); - this.has_mipmaps = true; - } -} - -/** -* Render texture in a quad of specified area -* @method renderQuad -* @param {number} x -* @param {number} y -* @param {number} width -* @param {number} height -*/ -Texture.prototype.renderQuad = (function() { - //static variables: less garbage - var identity = mat3.create(); - var pos = vec2.create(); - var size = vec2.create(); - var white = vec4.fromValues(1,1,1,1); - - return (function(x,y,w,h, shader, uniforms) - { - pos[0] = x; pos[1] = y; - size[0] = w; size[1] = h; - - shader = shader || Shader.getQuadShader(this.gl); - var mesh = Mesh.getScreenQuad(this.gl); - this.bind(0); - shader.uniforms({u_texture: 0, u_position: pos, u_color: white, u_size: size, u_viewport: gl.viewport_data.subarray(2,4), u_transform: identity }); - if(uniforms) - shader.uniforms(uniforms); - shader.draw( mesh, gl.TRIANGLES ); - }); -})(); - - -/** -* Applies a blur filter of 5x5 pixels to the texture (be careful using it, it is slow) -* @method applyBlur -* @param {Number} offsetx scalar that multiplies the offset when fetching pixels horizontally (default 1) -* @param {Number} offsety scalar that multiplies the offset when fetching pixels vertically (default 1) -* @param {Number} intensity scalar that multiplies the result (default 1) -* @param {Texture} output_texture [optional] if not passed the output is the own texture -* @param {Texture} temp_texture blur needs a temp texture, if not supplied it will use the temporary textures pool -* @return {Texture} returns the temp_texture in case you want to reuse it -*/ -Texture.prototype.applyBlur = function( offsetx, offsety, intensity, output_texture, temp_texture ) -{ - var that = this; - var gl = this.gl; - if(offsetx === undefined) - offsetx = 1; - if(offsety === undefined) - offsety = 1; - gl.disable( gl.DEPTH_TEST ); - gl.disable( gl.BLEND ); - output_texture = output_texture || this; - var is_temp = !temp_texture; - - //if(this === output_texture && this.texture_type === gl.TEXTURE_CUBE_MAP ) - // throw("cannot use applyBlur in a texture with itself when blurring a CUBE_MAP"); - if(temp_texture === output_texture) - throw("cannot use applyBlur in a texture using as temporary itself"); - - if(output_texture && this.texture_type !== output_texture.texture_type ) - throw("cannot use applyBlur with textures of different texture_type"); - - //if(this.width != output_texture.width || this.height != output_texture.height) - // throw("cannot use applyBlur with an output texture of different size, it doesnt work"); - - //save state - var current_fbo = gl.getParameter( gl.FRAMEBUFFER_BINDING ); - var viewport = gl.getViewport(); - - //reuse fbo - var fbo = gl.__copy_fbo; - if(!fbo) - fbo = gl.__copy_fbo = gl.createFramebuffer(); - gl.bindFramebuffer( gl.FRAMEBUFFER, fbo ); - gl.viewport(0,0, this.width, this.height); - - if( this.texture_type === gl.TEXTURE_2D ) - { - var shader = GL.Shader.getBlurShader(); - - if(!temp_texture) - temp_texture = GL.Texture.getTemporary( this.width, this.height, this ); - - //horizontal blur - gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, temp_texture.handler, 0); - this.toViewport( shader, {u_texture: 0, u_intensity: intensity, u_offset: [0, offsety / this.height ] }); - - //vertical blur - gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, output_texture.handler, 0); - gl.viewport(0,0,output_texture.width, output_texture.height); - temp_texture.toViewport( shader, {u_intensity: intensity, u_offset: [offsetx / temp_texture.width, 0] }); - - if(is_temp) - GL.Texture.releaseTemporary( temp_texture ); - } - else if( this.texture_type === gl.TEXTURE_CUBE_MAP ) - { - //var weights = new Float32Array([ 0.16/0.98, 0.15/0.98, 0.12/0.98, 0.09/0.98, 0.05/0.98 ]); - //var weights = new Float32Array([ 0.05/0.98, 0.09/0.98, 0.12/0.98, 0.15/0.98, 0.16/0.98, 0.15/0.98, 0.12/0.98, 0.09/0.98, 0.05/0.98, 0.0 ]); //extra 0 to avoid mat3 - var shader = GL.Shader.getCubemapBlurShader(); - shader.uniforms({u_texture: 0, u_intensity: intensity, u_offset: [ offsetx / this.width, offsety / this.height ] }); - this.bind(0); - var mesh = Mesh.getScreenQuad(); - mesh.bindBuffers( shader ); - shader.bind(); - - var destination = null; - - if(!temp_texture && output_texture == this) //we need a temporary texture - destination = temp_texture = GL.Texture.getTemporary( output_texture.width, output_texture.height, output_texture ); - else - destination = output_texture; //blur directly to output texture - - var rot_matrix = GL.temp_mat3; - for(var i = 0; i < 6; ++i) - { - gl.framebufferTexture2D( gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, destination.handler, 0); - var face_info = GL.Texture.cubemap_camera_parameters[ i ]; - mat3.identity(rot_matrix); - rot_matrix.set( face_info.right, 0 ); - rot_matrix.set( face_info.up, 3 ); - rot_matrix.set( face_info.dir, 6 ); - //mat3.invert(rot_matrix,rot_matrix); - shader._setUniform( "u_rotation", rot_matrix ); - gl.drawArrays( gl.TRIANGLES, 0, 6 ); - } - - mesh.unbindBuffers( shader ); - - if(temp_texture) //copy back - temp_texture.copyTo( output_texture ); - - if(temp_texture && is_temp) //release temp - GL.Texture.releaseTemporary( temp_texture ); - } - - //restore previous state - gl.setViewport(viewport); //restore viewport - gl.bindFramebuffer( gl.FRAMEBUFFER, current_fbo ); //restore fbo - - output_texture.data = null; - - //generate mipmaps when needed - if (output_texture.minFilter && output_texture.minFilter != gl.NEAREST && output_texture.minFilter != gl.LINEAR) { - output_texture.bind(); - gl.generateMipmap(output_texture.texture_type); - output_texture.has_mipmaps = true; - } - - gl.bindTexture( output_texture.texture_type, null ); //disable -} - - -/** -* Loads and uploads a texture from a url -* @method Texture.fromURL -* @param {String} url -* @param {Object} options -* @param {Function} on_complete -* @return {Texture} the texture -*/ -Texture.fromURL = function( url, options, on_complete, gl ) { - gl = gl || global.gl; - - options = options || {}; - options = Object.create(options); //creates a new options using the old one as prototype - - var texture = options.texture || new GL.Texture(1, 1, options, gl); - - if(url.length < 64) - texture.url = url; - texture.bind(); - var default_color = options.temp_color || Texture.loading_color; - //Texture.setUploadOptions(options); - gl.pixelStorei(gl.UNPACK_ALIGNMENT, 4); - var temp_color = options.type == gl.FLOAT ? new Float32Array(default_color) : new Uint8Array(default_color); - gl.texImage2D( gl.TEXTURE_2D, 0, texture.format, texture.width, texture.height, 0, texture.format, texture.type, temp_color ); - gl.bindTexture( texture.texture_type, null ); //disable - texture.ready = false; - - var ext = null; - if( options.extension ) //to force format - ext = options.extension; - - if(!ext && url.length < 512) //avoid base64 urls - { - var base = url; - var pos = url.indexOf("?"); - if(pos != -1) - base = url.substr(0,pos); - pos = base.lastIndexOf("."); - if(pos != -1) - ext = base.substr(pos+1).toLowerCase(); - } - - if( ext == "dds") - { - var ext = gl.getExtension("WEBKIT_WEBGL_compressed_texture_s3tc") || gl.getExtension("WEBGL_compressed_texture_s3tc"); - var new_texture = new GL.Texture(0,0, options, gl); - DDS.loadDDSTextureEx(gl, ext, url, new_texture.handler, true, function(t) { - texture.texture_type = t.texture_type; - texture.handler = t; - delete texture["ready"]; //texture.ready = true; - if(on_complete) - on_complete(texture, url); - }); - } - else if( ext == "tga" ) - { - HttpRequest( url, null, function(data) { - var img_data = GL.Texture.parseTGA(data); - if(!img_data) - return; - options.texture = texture; - if(img_data.format == "RGB") - texture.format = gl.RGB; - texture = GL.Texture.fromMemory( img_data.width, img_data.height, img_data.pixels, options ); - delete texture["ready"]; //texture.ready = true; - if(on_complete) - on_complete( texture, url ); - },null,{ binary: true }); - } - else //png,jpg,webp,... - { - var image = new Image(); - image.src = url; - var that = this; - image.onload = function() - { - options.texture = texture; - GL.Texture.fromImage(this, options); - delete texture["ready"]; //texture.ready = true; - if(on_complete) - on_complete(texture, url); - } - image.onerror = function() - { - if(on_complete) - on_complete(null); - } - } - - return texture; -}; - -Texture.parseTGA = function(data) -{ - if(!data || data.constructor !== ArrayBuffer) - throw( "TGA: data must be ArrayBuffer"); - data = new Uint8Array(data); - var TGAheader = new Uint8Array( [0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0] ); - var TGAcompare = data.subarray(0,12); - for(var i = 0; i < TGAcompare.length; i++) - if(TGAheader[i] != TGAcompare[i]) - { - console.error("TGA header is not valid"); - return null; //not a TGA - } - - var header = data.subarray(12,18); - var img = {}; - img.width = header[1] * 256 + header[0]; - img.height = header[3] * 256 + header[2]; - img.bpp = header[4]; - img.bytesPerPixel = img.bpp / 8; - img.imageSize = img.width * img.height * img.bytesPerPixel; - img.pixels = data.subarray(18,18+img.imageSize); - img.pixels = new Uint8Array( img.pixels ); //clone - if( (header[5] & (1<<4)) == 0) //hack, needs swap - { - //TGA comes in BGR format so we swap it, this is slooooow - for(var i = 0; i < img.imageSize; i+= img.bytesPerPixel) - { - var temp = img.pixels[i]; - img.pixels[i] = img.pixels[i+2]; - img.pixels[i+2] = temp; - } - header[5] |= 1<<4; //mark as swaped - img.format = img.bpp == 32 ? "RGBA" : "RGB"; - } - else - img.format = img.bpp == 32 ? "RGBA" : "RGB"; - //some extra bytes to avoid alignment problems - //img.pixels = new Uint8Array( img.imageSize + 14); - //img.pixels.set( data.subarray(18,18+img.imageSize), 0); - img.flipY = true; - //img.format = img.bpp == 32 ? "BGRA" : "BGR"; - //trace("TGA info: " + img.width + "x" + img.height ); - return img; -} - -/** -* Create a texture from an Image -* @method Texture.fromImage -* @param {Image} image -* @param {Object} options -* @return {Texture} the texture -*/ -Texture.fromImage = function( image, options ) { - options = options || {}; - - var texture = options.texture || new GL.Texture( image.width, image.height, options); - texture.uploadImage( image, options ); - - texture.bind(); - gl.texParameteri(texture.texture_type, gl.TEXTURE_MAG_FILTER, texture.magFilter ); - gl.texParameteri(texture.texture_type, gl.TEXTURE_MIN_FILTER, texture.minFilter ); - gl.texParameteri(texture.texture_type, gl.TEXTURE_WRAP_S, texture.wrapS ); - gl.texParameteri(texture.texture_type, gl.TEXTURE_WRAP_T, texture.wrapT ); - - if (GL.isPowerOfTwo(texture.width) && GL.isPowerOfTwo(texture.height) ) - { - if( options.minFilter && options.minFilter != gl.NEAREST && options.minFilter != gl.LINEAR) - { - texture.bind(); - gl.generateMipmap(texture.texture_type); - texture.has_mipmaps = true; - } - } - else - { - //no mipmaps supported - gl.texParameteri(texture.texture_type, gl.TEXTURE_MIN_FILTER, GL.LINEAR ); - gl.texParameteri(texture.texture_type, gl.TEXTURE_WRAP_S, GL.CLAMP_TO_EDGE ); - gl.texParameteri(texture.texture_type, gl.TEXTURE_WRAP_T, GL.CLAMP_TO_EDGE ); - texture.has_mipmaps = false; - } - gl.bindTexture(texture.texture_type, null); //disable - texture.data = image; - if(options.keep_image) - texture.img = image; - return texture; -}; - -/** -* Create a texture from a Video -* @method Texture.fromVideo -* @param {Video} video -* @param {Object} options -* @return {Texture} the texture -*/ -Texture.fromVideo = function(video, options) { - options = options || {}; - - var texture = options.texture || new GL.Texture(video.videoWidth, video.videoHeight, options); - texture.bind(); - texture.uploadImage( video, options ); - if (options.minFilter && options.minFilter != gl.NEAREST && options.minFilter != gl.LINEAR) { - texture.bind(); - gl.generateMipmap(texture.texture_type); - texture.has_mipmaps = true; - texture.data = video; - } - gl.bindTexture(texture.texture_type, null); //disable - return texture; -}; - -/** -* Create a clone of a texture -* @method Texture.fromTexture -* @param {Texture} old_texture -* @param {Object} options -* @return {Texture} the texture -*/ -Texture.fromTexture = function( old_texture, options) { - options = options || {}; - var texture = new GL.Texture( old_texture.width, old_texture.height, options ); - old_texture.copyTo( texture ); - return texture; -}; - -Texture.prototype.clone = function( options ) -{ - var old_options = this.getProperties(); - if(options) - for(var i in options) - old_options[i] = options[i]; - return Texture.fromTexture( this, old_options); -} - -/** -* Create a texture from an ArrayBuffer containing the pixels -* @method Texture.fromTexture -* @param {number} width -* @param {number} height -* @param {ArrayBuffer} pixels -* @param {Object} options -* @return {Texture} the texture -*/ -Texture.fromMemory = function( width, height, pixels, options) //format in options as format -{ - options = options || {}; - - var texture = options.texture || new GL.Texture(width, height, options); - Texture.setUploadOptions(options); - texture.bind(); - - if(pixels.constructor === Array) - { - if(options.type == gl.FLOAT) - pixels = new Float32Array( pixels ); - else if(options.type == GL.HALF_FLOAT || options.type == GL.HALF_FLOAT_OES) - pixels = new Uint16Array( pixels ); //gl.UNSIGNED_SHORT_4_4_4_4 is only for texture that are SHORT per pixel, not per channel! - else - pixels = new Uint8Array( pixels ); - } - - gl.texImage2D( gl.TEXTURE_2D, 0, texture.format, width, height, 0, texture.format, texture.type, pixels ); - texture.width = width; - texture.height = height; - texture.data = pixels; - if (options.minFilter && options.minFilter != gl.NEAREST && options.minFilter != gl.LINEAR) { - gl.generateMipmap(gl.TEXTURE_2D); - texture.has_mipmaps = true; - } - gl.bindTexture(texture.texture_type, null); //disable - return texture; -}; - -/** -* Create a texture from an ArrayBuffer containing the pixels -* @method Texture.fromDDSInMemory -* @param {ArrayBuffer} DDS data -* @param {Object} options -* @return {Texture} the texture -*/ -Texture.fromDDSInMemory = function(data, options) //format in options as format -{ - options = options || {}; - - var texture = options.texture || new GL.Texture(0, 0, options); - GL.Texture.setUploadOptions(options); - texture.bind(); - - var ext = gl.getExtension("WEBKIT_WEBGL_compressed_texture_s3tc") || gl.getExtension("WEBGL_compressed_texture_s3tc"); - DDS.loadDDSTextureFromMemoryEx(gl, ext, data, texture, true ); - - gl.bindTexture(texture.texture_type, null); //disable - return texture; -}; - -/** -* Create a generative texture from a shader ( must GL.Shader.getScreenShader as reference for the shader ) -* @method Texture.fromShader -* @param {number} width -* @param {number} height -* @param {Shader} shader -* @param {Object} options -* @return {Texture} the texture -*/ -Texture.fromShader = function(width, height, shader, options) { - options = options || {}; - - var texture = new GL.Texture( width, height, options ); - //copy content - texture.drawTo(function() { - gl.disable( gl.BLEND ); - gl.disable( gl.DEPTH_TEST ); - gl.disable( gl.CULL_FACE ); - var mesh = Mesh.getScreenQuad(); - shader.draw( mesh ); - }); - - return texture; -}; - -/** -* Create a cubemap texture from a set of 6 images -* @method Texture.cubemapFromImages -* @param {Array} images -* @param {Object} options -* @return {Texture} the texture -*/ -Texture.cubemapFromImages = function(images, options) { - options = options || {}; - if(images.length != 6) - throw "missing images to create cubemap"; - - var width = images[0].width; - var height = images[0].height; - options.texture_type = gl.TEXTURE_CUBE_MAP; - - var texture = null; - - if(options.texture) - { - texture = options.texture; - texture.width = width; - texture.height = height; - } - else - texture = new GL.Texture( width, height, options ); - - Texture.setUploadOptions(options); - texture.bind(); - - try { - - for(var i = 0; i < 6; i++) - gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X+i, 0, texture.format, texture.format, texture.type, images[i]); - texture.data = images; - } catch (e) { - if (location.protocol == 'file:') { - throw 'image not loaded for security reasons (serve this page over "http://" instead)'; - } else { - throw 'image not loaded for security reasons (image must originate from the same ' + - 'domain as this page or use Cross-Origin Resource Sharing)'; - } - } - if (options.minFilter && options.minFilter != gl.NEAREST && options.minFilter != gl.LINEAR) { - gl.generateMipmap(gl.TEXTURE_CUBE_MAP); - texture.has_mipmaps = true; - } - - texture.unbind(); - return texture; -}; - -/** -* Create a cubemap texture from a single image that contains all six images -* If it is a cross, it must be horizontally aligned, and options.is_cross must be equal to the column where the top and bottom are located (usually 1 or 2) -* otherwise it assumes the 6 images are arranged vertically, in the order of OpenGL: +X, -X, +Y, -Y, +Z, -Z -* @method Texture.cubemapFromImage -* @param {Image} image -* @param {Object} options -* @return {Texture} the texture -*/ -Texture.cubemapFromImage = function( image, options ) { - options = options || {}; - - if(image.width != (image.height / 6) && image.height % 6 != 0 && !options.faces && !options.is_polar ) - { - console.error( "Cubemap image not valid, only 1x6 (vertical) or 6x3 (cross) formats. Check size:", image.width, image.height ); - return null; - } - - var width = image.width; - var height = image.height; - - if(options.is_polar) - { - var size = options.size || GL.nearestPowerOfTwo( image.height ); - var temp_tex = GL.Texture.fromImage( image, { ignore_pot:true, wrap: gl.REPEAT, filter: gl.LINEAR } ); - var cubemap = new GL.Texture( size, size, { texture_type: gl.TEXTURE_CUBE_MAP, format: gl.RGBA }); - if(options.texture) - { - var old_tex = options.texture; - for(var i in cubemap) - old_tex[i] = cubemap[i]; - cubemap = old_tex; - } - var rot_matrix = mat3.create(); - var uniforms = { u_texture:0, u_rotation: rot_matrix }; - gl.disable( gl.DEPTH_TEST ); - gl.disable( gl.BLEND ); - var shader = GL.Shader.getPolarToCubemapShader(); - cubemap.drawTo(function(t,i){ - var face_info = GL.Texture.cubemap_camera_parameters[ i ]; - mat3.identity( rot_matrix ); - rot_matrix.set( face_info.right, 0 ); - rot_matrix.set( face_info.up, 3 ); - rot_matrix.set( face_info.dir, 6 ); - temp_tex.toViewport( shader, uniforms ); - }); - if(options.keep_image) - cubemap.img = image; - return cubemap; - } - else if(options.is_cross !== undefined) - { - options.faces = Texture.generateCubemapCrossFacesInfo(image.width, options.is_cross); - width = height = image.width / 4; - } - else if(options.faces) - { - width = options.width || options.faces[0].width; - height = options.height || options.faces[0].height; - } - else - height /= 6; - - if(width != height) - { - console.log("Texture not valid, width and height for every face must be square"); - return null; - } - - var size = width; - options.no_flip = true; - - var images = []; - for(var i = 0; i < 6; i++) - { - var canvas = createCanvas( size, size ); - var ctx = canvas.getContext("2d"); - if(options.faces) - ctx.drawImage(image, options.faces[i].x, options.faces[i].y, options.faces[i].width || size, options.faces[i].height || size, 0,0, size, size ); - else - ctx.drawImage(image, 0, height*i, width, height, 0,0, size, size ); - images.push(canvas); - //document.body.appendChild(canvas); //debug - } - - var texture = Texture.cubemapFromImages(images, options); - if(options.keep_image) - texture.img = image; - return texture; -}; - -/** -* Given the width and the height of an image, and in which column is the top and bottom sides of the cubemap, it gets the info to pass to Texture.cubemapFromImage in options.faces -* @method Texture.generateCubemapCrossFaces -* @param {number} width of the CROSS image (not the side image) -* @param {number} column the column where the top and the bottom is located -* @return {Object} object to pass to Texture.cubemapFromImage in options.faces -*/ -Texture.generateCubemapCrossFacesInfo = function(width, column) -{ - if(column === undefined) - column = 1; - var s = width / 4; - - return [ - { x: 2*s, y: s, width: s, height: s }, //+x - { x: 0, y: s, width: s, height: s }, //-x - { x: column*s, y: 0, width: s, height: s }, //+y - { x: column*s, y: 2*s, width: s, height: s }, //-y - { x: s, y: s, width: s, height: s }, //+z - { x: 3*s, y: s, width: s, height: s } //-z - ]; -} - -/** -* Create a cubemap texture from a single image url that contains the six images -* if it is a cross, it must be horizontally aligned, and options.is_cross must be equal to the column where the top and bottom are located (usually 1 or 2) -* otherwise it assumes the 6 images are arranged vertically. -* @method Texture.cubemapFromURL -* @param {Image} image -* @param {Object} options -* @param {Function} on_complete callback -* @return {Texture} the texture -*/ -Texture.cubemapFromURL = function( url, options, on_complete ) { - options = options || {}; - options = Object.create(options); //creates a new options using the old one as prototype - options.texture_type = gl.TEXTURE_CUBE_MAP; - var texture = options.texture || new GL.Texture(1, 1, options); - - texture.bind(); - Texture.setUploadOptions(options); - var default_color = options.temp_color || [0,0,0,255]; - var temp_color = options.type == gl.FLOAT ? new Float32Array(default_color) : new Uint8Array(default_color); - - for(var i = 0; i < 6; i++) - gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X+i, 0, texture.format, 1, 1, 0, texture.format, texture.type, temp_color); - gl.bindTexture(texture.texture_type, null); //disable - texture.ready = false; - - var image = new Image(); - image.src = url; - var that = this; - image.onload = function() - { - options.texture = texture; - texture = GL.Texture.cubemapFromImage(this, options); - if(texture) - delete texture["ready"]; //texture.ready = true; - if(on_complete) - on_complete(texture); - } - - return texture; -}; - -/** -* returns an ArrayBuffer with the pixels in the texture, they are fliped in Y -* Warn: If cubemap it only returns the pixels of the first face! use getCubemapPixels instead -* @method getPixels -* @param {number} cubemap_face [optional] the index of the cubemap face to read (ignore if texture_2D) -* @param {number} mipmap level [optional, default is 0] -* @return {ArrayBuffer} the data ( Uint8Array, Uint16Array or Float32Array ) -*/ -Texture.prototype.getPixels = function( cubemap_face, mipmap_level ) -{ - mipmap_level = mipmap_level || 0; - var gl = this.gl; - var v = gl.getViewport(); - var old_fbo = gl.getParameter( gl.FRAMEBUFFER_BINDING ); - - if(this.format == gl.DEPTH_COMPONENT) - throw("cannot use getPixels in depth textures"); - - gl.disable( gl.DEPTH_TEST ); - - //reuse fbo - var fbo = gl.__copy_fbo; - if(!fbo) - fbo = gl.__copy_fbo = gl.createFramebuffer(); - gl.bindFramebuffer( gl.FRAMEBUFFER, fbo ); - - var buffer = null; - - var width = this.width >> mipmap_level; - var height = this.height >> mipmap_level; - gl.viewport(0, 0, width, height); - - if(this.texture_type == gl.TEXTURE_2D) - gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, this.handler, mipmap_level); - else if(this.texture_type == gl.TEXTURE_CUBE_MAP) - gl.framebufferTexture2D( gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_CUBE_MAP_POSITIVE_X + (cubemap_face || 0), this.handler, mipmap_level); - - var channels = this.format == gl.RGB ? 3 : 4; - channels = 4; //WEBGL DOES NOT SUPPORT READING 3 CHANNELS ONLY, YET... - var type = this.type; - //type = gl.UNSIGNED_BYTE; //WEBGL DOES NOT SUPPORT READING FLOAT seems, YET... 23/5/18 now it seems it does now - - if(type == gl.UNSIGNED_BYTE) - buffer = new Uint8Array( width * height * channels ); - else if(type == GL.HALF_FLOAT || type == GL.HALF_FLOAT_OES) //previously half float couldnot be read - buffer = new Uint16Array( width * height * channels ); //gl.UNSIGNED_SHORT_4_4_4_4 is only for texture that are SHORT per pixel, not per channel! - else - buffer = new Float32Array( width * height * channels ); - - gl.readPixels( 0,0, width, height, channels == 3 ? gl.RGB : gl.RGBA, type, buffer ); //NOT SUPPORTED FLOAT or RGB BY WEBGL YET - - //restore - gl.bindFramebuffer(gl.FRAMEBUFFER, old_fbo ); - gl.viewport(v[0], v[1], v[2], v[3]); - return buffer; -} - -/** -* uploads some pixels to the texture (see uploadData method for more options) -* @method setPixels -* @param {ArrayBuffer} data gl.UNSIGNED_BYTE or gl.FLOAT data -* @param {Boolean} no_flip do not flip in Y -* @param {Boolean} skip_mipmaps do not update mipmaps when possible -* @param {Number} cubemap_face if the texture is a cubemap, which face -*/ -Texture.prototype.setPixels = function( data, no_flip, skip_mipmaps, cubemap_face ) -{ - var options = { no_flip: no_flip }; - if(cubemap_face) - options.cubemap_face = cubemap_face; - this.uploadData( data, options, skip_mipmaps ); -} - -/** -* returns an array with six arrays containing the pixels of every cubemap face -* @method getCubemapPixels -* @return {Array} the array that has 6 typed arrays containing the pixels -*/ -Texture.prototype.getCubemapPixels = function() -{ - if(this.texture_type !== gl.TEXTURE_CUBE_MAP) - throw("this texture is not a cubemap"); - return [ this.getPixels(0), this.getPixels(1), this.getPixels(2), this.getPixels(3), this.getPixels(4), this.getPixels(5) ]; -} - -/** -* fills a cubemap given an array with typed arrays containing the pixels of 6 faces -* @method setCubemapPixels -* @param {Array} data array that has 6 typed arrays containing the pixels -* @param {bool} noflip if pixels should not be flipped according to Y -*/ -Texture.prototype.setCubemapPixels = function( data_array, no_flip ) -{ - if(this.texture_type !== gl.TEXTURE_CUBE_MAP) - throw("this texture is not a cubemap, it should be created with { texture_type: gl.TEXTURE_CUBE_MAP }"); - for(var i = 0; i < 6; ++i) - this.setPixels( data_array[i], no_flip, i != 5, i ); -} - -/** -* Copy texture content to a canvas -* @method toCanvas -* @param {Canvas} canvas must have the same size, if different the canvas will be resized -* @param {boolean} flip_y optional, flip vertically -* @param {Number} max_size optional, if it is supplied the canvas wont be bigger of max_size (the image will be scaled down) -*/ -Texture.prototype.toCanvas = function( canvas, flip_y, max_size ) -{ - max_size = max_size || 8192; - var gl = this.gl; - - var w = Math.min( this.width, max_size ); - var h = Math.min( this.height, max_size ); - - //cross - if(this.texture_type == gl.TEXTURE_CUBE_MAP) - { - w = w * 4; - h = h * 3; - } - - canvas = canvas || createCanvas( w, h ); - if(canvas.width != w) - canvas.width = w; - if(canvas.height != h) - canvas.height = h; - - var buffer = null; - if(this.texture_type == gl.TEXTURE_2D ) - { - if(this.width != w || this.height != h || this.type != gl.UNSIGNED_BYTE) //resize image to fit the canvas - { - //create a temporary texture - var temp = new GL.Texture(w,h,{ format: gl.RGBA, filter: gl.NEAREST }); - this.copyTo( temp ); - buffer = temp.getPixels(); - } - else - buffer = this.getPixels(); - - var ctx = canvas.getContext("2d"); - var pixels = ctx.getImageData(0,0,w,h); - pixels.data.set( buffer ); - ctx.putImageData(pixels,0,0); - - if(flip_y) - { - var temp = createCanvas(w,h); - var temp_ctx = temp.getContext("2d"); - temp_ctx.translate(0,temp.height); - temp_ctx.scale(1,-1); - temp_ctx.drawImage( canvas, 0, 0, temp.width, temp.height ); - ctx.clearRect(0,0,ctx.canvas.width,ctx.canvas.height); - ctx.drawImage( temp, 0, 0 ); - } - } - else if(this.texture_type == gl.TEXTURE_CUBE_MAP ) - { - var temp_canvas = createCanvas( this.width, this.height ); - var temp_ctx = temp_canvas.getContext("2d"); - var info = GL.Texture.generateCubemapCrossFacesInfo( canvas.width, 1 ); - var ctx = canvas.getContext("2d"); - ctx.fillStyle = "black"; - ctx.fillRect(0,0,canvas.width, canvas.height ); - - var cubemap = this; - if(this.type != gl.UNSIGNED_BYTE) //convert pixels to uint8 as it is the only supported format by the canvas - { - //create a temporary texture - cubemap = new GL.Texture( this.width, this.height, { format: gl.RGBA, texture_type: gl.TEXTURE_CUBE_MAP, filter: gl.NEAREST, type: gl.UNSIGNED_BYTE }); - this.copyTo( cubemap ); - } - - for(var i = 0; i < 6; i++) - { - var pixels = temp_ctx.getImageData(0,0, temp_canvas.width, temp_canvas.height ); - buffer = cubemap.getPixels(i); - pixels.data.set( buffer ); - temp_ctx.putImageData(pixels,0,0); - ctx.drawImage( temp_canvas, info[i].x, info[i].y, temp_canvas.width, temp_canvas.height ); - } - } - - return canvas; -} - - -/** -* returns the texture file in binary format -* @method toBinary -* @param {Boolean} flip_y -* @return {ArrayBuffer} the arraybuffer of the file containing the image -*/ -Texture.binary_extension = "png"; -Texture.prototype.toBinary = function(flip_y, type) -{ - //dump to canvas - var canvas = this.toCanvas(null,flip_y); - //use the slow method (because its sync) - var data = canvas.toDataURL( type ); - var index = data.indexOf(","); - var base64_data = data.substr(index+1); - var binStr = atob( base64_data ); - var len = binStr.length, - arr = new Uint8Array(len); - for (var i=0; i 0 ) - console.warn("this texture is already in the textures pool"); - - var pool = gl._texture_pool; - if(!pool) - pool = gl._texture_pool = []; - tex._pool = getTime(); - pool.push( tex ); - - //do not store too much textures in the textures pool - if( pool.length > 20 ) - { - pool.sort( function(a,b) { return b._pool - a._pool } ); //sort by time - //pool.sort( function(a,b) { return a._key - b._key } ); //sort by size - var tex = pool.pop(); //free the last one - tex._pool = 0; - tex.delete(); - } -} - -//returns the next power of two bigger than size -Texture.nextPOT = function( size ) -{ - return Math.pow( 2, Math.ceil( Math.log(size) / Math.log(2) ) ); -} - -/** -* FBO for FrameBufferObjects, FBOs are used to store the render inside one or several textures -* Supports multibuffer and depthbuffer texture, useful for deferred rendering -* @namespace GL -* @class FBO -* @param {Array} color_textures an array containing the color textures, if not supplied a render buffer will be used -* @param {GL.Texture} depth_texture the depth texture, if not supplied a render buffer will be used -* @param {Bool} stencil create a stencil buffer? -* @constructor -*/ -function FBO( textures, depth_texture, stencil, gl ) -{ - gl = gl || global.gl; - this.gl = gl; - this._context_id = gl.context_id; - - if(textures && textures.constructor !== Array) - throw("FBO textures must be an Array"); - - this.handler = null; - this.width = -1; - this.height = -1; - this.color_textures = []; - this.depth_texture = null; - this.stencil = !!stencil; - - this._stencil_enabled = false; - this._num_binded_textures = 0; - - //assign textures - if((textures && textures.length) || depth_texture) - this.setTextures( textures, depth_texture ); - - //save state - this._old_fbo_handler = null; - this._old_viewport = new Float32Array(4); - this.order = null; -} - -GL.FBO = FBO; - -/** -* Changes the textures binded to this FBO -* @method setTextures -* @param {Array} color_textures an array containing the color textures, if not supplied a render buffer will be used -* @param {GL.Texture} depth_texture the depth texture, if not supplied a render buffer will be used -* @param {Boolean} skip_disable it doenst try to go back to the previous FBO enabled in case there was one -*/ -FBO.prototype.setTextures = function( color_textures, depth_texture, skip_disable ) -{ - //test depth - if( depth_texture && depth_texture.constructor === GL.Texture ) - { - if( depth_texture.format !== GL.DEPTH_COMPONENT && - depth_texture.format !== GL.DEPTH_STENCIL && - depth_texture.format !== GL.DEPTH_COMPONENT16 && - depth_texture.format !== GL.DEPTH_COMPONENT24 && - depth_texture.format !== GL.DEPTH_COMPONENT32F ) - throw("FBO Depth texture must be of format: gl.DEPTH_COMPONENT, gl.DEPTH_STENCIL or gl.DEPTH_COMPONENT16/24/32F (only in webgl2)"); - - if( depth_texture.type != GL.UNSIGNED_SHORT && - depth_texture.type != GL.UNSIGNED_INT && - depth_texture.type != GL.UNSIGNED_INT_24_8_WEBGL && - depth_texture.type != GL.FLOAT) - throw("FBO Depth texture must be of type: gl.UNSIGNED_SHORT, gl.UNSIGNED_INT, gl.UNSIGNED_INT_24_8_WEBGL"); - } - - //test if is already binded - var same = this.depth_texture == depth_texture; - if( same && color_textures ) - { - if( color_textures.constructor !== Array ) - throw("FBO: color_textures parameter must be an array containing all the textures to be binded in the color"); - if( color_textures.length == this.color_textures.length ) - { - for(var i = 0; i < color_textures.length; ++i) - if( color_textures[i] != this.color_textures[i] ) - { - same = false; - break; - } - } - else - same = false; - } - - if(this._stencil_enabled !== this.stencil) - same = false; - - if(same) - return; - - //copy textures in place - this.color_textures.length = color_textures ? color_textures.length : 0; - if(color_textures) - for(var i = 0; i < color_textures.length; ++i) - this.color_textures[i] = color_textures[i]; - this.depth_texture = depth_texture; - - //update GPU FBO - this.update( skip_disable ); -} - -/** -* Updates the FBO with the new set of textures and buffers -* @method update -* @param {Boolean} skip_disable it doenst try to go back to the previous FBO enabled in case there was one -*/ -FBO.prototype.update = function( skip_disable ) -{ - //save state to restore afterwards - this._old_fbo_handler = gl.getParameter( gl.FRAMEBUFFER_BINDING ); - - if(!this.handler) - this.handler = gl.createFramebuffer(); - - var w = -1, - h = -1, - type = null; - - var color_textures = this.color_textures; - var depth_texture = this.depth_texture; - - //compute the W and H (and check they have the same size) - if(color_textures && color_textures.length) - for(var i = 0; i < color_textures.length; i++) - { - var t = color_textures[i]; - if(t.constructor !== GL.Texture) - throw("FBO can only bind instances of GL.Texture"); - if(w == -1) - w = t.width; - else if(w != t.width) - throw("Cannot bind textures with different dimensions"); - if(h == -1) - h = t.height; - else if(h != t.height) - throw("Cannot bind textures with different dimensions"); - if(type == null) //first one defines the type - type = t.type; - else if (type != t.type) - throw("Cannot bind textures to a FBO with different pixel formats"); - if (t.texture_type != gl.TEXTURE_2D) - throw("Cannot bind a Cubemap to a FBO"); - } - else - { - w = depth_texture.width; - h = depth_texture.height; - } - - this.width = w; - this.height = h; - - gl.bindFramebuffer( gl.FRAMEBUFFER, this.handler ); - - //draw_buffers allow to have more than one color texture binded in a FBO - var ext = gl.extensions["WEBGL_draw_buffers"]; - if( gl.webgl_version == 1 && !ext && color_textures && color_textures.length > 1) - throw("Rendering to several textures not supported by your browser"); - - var target = gl.webgl_version == 1 ? gl.FRAMEBUFFER : gl.DRAW_FRAMEBUFFER; - - //detach anything bindede - gl.framebufferRenderbuffer( target, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, null ); - gl.framebufferRenderbuffer( target, gl.DEPTH_STENCIL_ATTACHMENT, gl.RENDERBUFFER, null ); - //detach color too? - - //bind a buffer for the depth - if( depth_texture && depth_texture.constructor === GL.Texture ) - { - if(gl.webgl_version == 1 && !gl.extensions["WEBGL_depth_texture"] ) - throw("Rendering to depth texture not supported by your browser"); - - if(this.stencil && depth_texture.format !== gl.DEPTH_STENCIL ) - console.warn("Stencil cannot be enabled if there is a depth texture with a DEPTH_STENCIL format"); - - if( depth_texture.format == gl.DEPTH_STENCIL ) - gl.framebufferTexture2D( target, gl.DEPTH_STENCIL_ATTACHMENT, gl.TEXTURE_2D, depth_texture.handler, 0); - else - gl.framebufferTexture2D( target, gl.DEPTH_ATTACHMENT, gl.TEXTURE_2D, depth_texture.handler, 0); - } - else //create a renderbuffer to store depth - { - var depth_renderbuffer = null; - - //allows to reuse a renderbuffer between FBOs - if( depth_texture && depth_texture.constructor === WebGLRenderbuffer && depth_texture.width == w && depth_texture.height == h ) - depth_renderbuffer = this._depth_renderbuffer = depth_texture; - else - { - //create one - depth_renderbuffer = this._depth_renderbuffer = this._depth_renderbuffer || gl.createRenderbuffer(); - depth_renderbuffer.width = w; - depth_renderbuffer.height = h; - } - - gl.bindRenderbuffer( gl.RENDERBUFFER, depth_renderbuffer ); - if(this.stencil) - { - gl.renderbufferStorage( gl.RENDERBUFFER, gl.DEPTH_STENCIL, w, h ); - gl.framebufferRenderbuffer( target, gl.DEPTH_STENCIL_ATTACHMENT, gl.RENDERBUFFER, depth_renderbuffer ); - } - else - { - gl.renderbufferStorage( gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, w, h ); - gl.framebufferRenderbuffer( target, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, depth_renderbuffer ); - } - } - - //bind buffers for the colors - if(color_textures && color_textures.length) - { - this.order = []; //draw_buffers request the use of an array with the order of the attachments - for(var i = 0; i < color_textures.length; i++) - { - var t = color_textures[i]; - - //not a bug, gl.COLOR_ATTACHMENT0 + i because COLOR_ATTACHMENT is sequential numbers - gl.framebufferTexture2D( target, gl.COLOR_ATTACHMENT0 + i, gl.TEXTURE_2D, t.handler, 0 ); - this.order.push( gl.COLOR_ATTACHMENT0 + i ); - } - } - else //create renderbuffer to store color - { - var color_renderbuffer = this._color_renderbuffer = this._color_renderbuffer || gl.createRenderbuffer(); - color_renderbuffer.width = w; - color_renderbuffer.height = h; - gl.bindRenderbuffer( gl.RENDERBUFFER, color_renderbuffer ); - gl.renderbufferStorage( gl.RENDERBUFFER, gl.RGBA4, w, h ); - gl.framebufferRenderbuffer( target, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, color_renderbuffer ); - } - - //detach old ones (only if is reusing a FBO with a different set of textures) - var num = color_textures ? color_textures.length : 0; - for(var i = num; i < this._num_binded_textures; ++i) - gl.framebufferTexture2D( target, gl.COLOR_ATTACHMENT0 + i, gl.TEXTURE_2D, null, 0); - this._num_binded_textures = num; - - this._stencil_enabled = this.stencil; - - /* does not work, must be used with the depth_stencil - if(this.stencil && !depth_texture) - { - var stencil_buffer = this._stencil_buffer = this._stencil_buffer || gl.createRenderbuffer(); - stencil_buffer.width = w; - stencil_buffer.height = h; - gl.bindRenderbuffer( gl.RENDERBUFFER, stencil_buffer ); - gl.renderbufferStorage( gl.RENDERBUFFER, gl.STENCIL_INDEX8, w, h); - gl.framebufferRenderbuffer( gl.FRAMEBUFFER, gl.STENCIL_ATTACHMENT, gl.RENDERBUFFER, stencil_buffer ); - this._stencil_enabled = true; - } - else - { - this._stencil_buffer = null; - this._stencil_enabled = false; - } - */ - - //when using more than one texture you need to use the multidraw extension - if(color_textures && color_textures.length > 1) - { - if( ext ) - ext.drawBuffersWEBGL( this.order ); - else - gl.drawBuffers( this.order ); - } - - //check completion - var complete = gl.checkFramebufferStatus( target ); - if(complete !== gl.FRAMEBUFFER_COMPLETE) //36054: GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT - throw("FBO not complete: " + complete); - - //restore state - gl.bindTexture(gl.TEXTURE_2D, null); - gl.bindRenderbuffer(gl.RENDERBUFFER, null); - if(!skip_disable) - gl.bindFramebuffer( target, this._old_fbo_handler ); -} - -/** -* Enables this FBO (from now on all the render will be stored in the textures attached to this FBO) -* It stores the previous viewport to restore it afterwards, and changes it to full FBO size -* @method bind -* @param {boolean} keep_old keeps the previous FBO is one was attached to restore it afterwards -*/ -FBO.prototype.bind = function( keep_old ) -{ - if(!this.color_textures.length && !this.depth_texture) - throw("FBO: no textures attached to FBO"); - this._old_viewport.set( gl.viewport_data ); - - if(keep_old) - this._old_fbo_handler = gl.getParameter( gl.FRAMEBUFFER_BINDING ); - else - this._old_fbo_handler = null; - - if(this._old_fbo_handler != this.handler ) - gl.bindFramebuffer( gl.FRAMEBUFFER, this.handler ); - - //mark them as in use in the FBO - for(var i = 0; i < this.color_textures.length; ++i) - this.color_textures[i]._in_current_fbo = true; - if(this.depth_texture) - this.depth_texture._in_current_fbo = true; - - gl.viewport( 0,0, this.width, this.height ); - FBO.current = this; -} - -/** -* Disables this FBO, if it was binded with keep_old then the old FBO is enabled, otherwise it will render to the screen -* Restores viewport to previous -* @method unbind -*/ -FBO.prototype.unbind = function() -{ - gl.bindFramebuffer( gl.FRAMEBUFFER, this._old_fbo_handler ); - this._old_fbo_handler = null; - gl.setViewport( this._old_viewport ); - - //mark the textures as no longer in use - for(var i = 0; i < this.color_textures.length; ++i) - this.color_textures[i]._in_current_fbo = false; - if(this.depth_texture) - this.depth_texture._in_current_fbo = false; - FBO.current = null; -} - -//binds another FBO without switch back to previous (faster) -FBO.prototype.switchTo = function( next_fbo ) -{ - next_fbo._old_fbo_handler = this._old_fbo_handler; - next_fbo._old_viewport.set( this._old_viewport ); - gl.bindFramebuffer( gl.FRAMEBUFFER, next_fbo.handler ); - this._old_fbo_handler = null; - gl.viewport( 0,0, this.width, this.height ); - - //mark the textures as no longer in use - for(var i = 0; i < this.color_textures.length; ++i) - this.color_textures[i]._in_current_fbo = false; - if(this.depth_texture) - this.depth_texture._in_current_fbo = false; - - //mark them as in use in the FBO - for(var i = 0; i < next_fbo.color_textures.length; ++i) - next_fbo.color_textures[i]._in_current_fbo = true; - if(next_fbo.depth_texture) - next_fbo.depth_texture._in_current_fbo = true; - - FBO.current = next_fbo; -} - -FBO.prototype.delete = function() -{ - gl.deleteFramebuffer( this.handler ); - this.handler = null; -} - -//WebGL 1.0 support for certaing FBOs is not very clear and can crash sometimes -FBO.supported = {}; -//type: gl.FLOAT, format: gl.RGBA -FBO.testSupport = function( type, format ) { - var name = type +":" + format; - if( FBO.supported[ name ] != null ) - return FBO.supported[ name ]; - - var tex = new GL.Texture(1,1,{ format: format, type: type }); - try - { - var fbo = new GL.FBO([tex]); - } - catch (err) - { - console.warn("This browser WEBGL implementation doesn't support this FBO format: " + GL.reverse[type] + " " + GL.reverse[format] ); - return FBO.supported[ name ] = false; - } - FBO.supported[ name ] = true; - return true; -} - -FBO.prototype.toSingle = function() -{ - if( this.color_textures.length < 2 ) - return; //nothing to do - var ext = gl.extensions.WEBGL_draw_buffers; - if( ext ) - ext.drawBuffersWEBGL( [ this.order[0] ] ); - else - gl.drawBuffers( [ this.order[0] ] ); -} - -FBO.prototype.toMulti = function() -{ - if( this.color_textures.length < 2 ) - return; //nothing to do - var ext = gl.extensions.WEBGL_draw_buffers; - if( ext ) - ext.drawBuffersWEBGL( this.order ); - else - gl.drawBuffers( this.order ); -} - -//clears only the secondary buffers (not the main one) -FBO.prototype.clearSecondary = function( color ) -{ - if(!this.order || this.order.length < 2) - return; - - var ext = gl.extensions.WEBGL_draw_buffers; - var new_order = [gl.NONE]; - for(var i = 1; i < this.order.length; ++i) - new_order.push(this.order[i]); - if(ext) - ext.drawBuffersWEBGL( new_order ); - else - gl.drawBuffers( new_order ); - gl.clearColor( color[0],color[1],color[2],color[3] ); - gl.clear( gl.COLOR_BUFFER_BIT ); - - if(ext) - ext.drawBuffersWEBGL( this.order ); - else - gl.drawBuffers( this.order ); -} - - - -/** -* @namespace GL -*/ - -/** -* Shader class to upload programs to the GPU -* @class Shader -* @constructor -* @param {String} vertexSource (it also allows to pass a compiled vertex shader) -* @param {String} fragmentSource (it also allows to pass a compiled fragment shader) -* @param {Object} macros (optional) precompiler macros to be applied when compiling -*/ -global.Shader = GL.Shader = function Shader( vertexSource, fragmentSource, macros ) -{ - if(GL.debug) - console.log("GL.Shader created"); - - if( !vertexSource || !fragmentSource ) - throw("GL.Shader source code parameter missing"); - - //used to avoid problems with resources moving between different webgl context - this._context_id = global.gl.context_id; - var gl = this.gl = global.gl; - - //expand macros - var extra_code = Shader.expandMacros( macros ); - - var final_vertexSource = vertexSource.constructor === String ? Shader.injectCode( extra_code, vertexSource, gl ) : vertexSource; - var final_fragmentSource = fragmentSource.constructor === String ? Shader.injectCode( extra_code, fragmentSource, gl ) : fragmentSource; - - this.program = gl.createProgram(); - - var vs = vertexSource.constructor === String ? GL.Shader.compileSource( gl.VERTEX_SHADER, final_vertexSource ) : vertexSource; - var fs = fragmentSource.constructor === String ? GL.Shader.compileSource( gl.FRAGMENT_SHADER, final_fragmentSource ) : fragmentSource; - - gl.attachShader( this.program, vs, gl ); - gl.attachShader( this.program, fs, gl ); - gl.linkProgram(this.program); - if (!gl.getProgramParameter(this.program, gl.LINK_STATUS)) { - throw 'link error: ' + gl.getProgramInfoLog(this.program); - } - - this.vs_shader = vs; - this.fs_shader = fs; - - //Extract info from the shader - this.attributes = {}; - this.uniformInfo = {}; - this.samplers = {}; - - //extract info about the shader to speed up future processes - this.extractShaderInfo(); -} - -Shader.expandMacros = function(macros) -{ - var extra_code = ""; //add here preprocessor directives that should be above everything - if(macros) - for(var i in macros) - extra_code += "#define " + i + " " + (macros[i] ? macros[i] : "") + "\n"; - return extra_code; -} - -//this is done to avoid problems with the #version which must be in the first line -Shader.injectCode = function( inject_code, code, gl ) -{ - var index = code.indexOf("\n"); - var version = ( gl ? "#define WEBGL" + gl.webgl_version + "\n" : ""); - var first_line = code.substr(0,index).trim(); - if( first_line.indexOf("#version") == -1 ) - return version + inject_code + code; - return first_line + "\n" + version + inject_code + code.substr(index); -} - - -/** -* Compiles one single shader source (could be gl.VERTEX_SHADER or gl.FRAGMENT_SHADER) and returns the webgl shader handler -* Used internaly to compile the vertex and fragment shader. -* It throws an exception if there is any error in the code -* @method Shader.compileSource -* @param {Number} type could be gl.VERTEX_SHADER or gl.FRAGMENT_SHADER -* @param {String} source the source file to compile -* @return {WebGLShader} the handler from webgl -*/ -Shader.compileSource = function( type, source, gl, shader ) -{ - gl = gl || global.gl; - shader = shader || gl.createShader(type); - gl.shaderSource(shader, source); - gl.compileShader(shader); - if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) { - throw (type == gl.VERTEX_SHADER ? "Vertex" : "Fragment") + ' shader compile error: ' + gl.getShaderInfoLog(shader); - } - return shader; -} - -Shader.parseError = function( error_str, vs_code, fs_code ) -{ - if(!error_str) - return null; - - var t = error_str.split(" "); - var nums = t[5].split(":"); - - return { - type: t[0], - line_number: parseInt( nums[1] ), - line_pos: parseInt( nums[0] ), - line_code: ( t[0] == "Fragment" ? fs_code : vs_code ).split("\n")[ parseInt( nums[1] ) ], - err: error_str - }; -} - -/** -* It updates the code inside one shader -* @method updateShader -* @param {String} vertexSource -* @param {String} fragmentSource -* @param {Object} macros [optional] -*/ -Shader.prototype.updateShader = function( vertexSource, fragmentSource, macros ) -{ - var gl = this.gl || global.gl; - - //expand macros - var extra_code = Shader.expandMacros( macros ); - - if(!this.program) - this.program = gl.createProgram(); - else - { - gl.detachShader( this.program, this.vs_shader ); - gl.detachShader( this.program, this.fs_shader ); - } - - var extra_code = Shader.expandMacros( macros ); - - var final_vertexSource = vertexSource.constructor === String ? Shader.injectCode( extra_code, vertexSource, gl ) : vertexSource; - var final_fragmentSource = fragmentSource.constructor === String ? Shader.injectCode( extra_code, fragmentSource, gl ) : fragmentSource; - - var vs = vertexSource.constructor === String ? GL.Shader.compileSource( gl.VERTEX_SHADER, final_vertexSource ) : vertexSource; - var fs = fragmentSource.constructor === String ? GL.Shader.compileSource( gl.FRAGMENT_SHADER, final_fragmentSource ) : fragmentSource; - - gl.attachShader( this.program, vs, gl ); - gl.attachShader( this.program, fs, gl ); - gl.linkProgram( this.program ); - if (!gl.getProgramParameter(this.program, gl.LINK_STATUS)) { - throw 'link error: ' + gl.getProgramInfoLog( this.program ); - } - - //store shaders separated - this.vs_shader = vs; - this.fs_shader = fs; - - //Extract info from the shader - this.attributes = {}; - this.uniformInfo = {}; - this.samplers = {}; - - //extract info about the shader to speed up future processes - this.extractShaderInfo(); -} - -/** -* It extract all the info about the compiled shader program, all the info about uniforms and attributes. -* This info is stored so it works faster during rendering. -* @method extractShaderInfo -*/ - - -Shader.prototype.extractShaderInfo = function() -{ - var gl = this.gl; - - var l = gl.getProgramParameter( this.program, gl.ACTIVE_UNIFORMS ); - - //extract uniforms info - for(var i = 0; i < l; ++i) - { - var data = gl.getActiveUniform( this.program, i); - if(!data) break; - - var uniformName = data.name; - - //arrays have uniformName[0], strip the [] (also data.size tells you if it is an array) - var pos = uniformName.indexOf("["); - if(pos != -1) - { - var pos2 = uniformName.indexOf("]."); //leave array of structs though - if(pos2 == -1) - uniformName = uniformName.substr(0,pos); - } - - //store texture samplers - if(data.type == gl.SAMPLER_2D || data.type == gl.SAMPLER_CUBE || data.type == GL.SAMPLER_3D) - this.samplers[ uniformName ] = data.type; - - //get which function to call when uploading this uniform - var func = Shader.getUniformFunc(data); - var is_matrix = false; - if(data.type == gl.FLOAT_MAT2 || data.type == gl.FLOAT_MAT3 || data.type == gl.FLOAT_MAT4) - is_matrix = true; - var type_length = GL.TYPE_LENGTH[ data.type ] || 1; - - //save the info so the user doesnt have to specify types when uploading data to the shader - this.uniformInfo[ uniformName ] = { - type: data.type, - func: func, - size: data.size, - type_length: type_length, - is_matrix: is_matrix, - loc: gl.getUniformLocation(this.program, uniformName), - data: new Float32Array( type_length * data.size ) //prealloc space to assign uniforms that are not typed - }; - } - - //extract attributes info - for(var i = 0, l = gl.getProgramParameter(this.program, gl.ACTIVE_ATTRIBUTES); i < l; ++i) - { - var data = gl.getActiveAttrib( this.program, i); - if(!data) - break; - var func = Shader.getUniformFunc(data); - var type_length = GL.TYPE_LENGTH[ data.type ] || 1; - this.uniformInfo[ data.name ] = { - type: data.type, - func: func, - type_length: type_length, - size: data.size, - loc: null - }; //gl.getAttribLocation( this.program, data.name ) - this.attributes[ data.name ] = gl.getAttribLocation(this.program, data.name ); - } -} - -/** -* Returns if this shader has a uniform with the given name -* @method hasUniform -* @param {String} name name of the uniform -* @return {Boolean} -*/ -Shader.prototype.hasUniform = function(name) -{ - return this.uniformInfo[name]; -} - -/** -* Returns if this shader has an attribute with the given name -* @method hasAttribute -* @param {String} name name of the attribute -* @return {Boolean} -*/ -Shader.prototype.hasAttribute = function(name) -{ - return this.attributes[name]; -} - - -/** -* Tells you which function to call when uploading a uniform according to the data type in the shader -* Used internally from extractShaderInfo to optimize calls -* @method Shader.getUniformFunc -* @param {Object} data info about the uniform -* @return {Function} -*/ -Shader.getUniformFunc = function( data ) -{ - var func = null; - switch (data.type) - { - case GL.FLOAT: - if(data.size == 1) - func = gl.uniform1f; - else - func = gl.uniform1fv; - break; - case GL.FLOAT_MAT2: func = gl.uniformMatrix2fv; break; - case GL.FLOAT_MAT3: func = gl.uniformMatrix3fv; break; - case GL.FLOAT_MAT4: func = gl.uniformMatrix4fv; break; - case GL.FLOAT_VEC2: func = gl.uniform2fv; break; - case GL.FLOAT_VEC3: func = gl.uniform3fv; break; - case GL.FLOAT_VEC4: func = gl.uniform4fv; break; - - case GL.UNSIGNED_INT: - case GL.INT: - if(data.size == 1) - func = gl.uniform1i; - else - func = gl.uniform1iv; - break; - case GL.INT_VEC2: func = gl.uniform2iv; break; - case GL.INT_VEC3: func = gl.uniform3iv; break; - case GL.INT_VEC4: func = gl.uniform4iv; break; - - case GL.SAMPLER_2D: - case GL.SAMPLER_3D: - case GL.SAMPLER_CUBE: - func = gl.uniform1i; break; - default: func = gl.uniform1f; break; - } - return func; -} - -/** -* Create a shader from two urls. While the system is fetching the two urls, the shader contains a dummy shader that renders black. -* @method Shader.fromURL -* @param {String} vs_path the url to the vertex shader -* @param {String} fs_path the url to the fragment shader -* @param {Function} on_complete [Optional] a callback to call once the shader is ready. -* @return {Shader} -*/ -Shader.fromURL = function( vs_path, fs_path, on_complete ) -{ - //create simple shader first - var vs_code = "\n\ - precision highp float;\n\ - attribute vec3 a_vertex;\n\ - attribute mat4 u_mvp;\n\ - void main() { \n\ - gl_Position = u_mvp * vec4(a_vertex,1.0); \n\ - }\n\ - "; - var fs_code = "\n\ - precision highp float;\n\ - void main() {\n\ - gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0);\n\ - }\n\ - "; - - var shader = new GL.Shader(vs_code, fs_code); - shader.ready = false; - - var true_vs = null; - var true_fs = null; - - HttpRequest( vs_path, null, function(vs_code) { - true_vs = vs_code; - if(true_fs) - compileShader(); - }); - - HttpRequest( fs_path, null, function(fs_code) { - true_fs = fs_code; - if(true_vs) - compileShader(); - }); - - function compileShader() - { - var true_shader = new GL.Shader(true_vs, true_fs); - for(var i in true_shader) - shader[i] = true_shader[i]; - shader.ready = true; - } - - return shader; -} - -/** -* enables the shader (calls useProgram) -* @method bind -*/ -Shader.prototype.bind = function() -{ - var gl = this.gl; - gl.useProgram( this.program ); - gl._current_shader = this; -} - -/** -* Returns the location of a uniform or attribute -* @method getLocation -* @param {String} name -* @return {WebGLUniformLocation} location -*/ -Shader.prototype.getLocation = function( name ) -{ - var info = this.uniformInfo[name]; - if(info) - return this.uniformInfo[name].loc; - return null; -} - -/** -* Uploads a set of uniforms to the Shader. You dont need to specify types, they are infered from the shader info. -* @method uniforms -* @param {Object} uniforms -*/ -Shader._temp_uniform = new Float32Array(16); - -Shader.prototype.uniforms = function(uniforms) { - var gl = this.gl; - gl.useProgram(this.program); - gl._current_shader = this; - - for (var name in uniforms) - { - var info = this.uniformInfo[ name ]; - if (!info) - continue; - this._setUniform( name, uniforms[name] ); - //this.setUniform( name, uniforms[name] ); - //this._assing_uniform(uniforms, name, gl ); - } - - return this; -}//uniforms - -Shader.prototype.uniformsArray = function(array) { - var gl = this.gl; - gl.useProgram( this.program ); - gl._current_shader = this; - - for(var i = 0, l = array.length; i < l; ++i) - { - var uniforms = array[i]; - for (var name in uniforms) - this._setUniform( name, uniforms[name] ); - //this._assing_uniform(uniforms, name, gl ); - } - - return this; -} - -/** -* Uploads a uniform to the Shader. You dont need to specify types, they are infered from the shader info. Shader must be binded! -* @method setUniform -* @param {string} name -* @param {*} value -*/ -Shader.prototype.setUniform = (function(){ - - return (function(name, value) - { - if( this.gl._current_shader != this ) - this.bind(); - - var info = this.uniformInfo[name]; - if (!info) - return; - - if(info.loc === null) - return; - - if(value == null) //strict? - return; - - if(value.constructor === Array) - { - info.data.set( value ); - value = info.data; - } - - if(info.is_matrix) - info.func.call( this.gl, info.loc, false, value ); - else - info.func.call( this.gl, info.loc, value ); - }); -})(); - -//skips enabling shader -Shader.prototype._setUniform = (function(){ - - return (function(name, value) - { - var info = this.uniformInfo[ name ]; - if (!info) - return; - - if(info.loc === null) - return; - - //if(info.loc.constructor !== Function) - // return; - - if(value == null) - return; - - if(value.constructor === Array) - { - info.data.set( value ); - value = info.data; - } - - if(info.is_matrix) - info.func.call( this.gl, info.loc, false, value ); - else - info.func.call( this.gl, info.loc, value ); - }); -})(); - -/** -* Renders a mesh using this shader, remember to use the function uniforms before to enable the shader -* @method draw -* @param {Mesh} mesh -* @param {number} mode could be gl.LINES, gl.POINTS, gl.TRIANGLES, gl.TRIANGLE_STRIP, gl.TRIANGLE_FAN -* @param {String} index_buffer_name the name of the index buffer, if not provided triangles will be assumed -*/ -Shader.prototype.draw = function( mesh, mode, index_buffer_name ) { - index_buffer_name = index_buffer_name === undefined ? (mode == gl.LINES ? 'lines' : 'triangles') : index_buffer_name; - this.drawBuffers( mesh.vertexBuffers, - index_buffer_name ? mesh.indexBuffers[ index_buffer_name ] : null, - arguments.length < 2 ? gl.TRIANGLES : mode); -} - -/** -* Renders a range of a mesh using this shader -* @method drawRange -* @param {Mesh} mesh -* @param {number} mode could be gl.LINES, gl.POINTS, gl.TRIANGLES, gl.TRIANGLE_STRIP, gl.TRIANGLE_FAN -* @param {number} start first primitive to render -* @param {number} length number of primitives to render -* @param {String} index_buffer_name the name of the index buffer, if not provided triangles will be assumed -*/ -Shader.prototype.drawRange = function(mesh, mode, start, length, index_buffer_name ) -{ - index_buffer_name = index_buffer_name === undefined ? (mode == gl.LINES ? 'lines' : 'triangles') : index_buffer_name; - - this.drawBuffers( mesh.vertexBuffers, - index_buffer_name ? mesh.indexBuffers[ index_buffer_name ] : null, - mode, start, length); -} - -/** -* render several buffers with a given index buffer -* @method drawBuffers -* @param {Object} vertexBuffers an object containing all the buffers -* @param {IndexBuffer} indexBuffer -* @param {number} mode could be gl.LINES, gl.POINTS, gl.TRIANGLES, gl.TRIANGLE_STRIP, gl.TRIANGLE_FAN -* @param {number} range_start first primitive to render -* @param {number} range_length number of primitives to render -*/ - -//this two variables are a hack to avoid memory allocation on drawCalls -var temp_attribs_array = new Uint8Array(16); -var temp_attribs_array_zero = new Uint8Array(16); //should be filled with zeros always - -Shader.prototype.drawBuffers = function( vertexBuffers, indexBuffer, mode, range_start, range_length ) -{ - if(range_length == 0) - return; - - var gl = this.gl; - - gl.useProgram(this.program); //this could be removed assuming every shader is called with some uniforms - - // enable attributes as necessary. - var length = 0; - var attribs_in_use = temp_attribs_array; //hack to avoid garbage - attribs_in_use.set( temp_attribs_array_zero ); //reset - - for (var name in vertexBuffers) - { - var buffer = vertexBuffers[name]; - var attribute = buffer.attribute || name; - //precompute attribute locations in shader - var location = this.attributes[attribute];// || gl.getAttribLocation(this.program, attribute); - - if (location == null || !buffer.buffer) //-1 changed for null - continue; //ignore this buffer - - attribs_in_use[location] = 1; //mark it as used - - //this.attributes[attribute] = location; - gl.bindBuffer(gl.ARRAY_BUFFER, buffer.buffer); - gl.enableVertexAttribArray(location); - - gl.vertexAttribPointer(location, buffer.buffer.spacing, buffer.buffer.gl_type, false, 0, 0); - length = buffer.buffer.length / buffer.buffer.spacing; - } - - //range rendering - var offset = 0; //in bytes - if(range_start > 0) //render a polygon range - offset = range_start; //in bytes (Uint16 == 2 bytes) - - if (indexBuffer) - length = indexBuffer.buffer.length - offset; - - if(range_length > 0 && range_length < length) //to avoid problems - length = range_length; - - var BYTES_PER_ELEMENT = (indexBuffer && indexBuffer.data) ? indexBuffer.data.constructor.BYTES_PER_ELEMENT : 1; - offset *= BYTES_PER_ELEMENT; - - // Force to disable buffers in this shader that are not in this mesh - for (var attribute in this.attributes) - { - var location = this.attributes[attribute]; - if (!(attribs_in_use[location])) { - gl.disableVertexAttribArray(this.attributes[attribute]); - } - } - - // Draw the geometry. - if (length && (!indexBuffer || indexBuffer.buffer)) { - if (indexBuffer) { - gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer.buffer); - gl.drawElements( mode, length, indexBuffer.buffer.gl_type, offset); //gl.UNSIGNED_SHORT - gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null); - } else { - gl.drawArrays(mode, offset, length); - } - } - - return this; -} - -Shader._instancing_arrays = []; - -Shader.prototype.drawInstanced = function( mesh, primitive, indices, instanced_uniforms, range_start, range_length, num_instances ) -{ - if(range_length === 0) - return; - - //bind buffers - var gl = this.gl; - - if( gl.webgl_version == 1 && !gl.extensions.ANGLE_instanced_arrays ) - throw("instancing not supported"); - - gl.useProgram(this.program); //this could be removed assuming every shader is called with some uniforms - - // enable attributes as necessary. - var length = 0; - var attribs_in_use = temp_attribs_array; //hack to avoid garbage - attribs_in_use.set( temp_attribs_array_zero ); //reset - - var vertexBuffers = mesh.vertexBuffers; - - for (var name in vertexBuffers) - { - var buffer = vertexBuffers[name]; - var attribute = buffer.attribute || name; - //precompute attribute locations in shader - var location = this.attributes[attribute];// || gl.getAttribLocation(this.program, attribute); - - if (location == null || !buffer.buffer) //-1 changed for null - continue; //ignore this buffer - - attribs_in_use[location] = 1; //mark it as used - - //this.attributes[attribute] = location; - gl.bindBuffer(gl.ARRAY_BUFFER, buffer.buffer); - gl.enableVertexAttribArray(location); - - gl.vertexAttribPointer(location, buffer.buffer.spacing, buffer.buffer.gl_type, false, 0, 0); - length = buffer.buffer.length / buffer.buffer.spacing; - } - - var indexBuffer = null; - if(indices) - { - if(indices.constructor === GL.Buffer) - indexBuffer = indices; - else - indexBuffer = mesh.getIndexBuffer( indices ); - } - - //range rendering - var offset = 0; //in bytes - if(range_start > 0) //render a polygon range - offset = range_start; - - if (indexBuffer) - length = indexBuffer.buffer.length - offset; - - if(range_length > 0 && range_length < length) //to avoid problems - length = range_length; - - var BYTES_PER_ELEMENT = (indexBuffer && indexBuffer.data) ? indexBuffer.data.constructor.BYTES_PER_ELEMENT : 1; - offset *= BYTES_PER_ELEMENT; - - // Force to disable buffers in this shader that are not in this mesh - for (var attribute in this.attributes) - { - var location = this.attributes[attribute]; - if (!(attribs_in_use[location])) { - gl.disableVertexAttribArray(this.attributes[attribute]); - } - } - - var ext = gl.extensions.ANGLE_instanced_arrays; - var batch_length = 0; - - //pack the instanced uniforms - var index = 0; - for(var uniform in instanced_uniforms) - { - var values = instanced_uniforms[ uniform ]; - batch_length = values.length; - var uniformLocation = this.attributes[ uniform ]; - if( uniformLocation == null ) - return; //not found - var element_size = 0; - var total_size = 0; - if( values.constructor === Array ) - { - element_size = values[0].constructor === Number ? 1 : values[0].length; - total_size = element_size * values.length; - } - else //typed array - { - element_size = this.uniformInfo[ uniform ].type_length; - total_size = values.length; - batch_length = total_size / element_size; - } - - var data_array = Shader._instancing_arrays[ index ]; - if( !data_array || data_array.data.length < total_size ) - data_array = Shader._instancing_arrays[ index ] = { data: new Float32Array( total_size ), buffer: gl.createBuffer() }; - data_array.uniform = uniform; - data_array.element_size = element_size; - if( values.constructor === Array ) - for(var j = 0; j < values.length; ++j) - data_array.data.set( values[j], j*element_size ); //flatten array - else - data_array.data.set( values ); //copy - gl.bindBuffer( gl.ARRAY_BUFFER, data_array.buffer ); - gl.bufferData( gl.ARRAY_BUFFER, data_array.data, gl.STREAM_DRAW ); - - if(element_size == 16) //mat4 - { - for(var k = 0; k < 4; ++k) - { - gl.enableVertexAttribArray( uniformLocation+k ); - gl.vertexAttribPointer( uniformLocation+k, 4, gl.FLOAT , false, 16*4, k*4*4 ); //4 bytes per float - if( ext ) //webgl 1 - ext.vertexAttribDivisorANGLE( uniformLocation+k, 1 ); // This makes it instanced! - else - gl.vertexAttribDivisor( uniformLocation+k, 1 ); // This makes it instanced! - } - } - else //others - { - gl.enableVertexAttribArray( uniformLocation ); - gl.vertexAttribPointer( uniformLocation, element_size, gl.FLOAT, false, element_size*4, 0 ); //4 bytes per float, 0 offset - if( ext ) //webgl 1 - ext.vertexAttribDivisorANGLE( uniformLocation, 1 ); // This makes it instanced! - else - gl.vertexAttribDivisor( uniformLocation, 1 ); // This makes it instanced! - } - index+=1; - } - - if( num_instances ) - batch_length = num_instances; - - if( ext ) //webgl 1.0 - { - if(indexBuffer) - { - gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer.buffer); - ext.drawElementsInstancedANGLE( primitive, length, indexBuffer.buffer.gl_type, offset, batch_length ); - gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null ); - } - else - ext.drawArraysInstancedANGLE( primitive, offset, length, batch_length); - } - else - { - if(indexBuffer) - { - gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer.buffer); - gl.drawElementsInstanced( primitive, length, indexBuffer.buffer.gl_type, offset, batch_length ); - gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null ); - } - else - gl.drawArraysInstanced( primitive, offset, length, batch_length); - } - - //disable instancing buffers - for(var i = 0; i < index; ++i) - { - var info = Shader._instancing_arrays[ i ]; - var uniformLocation = this.attributes[ info.uniform ]; - var element_size = info.element_size; - if( element_size == 16) //mat4 - { - for(var k = 0; k < 4; ++k) - { - gl.disableVertexAttribArray( uniformLocation+k ); - if( ext ) //webgl 1 - ext.vertexAttribDivisorANGLE( uniformLocation+k, 0 ); - else - gl.vertexAttribDivisor( uniformLocation+k, 0 ); - } - } - else //others - { - gl.enableVertexAttribArray( uniformLocation ); - if( ext ) //webgl 1 - ext.vertexAttribDivisorANGLE( uniformLocation, 0 ); - else - gl.vertexAttribDivisor( uniformLocation, 0 ); - } - } - - return this; -} - - - -/** -* Given a source code with the directive #import it expands it inserting the code using Shader.files to fetch for import files. -* Warning: Imports are evaluated only the first inclusion, the rest are ignored to avoid double inclusion of functions -* Also, imports cannot have other imports inside. -* @method Shader.expandImports -* @param {String} code the source code -* @param {Object} files [Optional] object with files to import from (otherwise Shader.files is used) -* @return {String} the code with the lines #import removed and replaced by the code -*/ -Shader.expandImports = function(code, files) -{ - files = files || Shader.files; - - var already_imported = {}; //avoid to import two times the same code - if( !files ) - throw("Shader.files not initialized, assign files there"); - - var replace_import = function(v) - { - var token = v.split("\""); - var id = token[1]; - if( already_imported[id] ) - return "//already imported: " + id + "\n"; - var file = files[id]; - already_imported[ id ] = true; - if(file) - return file + "\n"; - return "//import code not found: " + id + "\n"; - } - - //return code.replace(/#import\s+\"(\w+)\"\s*\n/g, replace_import ); - return code.replace(/#import\s+\"([a-zA-Z0-9_\.]+)\"\s*\n/g, replace_import ); -} - -Shader.dumpErrorToConsole = function(err, vscode, fscode) -{ - console.error(err); - var msg = err.msg; - var code = null; - if(err.indexOf("Fragment") != -1) - code = fscode; - else - code = vscode; - - var lines = code.split("\n"); - for(var i in lines) - lines[i] = i + "| " + lines[i]; - - console.groupCollapsed("Shader code"); - console.log( lines.join("\n") ); - console.groupEnd(); -} - -Shader.convertTo100 = function(code,type) -{ - //in VERTEX - //change in for attribute - //change out for varying - //add #extension GL_OES_standard_derivatives - //in FRAGMENT - //change in for varying - //remove out vec4 _gl_FragColor - //rename _gl_FragColor for gl_FragColor - //in both - //change #version 300 es for #version 100 - //replace 'texture(' for 'texture2D(' -} - - -Shader.convertTo300 = function(code,type) -{ - //in VERTEX - //change attribute for in - //change varying for out - //remove #extension GL_OES_standard_derivatives - //in FRAGMENT - //change varying for in - //rename gl_FragColor for _gl_FragColor - //rename gl_FragData[0] for _gl_FragColor - //add out vec4 _gl_FragColor - //in both - //replace texture2D for texture -} - -//helps to check if a variable value is valid to an specific uniform in a shader -Shader.validateValue = function( value, uniform_info ) -{ - if(value === null || value === undefined) - return false; - - switch (uniform_info.type) - { - //used to validate shaders - case GL.INT: - case GL.FLOAT: - case GL.SAMPLER_2D: - case GL.SAMPLER_CUBE: - return isNumber(value); - case GL.INT_VEC2: - case GL.FLOAT_VEC2: - return value.length === 2; - case GL.INT_VEC3: - case GL.FLOAT_VEC3: - return value.length === 3; - case GL.INT_VEC4: - case GL.FLOAT_VEC4: - case GL.FLOAT_MAT2: - return value.length === 4; - case GL.FLOAT_MAT3: - return value.length === 8; - case GL.FLOAT_MAT4: - return value.length === 16; - } - return true; -} - -//**************** SHADERS *********************************** - -Shader.DEFAULT_VERTEX_SHADER = "\n\ - precision highp float;\n\ - attribute vec3 a_vertex;\n\ - attribute vec3 a_normal;\n\ - attribute vec2 a_coord;\n\ - varying vec3 v_position;\n\ - varying vec3 v_normal;\n\ - varying vec2 v_coord;\n\ - uniform mat4 u_model;\n\ - uniform mat4 u_mvp;\n\ - void main() {\n\ - v_position = (u_model * vec4(a_vertex,1.0)).xyz;\n\ - v_normal = (u_model * vec4(a_normal,0.0)).xyz;\n\ - v_coord = a_coord;\n\ - gl_Position = u_mvp * vec4(a_vertex,1.0);\n\ - }\n\ - "; - -Shader.SCREEN_VERTEX_SHADER = "\n\ - precision highp float;\n\ - attribute vec3 a_vertex;\n\ - attribute vec2 a_coord;\n\ - varying vec2 v_coord;\n\ - void main() { \n\ - v_coord = a_coord; \n\ - gl_Position = vec4(a_coord * 2.0 - 1.0, 0.0, 1.0); \n\ - }\n\ - "; - -Shader.SCREEN_FRAGMENT_SHADER = "\n\ - precision highp float;\n\ - uniform sampler2D u_texture;\n\ - varying vec2 v_coord;\n\ - void main() {\n\ - gl_FragColor = texture2D(u_texture, v_coord);\n\ - }\n\ - "; - -//used in createFX -Shader.SCREEN_FRAGMENT_FX = "\n\ - precision highp float;\n\ - uniform sampler2D u_texture;\n\ - varying vec2 v_coord;\n\ - #ifdef FX_UNIFORMS\n\ - FX_UNIFORMS\n\ - #endif\n\ - void main() {\n\ - vec2 uv = v_coord;\n\ - vec4 color = texture2D(u_texture, uv);\n\ - #ifdef FX_CODE\n\ - FX_CODE ;\n\ - #endif\n\ - gl_FragColor = color;\n\ - }\n\ - "; - -Shader.SCREEN_COLORED_FRAGMENT_SHADER = "\n\ - precision highp float;\n\ - uniform sampler2D u_texture;\n\ - uniform vec4 u_color;\n\ - varying vec2 v_coord;\n\ - void main() {\n\ - gl_FragColor = u_color * texture2D(u_texture, v_coord);\n\ - }\n\ - "; - -Shader.BLEND_FRAGMENT_SHADER = "\n\ - precision highp float;\n\ - uniform sampler2D u_texture;\n\ - uniform sampler2D u_texture2;\n\ - uniform float u_factor;\n\ - varying vec2 v_coord;\n\ - void main() {\n\ - gl_FragColor = mix( texture2D(u_texture, v_coord), texture2D(u_texture2, v_coord), u_factor);\n\ - }\n\ - "; - -//used to paint quads -Shader.QUAD_VERTEX_SHADER = "\n\ - precision highp float;\n\ - attribute vec3 a_vertex;\n\ - attribute vec2 a_coord;\n\ - varying vec2 v_coord;\n\ - uniform vec2 u_position;\n\ - uniform vec2 u_size;\n\ - uniform vec2 u_viewport;\n\ - uniform mat3 u_transform;\n\ - void main() { \n\ - vec3 pos = vec3(u_position + vec2(a_coord.x,1.0 - a_coord.y) * u_size, 1.0);\n\ - v_coord = a_coord; \n\ - pos = u_transform * pos;\n\ - pos.z = 0.0;\n\ - //normalize\n\ - pos.x = (2.0 * pos.x / u_viewport.x) - 1.0;\n\ - pos.y = -((2.0 * pos.y / u_viewport.y) - 1.0);\n\ - gl_Position = vec4(pos, 1.0); \n\ - }\n\ - "; - -Shader.QUAD_FRAGMENT_SHADER = "\n\ - precision highp float;\n\ - uniform sampler2D u_texture;\n\ - uniform vec4 u_color;\n\ - varying vec2 v_coord;\n\ - void main() {\n\ - gl_FragColor = u_color * texture2D(u_texture, v_coord);\n\ - }\n\ - "; - -//used to render partially a texture -Shader.QUAD2_FRAGMENT_SHADER = "\n\ - precision highp float;\n\ - uniform sampler2D u_texture;\n\ - uniform vec4 u_color;\n\ - uniform vec4 u_texture_area;\n\ - varying vec2 v_coord;\n\ - void main() {\n\ - vec2 uv = vec2( mix(u_texture_area.x, u_texture_area.z, v_coord.x), 1.0 - mix(u_texture_area.w, u_texture_area.y, v_coord.y) );\n\ - gl_FragColor = u_color * texture2D(u_texture, uv);\n\ - }\n\ - "; - -Shader.PRIMITIVE2D_VERTEX_SHADER = "\n\ - precision highp float;\n\ - attribute vec3 a_vertex;\n\ - uniform vec2 u_viewport;\n\ - uniform mat3 u_transform;\n\ - void main() { \n\ - vec3 pos = a_vertex;\n\ - pos = u_transform * pos;\n\ - pos.z = 0.0;\n\ - //normalize\n\ - pos.x = (2.0 * pos.x / u_viewport.x) - 1.0;\n\ - pos.y = -((2.0 * pos.y / u_viewport.y) - 1.0);\n\ - gl_Position = vec4(pos, 1.0); \n\ - }\n\ - "; - -Shader.FLAT_VERTEX_SHADER = "\n\ - precision highp float;\n\ - attribute vec3 a_vertex;\n\ - uniform mat4 u_mvp;\n\ - void main() { \n\ - gl_Position = u_mvp * vec4(a_vertex,1.0); \n\ - }\n\ - "; - -Shader.FLAT_FRAGMENT_SHADER = "\n\ - precision highp float;\n\ - uniform vec4 u_color;\n\ - void main() {\n\ - gl_FragColor = u_color;\n\ - }\n\ - "; -Shader.SCREEN_FLAT_FRAGMENT_SHADER = Shader.FLAT_FRAGMENT_SHADER; //legacy - - -/** -* Allows to create a simple shader meant to be used to process a texture, instead of having to define the generic Vertex & Fragment Shader code -* @method Shader.createFX -* @param {string} code string containg code, like "color = color * 2.0;" -* @param {string} [uniforms=null] string containg extra uniforms, like "uniform vec3 u_pos;" -*/ -Shader.createFX = function(code, uniforms, shader) -{ - //remove comments - code = GL.Shader.removeComments( code, true ); //remove comments and breaklines to avoid problems with the macros - var macros = { - FX_CODE: code, - FX_UNIFORMS: uniforms || "" - } - if(!shader) - return new GL.Shader( GL.Shader.SCREEN_VERTEX_SHADER, GL.Shader.SCREEN_FRAGMENT_FX, macros ); - shader.updateShader( GL.Shader.SCREEN_VERTEX_SHADER, GL.Shader.SCREEN_FRAGMENT_FX, macros ); - return shader; -} - -/** -* Given a shader code with some vars inside (like {{varname}}) and an object with the variable values, it will replace them. -* @method Shader.replaceCodeUsingContext -* @param {string} code string containg code and vars in {{varname}} format -* @param {object} context object containing all var values -*/ -Shader.replaceCodeUsingContext = function( code_template, context ) -{ - return code_template.replace(/\{\{[a-zA-Z0-9_]*\}\}/g, function(v){ - v = v.replace( /[\{\}]/g, "" ); - return context[v] || ""; - }); -} - -Shader.removeComments = function(code, one_line) -{ - if(!code) - return ""; - - var rx = /(\/\*([^*]|[\r\n]|(\*+([^*\/]|[\r\n])))*\*+\/)|(\/\/.*)/g; - var code = code.replace( rx ,""); - var lines = code.split("\n"); - var result = []; - for(var i = 0; i < lines.length; ++i) - { - var line = lines[i]; - var pos = line.indexOf("//"); - if(pos != -1) - line = lines[i].substr(0,pos); - line = line.trim(); - if(line.length) - result.push(line); - } - return result.join( one_line ? "" : "\n" ); -} - -/** -* Renders a fullscreen quad with this shader applied -* @method toViewport -* @param {object} uniforms -*/ -Shader.prototype.toViewport = function(uniforms) -{ - var mesh = GL.Mesh.getScreenQuad(); - if(uniforms) - this.uniforms(uniforms); - this.draw( mesh ); -} - -//Now some common shaders everybody needs - -/** -* Returns a shader ready to render a textured quad in fullscreen, use with Mesh.getScreenQuad() mesh -* shader params: sampler2D u_texture -* @method Shader.getScreenShader -*/ -Shader.getScreenShader = function(gl) -{ - gl = gl || global.gl; - var shader = gl.shaders[":screen"]; - if(shader) - return shader; - shader = gl.shaders[":screen"] = new GL.Shader( Shader.SCREEN_VERTEX_SHADER, Shader.SCREEN_FRAGMENT_SHADER ); - return shader.uniforms({u_texture:0}); //do it the first time so I dont have to do it every time -} - -/** -* Returns a shader ready to render a flat color quad in fullscreen, use with Mesh.getScreenQuad() mesh -* shader params: vec4 u_color -* @method Shader.getFlatScreenShader -*/ -Shader.getFlatScreenShader = function(gl) -{ - gl = gl || global.gl; - var shader = gl.shaders[":flat_screen"]; - if(shader) - return shader; - shader = gl.shaders[":flat_screen"] = new GL.Shader( Shader.SCREEN_VERTEX_SHADER, Shader.FLAT_FRAGMENT_SHADER ); - return shader.uniforms({u_color:[1,1,1,1]}); //do it the first time so I dont have to do it every time -} - -/** -* Returns a shader ready to render a colored textured quad in fullscreen, use with Mesh.getScreenQuad() mesh -* shader params vec4 u_color and sampler2D u_texture -* @method Shader.getColoredScreenShader -*/ -Shader.getColoredScreenShader = function(gl) -{ - gl = gl || global.gl; - var shader = gl.shaders[":colored_screen"]; - if(shader) - return shader; - shader = gl.shaders[":colored_screen"] = new GL.Shader( Shader.SCREEN_VERTEX_SHADER, Shader.SCREEN_COLORED_FRAGMENT_SHADER ); - return shader.uniforms({u_texture:0, u_color: vec4.fromValues(1,1,1,1) }); //do it the first time so I dont have to do it every time -} - -/** -* Returns a shader ready to render a quad with transform, use with Mesh.getScreenQuad() mesh -* shader must have: u_position, u_size, u_viewport, u_transform (mat3) -* @method Shader.getQuadShader -*/ -Shader.getQuadShader = function(gl) -{ - gl = gl || global.gl; - var shader = gl.shaders[":quad"]; - if(shader) - return shader; - return gl.shaders[":quad"] = new GL.Shader( Shader.QUAD_VERTEX_SHADER, Shader.QUAD_FRAGMENT_SHADER ); -} - -/** -* Returns a shader ready to render part of a texture into the viewport -* shader must have: u_position, u_size, u_viewport, u_transform, u_texture_area (vec4) -* @method Shader.getPartialQuadShader -*/ -Shader.getPartialQuadShader = function(gl) -{ - gl = gl || global.gl; - var shader = gl.shaders[":quad2"]; - if(shader) - return shader; - return gl.shaders[":quad2"] = new GL.Shader( Shader.QUAD_VERTEX_SHADER, Shader.QUAD2_FRAGMENT_SHADER ); -} - -/** -* Returns a shader that blends two textures -* shader must have: u_factor, u_texture, u_texture2 -* @method Shader.getBlendShader -*/ -Shader.getBlendShader = function(gl) -{ - gl = gl || global.gl; - var shader = gl.shaders[":blend"]; - if(shader) - return shader; - return gl.shaders[":blend"] = new GL.Shader( Shader.SCREEN_VERTEX_SHADER, Shader.BLEND_FRAGMENT_SHADER ); -} - -/** -* Returns a shader used to apply gaussian blur to one texture in one axis (you should use it twice to get a gaussian blur) -* shader params are: vec2 u_offset, float u_intensity -* @method Shader.getBlurShader -*/ -Shader.getBlurShader = function(gl) -{ - gl = gl || global.gl; - var shader = gl.shaders[":blur"]; - if(shader) - return shader; - - var shader = new GL.Shader( Shader.SCREEN_VERTEX_SHADER,"\n\ - precision highp float;\n\ - varying vec2 v_coord;\n\ - uniform sampler2D u_texture;\n\ - uniform vec2 u_offset;\n\ - uniform float u_intensity;\n\ - void main() {\n\ - vec4 sum = vec4(0.0);\n\ - sum += texture2D(u_texture, v_coord + u_offset * -4.0) * 0.05/0.98;\n\ - sum += texture2D(u_texture, v_coord + u_offset * -3.0) * 0.09/0.98;\n\ - sum += texture2D(u_texture, v_coord + u_offset * -2.0) * 0.12/0.98;\n\ - sum += texture2D(u_texture, v_coord + u_offset * -1.0) * 0.15/0.98;\n\ - sum += texture2D(u_texture, v_coord) * 0.16/0.98;\n\ - sum += texture2D(u_texture, v_coord + u_offset * 4.0) * 0.05/0.98;\n\ - sum += texture2D(u_texture, v_coord + u_offset * 3.0) * 0.09/0.98;\n\ - sum += texture2D(u_texture, v_coord + u_offset * 2.0) * 0.12/0.98;\n\ - sum += texture2D(u_texture, v_coord + u_offset * 1.0) * 0.15/0.98;\n\ - gl_FragColor = u_intensity * sum;\n\ - }\n\ - "); - return gl.shaders[":blur"] = shader; -} - -//shader to copy a depth texture into another one -Shader.getCopyDepthShader = function(gl) -{ - gl = gl || global.gl; - var shader = gl.shaders[":copy_depth"]; - if(shader) - return shader; - - var shader = new GL.Shader( Shader.SCREEN_VERTEX_SHADER,"\n\ - #extension GL_EXT_frag_depth : enable\n\ - precision highp float;\n\ - varying vec2 v_coord;\n\ - uniform sampler2D u_texture;\n\ - void main() {\n\ - gl_FragDepthEXT = texture2D( u_texture, v_coord ).x;\n\ - gl_FragColor = vec4(1.0);\n\ - }\n\ - "); - return gl.shaders[":copy_depth"] = shader; -} - -Shader.getCubemapShowShader = function(gl) -{ - gl = gl || global.gl; - var shader = gl.shaders[":show_cubemap"]; - if(shader) - return shader; - - var shader = new GL.Shader( Shader.DEFAULT_VERTEX_SHADER,"\n\ - precision highp float;\n\ - varying vec3 v_normal;\n\ - uniform samplerCube u_texture;\n\ - void main() {\n\ - gl_FragColor = textureCube( u_texture, v_normal );\n\ - }\n\ - "); - shader.uniforms({u_texture:0}); - return gl.shaders[":show_cubemap"] = shader; -} - -//shader to copy a cubemap into another -Shader.getPolarToCubemapShader = function(gl) -{ - gl = gl || global.gl; - var shader = gl.shaders[":polar_to_cubemap"]; - if(shader) - return shader; - - var shader = new GL.Shader( Shader.SCREEN_VERTEX_SHADER,"\n\ - precision highp float;\n\ - varying vec2 v_coord;\n\ - uniform sampler2D u_texture;\n\ - uniform mat3 u_rotation;\n\ - void main() {\n\ - vec2 uv = vec2( v_coord.x, 1.0 - v_coord.y );\n\ - vec3 dir = normalize( vec3( uv - vec2(0.5), 0.5 ));\n\ - dir = u_rotation * dir;\n\ - float u = atan(dir.x,dir.z) / 6.28318531;\n\ - float v = (asin(dir.y) / 1.57079633) * 0.5 + 0.5;\n\ - u = mod(u,1.0);\n\ - v = mod(v,1.0);\n\ - gl_FragColor = texture2D( u_texture, vec2(u,v) );\n\ - }\n\ - "); - return gl.shaders[":polar_to_cubemap"] = shader; -} - - -//shader to copy a cubemap into another -Shader.getCubemapCopyShader = function(gl) -{ - gl = gl || global.gl; - var shader = gl.shaders[":copy_cubemap"]; - if(shader) - return shader; - - var shader = new GL.Shader( Shader.SCREEN_VERTEX_SHADER,"\n\ - precision highp float;\n\ - varying vec2 v_coord;\n\ - uniform samplerCube u_texture;\n\ - uniform mat3 u_rotation;\n\ - void main() {\n\ - vec2 uv = vec2( v_coord.x, 1.0 - v_coord.y );\n\ - vec3 dir = vec3( uv - vec2(0.5), 0.5 );\n\ - dir = u_rotation * dir;\n\ - gl_FragColor = textureCube( u_texture, dir );\n\ - }\n\ - "); - return gl.shaders[":copy_cubemap"] = shader; -} - -//shader to blur a cubemap -Shader.getCubemapBlurShader = function(gl) -{ - gl = gl || global.gl; - var shader = gl.shaders[":blur_cubemap"]; - if(shader) - return shader; - - var shader = new GL.Shader( Shader.SCREEN_VERTEX_SHADER,"\n\ - #ifndef NUM_SAMPLES\n\ - #define NUM_SAMPLES 4\n\ - #endif\n\ - \n\ - precision highp float;\n\ - varying vec2 v_coord;\n\ - uniform samplerCube u_texture;\n\ - uniform mat3 u_rotation;\n\ - uniform vec2 u_offset;\n\ - uniform float u_intensity;\n\ - void main() {\n\ - vec4 sum = vec4(0.0);\n\ - vec2 uv = vec2( v_coord.x, 1.0 - v_coord.y ) - vec2(0.5);\n\ - vec3 dir = vec3(0.0);\n\ - vec4 color = vec4(0.0);\n\ - for( int x = -2; x <= 2; x++ )\n\ - {\n\ - for( int y = -2; y <= 2; y++ )\n\ - {\n\ - dir.xy = uv + vec2( u_offset.x * float(x), u_offset.y * float(y)) * 0.5;\n\ - dir.z = 0.5;\n\ - dir = u_rotation * dir;\n\ - color = textureCube( u_texture, dir );\n\ - color.xyz = color.xyz * color.xyz;/*linearize*/\n\ - sum += color;\n\ - }\n\ - }\n\ - sum /= 25.0;\n\ - gl_FragColor = vec4( sqrt( sum.xyz ), sum.w ) ;\n\ - }\n\ - "); - return gl.shaders[":blur_cubemap"] = shader; -} - -//shader to do FXAA (antialiasing) -Shader.FXAA_FUNC = "\n\ - uniform vec2 u_viewportSize;\n\ - uniform vec2 u_iViewportSize;\n\ - #define FXAA_REDUCE_MIN (1.0/ 128.0)\n\ - #define FXAA_REDUCE_MUL (1.0 / 8.0)\n\ - #define FXAA_SPAN_MAX 8.0\n\ - \n\ - /* from mitsuhiko/webgl-meincraft based on the code on geeks3d.com */\n\ - vec4 applyFXAA(sampler2D tex, vec2 fragCoord)\n\ - {\n\ - vec4 color = vec4(0.0);\n\ - /*vec2 u_iViewportSize = vec2(1.0 / u_viewportSize.x, 1.0 / u_viewportSize.y);*/\n\ - vec3 rgbNW = texture2D(tex, (fragCoord + vec2(-1.0, -1.0)) * u_iViewportSize).xyz;\n\ - vec3 rgbNE = texture2D(tex, (fragCoord + vec2(1.0, -1.0)) * u_iViewportSize).xyz;\n\ - vec3 rgbSW = texture2D(tex, (fragCoord + vec2(-1.0, 1.0)) * u_iViewportSize).xyz;\n\ - vec3 rgbSE = texture2D(tex, (fragCoord + vec2(1.0, 1.0)) * u_iViewportSize).xyz;\n\ - vec3 rgbM = texture2D(tex, fragCoord * u_iViewportSize).xyz;\n\ - vec3 luma = vec3(0.299, 0.587, 0.114);\n\ - float lumaNW = dot(rgbNW, luma);\n\ - float lumaNE = dot(rgbNE, luma);\n\ - float lumaSW = dot(rgbSW, luma);\n\ - float lumaSE = dot(rgbSE, luma);\n\ - float lumaM = dot(rgbM, luma);\n\ - float lumaMin = min(lumaM, min(min(lumaNW, lumaNE), min(lumaSW, lumaSE)));\n\ - float lumaMax = max(lumaM, max(max(lumaNW, lumaNE), max(lumaSW, lumaSE)));\n\ - \n\ - vec2 dir;\n\ - dir.x = -((lumaNW + lumaNE) - (lumaSW + lumaSE));\n\ - dir.y = ((lumaNW + lumaSW) - (lumaNE + lumaSE));\n\ - \n\ - float dirReduce = max((lumaNW + lumaNE + lumaSW + lumaSE) * (0.25 * FXAA_REDUCE_MUL), FXAA_REDUCE_MIN);\n\ - \n\ - float rcpDirMin = 1.0 / (min(abs(dir.x), abs(dir.y)) + dirReduce);\n\ - dir = min(vec2(FXAA_SPAN_MAX, FXAA_SPAN_MAX), max(vec2(-FXAA_SPAN_MAX, -FXAA_SPAN_MAX), dir * rcpDirMin)) * u_iViewportSize;\n\ - \n\ - vec3 rgbA = 0.5 * (texture2D(tex, fragCoord * u_iViewportSize + dir * (1.0 / 3.0 - 0.5)).xyz + \n\ - texture2D(tex, fragCoord * u_iViewportSize + dir * (2.0 / 3.0 - 0.5)).xyz);\n\ - vec3 rgbB = rgbA * 0.5 + 0.25 * (texture2D(tex, fragCoord * u_iViewportSize + dir * -0.5).xyz + \n\ - texture2D(tex, fragCoord * u_iViewportSize + dir * 0.5).xyz);\n\ - \n\ - return vec4(rgbA,1.0);\n\ - float lumaB = dot(rgbB, luma);\n\ - if ((lumaB < lumaMin) || (lumaB > lumaMax))\n\ - color = vec4(rgbA, 1.0);\n\ - else\n\ - color = vec4(rgbB, 1.0);\n\ - return color;\n\ - }\n\ -"; - -/** -* Returns a shader to apply FXAA antialiasing -* params are vec2 u_viewportSize, vec2 u_iViewportSize or you can call shader.setup() -* @method Shader.getFXAAShader -*/ -Shader.getFXAAShader = function(gl) -{ - gl = gl || global.gl; - var shader = gl.shaders[":fxaa"]; - if(shader) - return shader; - - var shader = new GL.Shader( Shader.SCREEN_VERTEX_SHADER,"\n\ - precision highp float;\n\ - varying vec2 v_coord;\n\ - uniform sampler2D u_texture;\n\ - " + Shader.FXAA_FUNC + "\n\ - \n\ - void main() {\n\ - gl_FragColor = applyFXAA( u_texture, v_coord * u_viewportSize) ;\n\ - }\n\ - "); - - var viewport = vec2.fromValues( gl.viewport_data[2], gl.viewport_data[3] ); - var iviewport = vec2.fromValues( 1/gl.viewport_data[2], 1/gl.viewport_data[3] ); - - shader.setup = function() { - viewport[0] = gl.viewport_data[2]; - viewport[1] = gl.viewport_data[3]; - iviewport[0] = 1/gl.viewport_data[2]; - iviewport[1] = 1/gl.viewport_data[3]; - this.uniforms({ u_viewportSize: viewport, u_iViewportSize: iviewport }); - } - return gl.shaders[":fxaa"] = shader; -} - -/** -* Returns a flat shader (useful to render lines) -* @method Shader.getFlatShader -*/ -Shader.getFlatShader = function(gl) -{ - gl = gl || global.gl; - var shader = gl.shaders[":flat"]; - if(shader) - return shader; - - var shader = new GL.Shader( Shader.FLAT_VERTEX_SHADER,Shader.FLAT_FRAGMENT_SHADER); - shader.uniforms({u_color:[1,1,1,1]}); - return gl.shaders[":flat"] = shader; -} - -/** -* The global scope that contains all the classes from LiteGL and also all the enums of WebGL so you dont need to create a context to use the values. -* @class GL -*/ - -/** -* creates a new WebGL context (it can create the canvas or use an existing one) -* @method create -* @param {Object} options supported are: -* - width -* - height -* - canvas -* - container (string or element) -* @return {WebGLRenderingContext} webgl context with all the extra functions (check gl in the doc for more info) -*/ -GL.create = function(options) { - options = options || {}; - var canvas = null; - if(options.canvas) - { - if(typeof(options.canvas) == "string") - { - canvas = document.getElementById( options.canvas ); - if(!canvas) throw("Canvas element not found: " + options.canvas ); - } - else - canvas = options.canvas; - } - else - { - var root = null; - if(options.container) - root = options.container.constructor === String ? document.querySelector( options.container ) : options.container; - if(root && !options.width) - { - var rect = root.getBoundingClientRect(); - options.width = rect.width; - options.height = rect.height; - } - - canvas = createCanvas( options.width || 800, options.height || 600 ); - if(root) - root.appendChild(canvas); - } - - if (!('alpha' in options)) options.alpha = false; - - - /** - * the webgl context returned by GL.create, its a WebGLRenderingContext with some extra methods added - * @class gl - */ - var gl = null; - - var seq = null; - if(options.version == 2) - seq = ['webgl2','experimental-webgl2']; - else if(options.version == 1 || options.version === undefined) //default - seq = ['webgl','experimental-webgl']; - else if(options.version === 0) //latest - seq = ['webgl2','experimental-webgl2','webgl','experimental-webgl']; - - if(!seq) - throw 'Incorrect WebGL version, must be 1 or 2'; - - var context_options = { - alpha: options.alpha === undefined ? true : options.alpha, - depth: options.depth === undefined ? true : options.depth, - stencil: options.stencil === undefined ? true : options.stencil, - antialias: options.antialias === undefined ? true : options.antialias, - premultipliedAlpha: options.premultipliedAlpha === undefined ? true : options.premultipliedAlpha, - preserveDrawingBuffer: options.preserveDrawingBuffer === undefined ? true : options.preserveDrawingBuffer - }; - - for(var i = 0; i < seq.length; ++i) - { - try { gl = canvas.getContext( seq[i], context_options ); } catch (e) {} - if(gl) - break; - } - - if (!gl) - { - if( canvas.getContext( "webgl" ) ) - throw 'WebGL supported but not with those parameters'; - throw 'WebGL not supported'; - } - - //context globals - gl.webgl_version = gl.constructor.name === "WebGL2RenderingContext" ? 2 : 1; - global.gl = gl; - canvas.is_webgl = true; - canvas.gl = gl; - gl.context_id = this.last_context_id++; - - //get all supported extensions - var supported_extensions = gl.getSupportedExtensions(); - gl.extensions = {}; - for(var i in supported_extensions) - gl.extensions[ supported_extensions[i] ] = gl.getExtension( supported_extensions[i] ); - gl.derivatives_supported = gl.extensions['OES_standard_derivatives'] != null || gl.webgl_version > 1; - - /* - gl.extensions["OES_standard_derivatives"] = gl.derivatives_supported = gl.getExtension('OES_standard_derivatives') || false; - gl.extensions["WEBGL_depth_texture"] = gl.getExtension("WEBGL_depth_texture") || gl.getExtension("WEBKIT_WEBGL_depth_texture") || gl.getExtension("MOZ_WEBGL_depth_texture"); - gl.extensions["OES_element_index_uint"] = gl.getExtension("OES_element_index_uint"); - gl.extensions["WEBGL_draw_buffers"] = gl.getExtension("WEBGL_draw_buffers"); - gl.extensions["EXT_shader_texture_lod"] = gl.getExtension("EXT_shader_texture_lod"); - gl.extensions["EXT_sRGB"] = gl.getExtension("EXT_sRGB"); - gl.extensions["EXT_texture_filter_anisotropic"] = gl.getExtension("EXT_texture_filter_anisotropic") || gl.getExtension("WEBKIT_EXT_texture_filter_anisotropic") || gl.getExtension("MOZ_EXT_texture_filter_anisotropic"); - gl.extensions["EXT_frag_depth"] = gl.getExtension("EXT_frag_depth") || gl.getExtension("WEBKIT_EXT_frag_depth") || gl.getExtension("MOZ_EXT_frag_depth"); - gl.extensions["WEBGL_lose_context"] = gl.getExtension("WEBGL_lose_context") || gl.getExtension("WEBKIT_WEBGL_lose_context") || gl.getExtension("MOZ_WEBGL_lose_context"); - gl.extensions["ANGLE_instanced_arrays"] = gl.getExtension("ANGLE_instanced_arrays"); - gl.extensions["disjoint_timer_query"] = gl.getExtension("EXT_disjoint_timer_query"); - - //for float textures - gl.extensions["OES_texture_float_linear"] = gl.getExtension("OES_texture_float_linear"); - if(gl.extensions["OES_texture_float_linear"]) - gl.extensions["OES_texture_float"] = gl.getExtension("OES_texture_float"); - gl.extensions["EXT_color_buffer_float"] = gl.getExtension("EXT_color_buffer_float"); - - //for half float textures in webgl 1 require extension - gl.extensions["OES_texture_half_float_linear"] = gl.getExtension("OES_texture_half_float_linear"); - if(gl.extensions["OES_texture_half_float_linear"]) - gl.extensions["OES_texture_half_float"] = gl.getExtension("OES_texture_half_float"); - */ - - if( gl.webgl_version == 1 ) - gl.HIGH_PRECISION_FORMAT = gl.extensions["OES_texture_half_float"] ? GL.HALF_FLOAT_OES : (gl.extensions["OES_texture_float"] ? GL.FLOAT : GL.UNSIGNED_BYTE); //because Firefox dont support half float - else - gl.HIGH_PRECISION_FORMAT = GL.HALF_FLOAT_OES; - - gl.max_texture_units = gl.getParameter(gl.MAX_TEXTURE_IMAGE_UNITS); - - //viewport hack to retrieve it without using getParameter (which is slow and generates garbage) - if(!gl._viewport_func) - { - gl._viewport_func = gl.viewport; - gl.viewport_data = new Float32Array([0,0,gl.canvas.width,gl.canvas.height]); //32000 max viewport, I guess its fine - gl.viewport = function(a,b,c,d) { var v = this.viewport_data; v[0] = a|0; v[1] = b|0; v[2] = c|0; v[3] = d|0; this._viewport_func(a,b,c,d); } - gl.getViewport = function(v) { - if(v) { v[0] = gl.viewport_data[0]; v[1] = gl.viewport_data[1]; v[2] = gl.viewport_data[2]; v[3] = gl.viewport_data[3]; return v; } - return new Float32Array( gl.viewport_data ); - }; - gl.setViewport = function( v, flip_y ) { - gl.viewport_data.set(v); - if(flip_y) - gl.viewport_data[1] = this.drawingBufferHeight-v[1]-v[3]; - this._viewport_func(v[0],gl.viewport_data[1],v[2],v[3]); - }; - } - else - console.warn("Creating LiteGL context over the same canvas twice"); - - //reverse names helper (assuming no names repeated) - if(!GL.reverse) - { - GL.reverse = {}; - for(var i in gl) - if( gl[i] && gl[i].constructor === Number ) - GL.reverse[ gl[i] ] = i; - } - - //just some checks - if(typeof(glMatrix) == "undefined") - throw("glMatrix not found, LiteGL requires glMatrix to be included"); - - var last_click_time = 0; - - //some global containers, use them to reuse assets - gl.shaders = {}; - gl.textures = {}; - gl.meshes = {}; - - /** - * sets this context as the current global gl context (in case you have more than one) - * @method makeCurrent - */ - gl.makeCurrent = function() - { - global.gl = this; - } - - /** - * executes callback inside this webgl context - * @method execute - * @param {Function} callback - */ - gl.execute = function(callback) - { - var old_gl = global.gl; - global.gl = this; - callback(); - global.gl = old_gl; - } - - - /** - * Launch animation loop (calls gl.onupdate and gl.ondraw every frame) - * example: gl.ondraw = function(){ ... } or gl.onupdate = function(dt) { ... } - * @method animate - */ - gl.animate = function(v) { - if(v === false) - { - global.cancelAnimationFrame( this._requestFrame_id ); - this._requestFrame_id = null; - return; - } - - var post = global.requestAnimationFrame; - var time = getTime(); - var context = this; - - //loop only if browser tab visible - function loop() { - if(gl.destroyed) //to stop rendering once it is destroyed - return; - - context._requestFrame_id = post(loop); //do it first, in case it crashes - - var now = getTime(); - var dt = (now - time) * 0.001; - if(context.mouse) - context.mouse.last_buttons = context.mouse.buttons; - if (context.onupdate) - context.onupdate(dt); - LEvent.trigger( context, "update", dt); - if (context.ondraw) - { - //make sure the ondraw is called using this gl context (in case there is more than one) - var old_gl = global.gl; - global.gl = context; - //call ondraw - context.ondraw(); - LEvent.trigger(context,"draw"); - //restore old context - global.gl = old_gl; - } - time = now; - } - this._requestFrame_id = post(loop); //launch main loop - } - - //store binded to be able to remove them if destroyed - /* - var _binded_events = []; - function addEvent(object, type, callback) - { - _binded_events.push(object,type,callback); - } - */ - - /** - * Destroy this WebGL context (removes also the Canvas from the DOM) - * @method destroy - */ - gl.destroy = function() { - //unbind global events - if(onkey_handler) - { - document.removeEventListener("keydown", onkey_handler ); - document.removeEventListener("keyup", onkey_handler ); - } - - if(this.canvas.parentNode) - this.canvas.parentNode.removeChild(this.canvas); - this.destroyed = true; - if(global.gl == this) - global.gl = null; - } - - var mouse = gl.mouse = { - buttons: 0, //this should always be up-to-date with mouse state - last_buttons: 0, //button state in the previous frame - left_button: false, - middle_button: false, - right_button: false, - position: new Float32Array(2), - x:0, //in canvas coordinates - y:0, - deltax: 0, - deltay: 0, - clientx:0, //in client coordinates - clienty:0, - isInsideRect: function(x,y,w,h, flip_y ) - { - var mouse_y = this.y; - if(flip_y) - mouse_y = gl.canvas.height - mouse_y; - if( this.x > x && this.x < x + w && - mouse_y > y && mouse_y < y + h) - return true; - return false; - }, - - /** - * returns true if button num is pressed (where num could be GL.LEFT_MOUSE_BUTTON, GL.RIGHT_MOUSE_BUTTON, GL.MIDDLE_MOUSE_BUTTON - * @method captureMouse - * @param {boolean} capture_wheel capture also the mouse wheel - */ - isButtonPressed: function(num) - { - if(num == GL.LEFT_MOUSE_BUTTON) - return this.buttons & GL.LEFT_MOUSE_BUTTON_MASK; - if(num == GL.MIDDLE_MOUSE_BUTTON) - return this.buttons & GL.MIDDLE_MOUSE_BUTTON_MASK; - if(num == GL.RIGHT_MOUSE_BUTTON) - return this.buttons & GL.RIGHT_MOUSE_BUTTON_MASK; - }, - - wasButtonPressed: function(num) - { - var mask = 0; - if(num == GL.LEFT_MOUSE_BUTTON) - mask = GL.LEFT_MOUSE_BUTTON_MASK; - else if(num == GL.MIDDLE_MOUSE_BUTTON) - mask = GL.MIDDLE_MOUSE_BUTTON_MASK; - else if(num == GL.RIGHT_MOUSE_BUTTON) - mask = GL.RIGHT_MOUSE_BUTTON_MASK; - return (this.buttons & mask) && !(this.last_buttons & mask); - } - }; - - /** - * Tells the system to capture mouse events on the canvas. - * This will trigger onmousedown, onmousemove, onmouseup, onmousewheel callbacks assigned in the gl context - * example: gl.onmousedown = function(e){ ... } - * The event is a regular MouseEvent with some extra parameters - * @method captureMouse - * @param {boolean} capture_wheel capture also the mouse wheel - */ - gl.captureMouse = function(capture_wheel, translate_touchs ) { - - canvas.addEventListener("mousedown", onmouse); - canvas.addEventListener("mousemove", onmouse); - canvas.addEventListener("dragstart", onmouse); - //canvas.addEventListener("mouseup", onmouse); ?? - if(capture_wheel) - { - canvas.addEventListener("mousewheel", onmouse, false); - canvas.addEventListener("wheel", onmouse, false); - //canvas.addEventListener("DOMMouseScroll", onmouse, false); //deprecated or non-standard - } - //prevent right click context menu - canvas.addEventListener("contextmenu", function(e) { e.preventDefault(); return false; }); - - if( translate_touchs ) - this.captureTouch( true ); - } - - function onmouse(e) { - - if(gl.ignore_events) - return; - //console.log(e.type); //debug - var old_mouse_mask = gl.mouse.buttons; - GL.augmentEvent(e, canvas); - e.eventType = e.eventType || e.type; //type cannot be overwritten, so I make a clone to allow me to overwrite - var now = getTime(); - - //gl.mouse info - mouse.dragging = e.dragging; - mouse.position[0] = e.canvasx; - mouse.position[1] = e.canvasy; - mouse.x = e.canvasx; - mouse.y = e.canvasy; - mouse.mousex = e.mousex; - mouse.mousey = e.mousey; - mouse.canvasx = e.canvasx; - mouse.canvasy = e.canvasy; - mouse.clientx = e.mousex; - mouse.clienty = e.mousey; - mouse.buttons = e.buttons; - mouse.left_button = !!(mouse.buttons & GL.LEFT_MOUSE_BUTTON_MASK); - mouse.middle_button = !!(mouse.buttons & GL.MIDDLE_MOUSE_BUTTON_MASK); - mouse.right_button = !!(mouse.buttons & GL.RIGHT_MOUSE_BUTTON_MASK); - - if(e.eventType == "mousedown") - { - if(old_mouse_mask == 0) //no mouse button was pressed till now - { - canvas.removeEventListener("mousemove", onmouse); - var doc = canvas.ownerDocument; - doc.addEventListener("mousemove", onmouse); - doc.addEventListener("mouseup", onmouse); - } - last_click_time = now; - - if(gl.onmousedown) - gl.onmousedown(e); - LEvent.trigger(gl,"mousedown"); - } - else if(e.eventType == "mousemove") - { - if(gl.onmousemove) - gl.onmousemove(e); - LEvent.trigger(gl,"mousemove",e); - } - else if(e.eventType == "mouseup") - { - //console.log("mouseup"); - if(gl.mouse.buttons == 0) //no more buttons pressed - { - canvas.addEventListener("mousemove", onmouse); - var doc = canvas.ownerDocument; - doc.removeEventListener("mousemove", onmouse); - doc.removeEventListener("mouseup", onmouse); - } - e.click_time = now - last_click_time; - //last_click_time = now; //commented to avoid reseting click time when unclicking two mouse buttons - - if(gl.onmouseup) - gl.onmouseup(e); - LEvent.trigger(gl,"mouseup",e); - } - else if((e.eventType == "mousewheel" || e.eventType == "wheel" || e.eventType == "DOMMouseScroll")) - { - e.eventType = "mousewheel"; - if(e.type == "wheel") - e.wheel = -e.deltaY; //in firefox deltaY is 1 while in Chrome is 120 - else - e.wheel = (e.wheelDeltaY != null ? e.wheelDeltaY : e.detail * -60); - - //from stack overflow - //firefox doesnt have wheelDelta - e.delta = e.wheelDelta !== undefined ? (e.wheelDelta/40) : (e.deltaY ? -e.deltaY/3 : 0); - //console.log(e.delta); - if(gl.onmousewheel) - gl.onmousewheel(e); - - LEvent.trigger(gl, "mousewheel", e); - } - else if(e.eventType == "dragstart") - { - if(gl.ondragstart) - gl.ondragstart(e); - LEvent.trigger(gl, "dragstart", e); - } - - if(gl.onmouse) - gl.onmouse(e); - - if(!e.skip_preventDefault) - { - if(e.eventType != "mousemove") - e.stopPropagation(); - e.preventDefault(); - return false; - } - } - - var translate_touches = false; - - gl.captureTouch = function( translate_to_mouse_events ) - { - translate_touches = translate_to_mouse_events; - - canvas.addEventListener("touchstart", ontouch, true); - canvas.addEventListener("touchmove", ontouch, true); - canvas.addEventListener("touchend", ontouch, true); - canvas.addEventListener("touchcancel", ontouch, true); - - canvas.addEventListener('gesturestart', ongesture ); - canvas.addEventListener('gesturechange', ongesture ); - canvas.addEventListener('gestureend', ongesture ); - } - - //translates touch events in mouseevents - function ontouch( e ) - { - var touches = e.changedTouches, - first = touches[0], - type = ""; - - if( gl.ontouch && gl.ontouch(e) === true ) - return; - - if( LEvent.trigger( gl, e.type, e ) === true ) - return; - - if(!translate_touches) - return; - - //ignore secondary touches - if(e.touches.length && e.changedTouches[0].identifier !== e.touches[0].identifier) - return; - - if(touches > 1) - return; - - switch(e.type) - { - case "touchstart": type = "mousedown"; break; - case "touchmove": type = "mousemove"; break; - case "touchend": type = "mouseup"; break; - default: return; - } - - var simulatedEvent = document.createEvent("MouseEvent"); - simulatedEvent.initMouseEvent(type, true, true, window, 1, - first.screenX, first.screenY, - first.clientX, first.clientY, false, - false, false, false, 0/*left*/, null); - simulatedEvent.originalEvent = simulatedEvent; - simulatedEvent.is_touch = true; - first.target.dispatchEvent( simulatedEvent ); - e.preventDefault(); - } - - function ongesture(e) - { - e.eventType = e.type; - - if(gl.ongesture && gl.ongesture(e) === false ) - return; - - if( LEvent.trigger( gl, e.type, e ) === false ) - return; - - e.preventDefault(); - } - - var keys = gl.keys = {}; - - /** - * Tells the system to capture key events on the canvas. This will trigger onkey - * @method captureKeys - * @param {boolean} prevent_default prevent default behaviour (like scroll on the web, etc) - * @param {boolean} only_canvas only caches keyboard events if they happen when the canvas is in focus - */ - var onkey_handler = null; - gl.captureKeys = function( prevent_default, only_canvas ) { - if(onkey_handler) - return; - gl.keys = {}; - - var target = only_canvas ? gl.canvas : document; - - document.addEventListener("keydown", inner ); - document.addEventListener("keyup", inner ); - function inner(e) { onkey(e, prevent_default); } - onkey_handler = inner; - } - - - - function onkey(e, prevent_default) - { - e.eventType = e.type; //type cannot be overwritten, so I make a clone to allow me to overwrite - - var target_element = e.target.nodeName.toLowerCase(); - if(target_element === "input" || target_element === "textarea" || target_element === "select") - return; - - e.character = String.fromCharCode(e.keyCode).toLowerCase(); - var prev_state = false; - var key = GL.mapKeyCode(e.keyCode); - if(!key) //this key doesnt look like an special key - key = e.character; - - //regular key - if (!e.altKey && !e.ctrlKey && !e.metaKey) { - if (key) - gl.keys[key] = e.type == "keydown"; - prev_state = gl.keys[e.keyCode]; - gl.keys[e.keyCode] = e.type == "keydown"; - } - - //avoid repetition if key stays pressed - if(prev_state != gl.keys[e.keyCode]) - { - if(e.type == "keydown" && gl.onkeydown) - gl.onkeydown(e); - else if(e.type == "keyup" && gl.onkeyup) - gl.onkeyup(e); - LEvent.trigger(gl, e.type, e); - } - - if(gl.onkey) - gl.onkey(e); - - if(prevent_default && (e.isChar || GL.blockable_keys[e.keyIdentifier || e.key ]) ) - e.preventDefault(); - } - - //gamepads - gl.gamepads = null; - /* - function onButton(e, pressed) - { - console.log(e); - if(pressed && gl.onbuttondown) - gl.onbuttondown(e); - else if(!pressed && gl.onbuttonup) - gl.onbuttonup(e); - if(gl.onbutton) - gl.onbutton(e); - LEvent.trigger(gl, pressed ? "buttondown" : "buttonup", e ); - } - function onGamepad(e) - { - console.log(e); - if(gl.ongamepad) - gl.ongamepad(e); - } - */ - - /** - * Tells the system to capture gamepad events on the canvas. - * @method captureGamepads - */ - gl.captureGamepads = function() - { - var getGamepads = navigator.getGamepads || navigator.webkitGetGamepads || navigator.mozGetGamepads; - if(!getGamepads) return; - this.gamepads = getGamepads.call(navigator); - - //only in firefox, so I cannot rely on this - /* - window.addEventListener("gamepadButtonDown", function(e) { onButton(e, true); }, false); - window.addEventListener("MozGamepadButtonDown", function(e) { onButton(e, true); }, false); - window.addEventListener("WebkitGamepadButtonDown", function(e) { onButton(e, true); }, false); - window.addEventListener("gamepadButtonUp", function(e) { onButton(e, false); }, false); - window.addEventListener("MozGamepadButtonUp", function(e) { onButton(e, false); }, false); - window.addEventListener("WebkitGamepadButtonUp", function(e) { onButton(e, false); }, false); - window.addEventListener("gamepadconnected", onGamepad, false); - window.addEventListener("gamepaddisconnected", onGamepad, false); - */ - - } - - /** - * returns the detected gamepads on the system - * @method getGamepads - * @param {bool} skip_mapping if set to true it returns the basic gamepad, otherwise it returns a class with mapping info to XBOX controller - */ - gl.getGamepads = function(skip_mapping) - { - //gamepads - var getGamepads = navigator.getGamepads || navigator.webkitGetGamepads || navigator.mozGetGamepads; - if(!getGamepads) - return; - var gamepads = getGamepads.call(navigator); - if(!this.gamepads) - this.gamepads = []; - - //iterate to generate events - for(var i = 0; i < 4; i++) - { - var gamepad = gamepads[i]; //current state - - if(gamepad && !skip_mapping) - addGamepadXBOXmapping(gamepad); - - //launch connected gamepads events - if(gamepad && !gamepad.prev_buttons) - { - gamepad.prev_buttons = new Uint8Array(32); - var event = new CustomEvent("gamepadconnected"); - event.eventType = event.type; - event.gamepad = gamepad; - if(this.ongamepadconnected) - this.ongamepadconnected(event); - LEvent.trigger(gl,"gamepadconnected",event); - } - /* - else if(old_gamepad && !gamepad) - { - var event = new CustomEvent("gamepaddisconnected"); - event.eventType = event.type; - event.gamepad = old_gamepad; - if(this.ongamepaddisconnected) - this.ongamepaddisconnected(event); - LEvent.trigger(gl,"gamepaddisconnected",event); - } - */ - - //seek buttons changes to trigger events - if(gamepad) - { - for(var j = 0; j < gamepad.buttons.length; ++j) - { - var button = gamepad.buttons[j]; - button.was_pressed = false; - if( button.pressed && !gamepad.prev_buttons[j] ) - { - button.was_pressed = true; - var event = new CustomEvent("gamepadButtonDown"); - event.eventType = event.type; - event.button = button; - event.which = j; - event.gamepad = gamepad; - if(gl.onbuttondown) - gl.onbuttondown(event); - LEvent.trigger(gl,"buttondown",event); - } - else if( !button.pressed && gamepad.prev_buttons[j] ) - { - var event = new CustomEvent("gamepadButtonUp"); - event.eventType = event.type; - event.button = button; - event.which = j; - event.gamepad = gamepad; - if(gl.onbuttondown) - gl.onbuttondown(event); - LEvent.trigger(gl,"buttonup",event); - } - - gamepad.prev_buttons[j] = button.pressed ? 1 : 0; - } - } - } - this.gamepads = gamepads; - return gamepads; - } - - function addGamepadXBOXmapping(gamepad) - { - //xbox controller mapping - var xbox = gamepad.xbox || { axes:[], buttons:{}, hat: ""}; - xbox.axes["lx"] = gamepad.axes[0]; - xbox.axes["ly"] = gamepad.axes[1]; - xbox.axes["rx"] = gamepad.axes[2]; - xbox.axes["ry"] = gamepad.axes[3]; - xbox.axes["triggers"] = gamepad.axes[4]; - - for(var i = 0; i < gamepad.buttons.length; i++) - { - switch(i) //I use a switch to ensure that a player with another gamepad could play - { - case 0: xbox.buttons["a"] = gamepad.buttons[i].pressed; break; - case 1: xbox.buttons["b"] = gamepad.buttons[i].pressed; break; - case 2: xbox.buttons["x"] = gamepad.buttons[i].pressed; break; - case 3: xbox.buttons["y"] = gamepad.buttons[i].pressed; break; - case 4: xbox.buttons["lb"] = gamepad.buttons[i].pressed; break; - case 5: xbox.buttons["rb"] = gamepad.buttons[i].pressed; break; - case 6: xbox.buttons["lt"] = gamepad.buttons[i].pressed; break; - case 7: xbox.buttons["rt"] = gamepad.buttons[i].pressed; break; - case 8: xbox.buttons["back"] = gamepad.buttons[i].pressed; break; - case 9: xbox.buttons["start"] = gamepad.buttons[i].pressed; break; - case 10: xbox.buttons["ls"] = gamepad.buttons[i].pressed; break; - case 11: xbox.buttons["rs"] = gamepad.buttons[i].pressed; break; - case 12: if( gamepad.buttons[i].pressed) xbox.hat += "up"; break; - case 13: if( gamepad.buttons[i].pressed) xbox.hat += "down"; break; - case 14: if( gamepad.buttons[i].pressed) xbox.hat += "left"; break; - case 15: if( gamepad.buttons[i].pressed) xbox.hat += "right"; break; - case 16: xbox.buttons["home"] = gamepad.buttons[i].pressed; break; - default: - } - } - gamepad.xbox = xbox; - } - - /** - * launches de canvas in fullscreen mode - * @method fullscreen - */ - gl.fullscreen = function() - { - var canvas = this.canvas; - if(canvas.requestFullScreen) - canvas.requestFullScreen(); - else if(canvas.webkitRequestFullScreen) - canvas.webkitRequestFullScreen(); - else if(canvas.mozRequestFullScreen) - canvas.mozRequestFullScreen(); - else - console.error("Fullscreen not supported"); - } - - /** - * returns a canvas with a snapshot of an area - * this is safer than using the canvas itself due to internals of webgl - * @method snapshot - * @param {Number} startx viewport x coordinate - * @param {Number} starty viewport y coordinate from bottom - * @param {Number} areax viewport area width - * @param {Number} areay viewport area height - * @return {Canvas} canvas - */ - gl.snapshot = function(startx, starty, areax, areay, skip_reverse) - { - var canvas = createCanvas(areax,areay); - var ctx = canvas.getContext("2d"); - var pixels = ctx.getImageData(0,0,canvas.width,canvas.height); - - var buffer = new Uint8Array(areax * areay * 4); - gl.readPixels(startx, starty, canvas.width, canvas.height, gl.RGBA,gl.UNSIGNED_BYTE, buffer); - - pixels.data.set( buffer ); - ctx.putImageData(pixels,0,0); - - if(skip_reverse) - return canvas; - - //flip image - var final_canvas = createCanvas(areax,areay); - var ctx = final_canvas.getContext("2d"); - ctx.translate(0,areay); - ctx.scale(1,-1); - ctx.drawImage(canvas,0,0); - - return final_canvas; - } - - //from https://webgl2fundamentals.org/webgl/lessons/webgl1-to-webgl2.html - function getAndApplyExtension( gl, name ) { - var ext = gl.getExtension(name); - if (!ext) { - return false; - } - var suffix = name.split("_")[0]; - var prefix = suffix = '_'; - var suffixRE = new RegExp(suffix + '$'); - var prefixRE = new RegExp('^' + prefix); - for (var key in ext) { - var val = ext[key]; - if (typeof(val) === 'function') { - // remove suffix (eg: bindVertexArrayOES -> bindVertexArray) - var unsuffixedKey = key.replace(suffixRE, ''); - if (key.substing) - gl[unprefixedKey] = ext[key].bind(ext); - } else { - var unprefixedKey = key.replace(prefixRE, ''); - gl[unprefixedKey] = ext[key]; - } - } - } - - - //mini textures manager - var loading_textures = {}; - /** - * returns a texture and caches it inside gl.textures[] - * @method loadTexture - * @param {String} url - * @param {Object} options (same options as when creating a texture) - * @param {Function} callback function called once the texture is loaded - * @return {Texture} texture - */ - gl.loadTexture = function(url, options, on_load) - { - if(this.textures[ url ]) - return this.textures[url]; - - if( loading_textures[url] ) - return null; - - var img = new Image(); - img.url = url; - img.onload = function() - { - var texture = GL.Texture.fromImage(this, options); - texture.img = this; - gl.textures[this.url] = texture; - delete loading_textures[this.url]; - if(on_load) - on_load(texture); - } - img.src = url; - loading_textures[url] = true; - return null; - } - - /** - * draws a texture to the viewport - * @method drawTexture - * @param {Texture} texture - * @param {number} x in viewport coordinates - * @param {number} y in viewport coordinates - * @param {number} w in viewport coordinates - * @param {number} h in viewport coordinates - * @param {number} tx texture x in texture coordinates - * @param {number} ty texture y in texture coordinates - * @param {number} tw texture width in texture coordinates - * @param {number} th texture height in texture coordinates - */ - gl.drawTexture = (function() { - //static variables: less garbage - var identity = mat3.create(); - var pos = vec2.create(); - var size = vec2.create(); - var area = vec4.create(); - var white = vec4.fromValues(1,1,1,1); - var viewport = vec2.create(); - var _uniforms = {u_texture: 0, u_position: pos, u_color: white, u_size: size, u_texture_area: area, u_viewport: viewport, u_transform: identity }; - - return (function(texture, x,y, w,h, tx,ty, tw,th, shader, uniforms) - { - pos[0] = x; pos[1] = y; - if(w === undefined) - w = texture.width; - if(h === undefined) - h = texture.height; - size[0] = w; - size[1] = h; - - if(tx === undefined) tx = 0; - if(ty === undefined) ty = 0; - if(tw === undefined) tw = texture.width; - if(th === undefined) th = texture.height; - - area[0] = tx / texture.width; - area[1] = ty / texture.height; - area[2] = (tx + tw) / texture.width; - area[3] = (ty + th) / texture.height; - - viewport[0] = this.viewport_data[2]; - viewport[1] = this.viewport_data[3]; - - shader = shader || Shader.getPartialQuadShader(this); - var mesh = Mesh.getScreenQuad(this); - texture.bind(0); - shader.uniforms( _uniforms ); - if( uniforms ) - shader.uniforms( uniforms ); - shader.draw( mesh, gl.TRIANGLES ); - }); - })(); - - gl.canvas.addEventListener("webglcontextlost", function(e) { - e.preventDefault(); - gl.context_lost = true; - if(gl.onlosecontext) - gl.onlosecontext(e); - }, false); - - /** - * use it to reset the the initial gl state - * @method gl.reset - */ - gl.reset = function() - { - //viewport - gl.viewport(0,0, this.canvas.width, this.canvas.height ); - - //flags - gl.disable( gl.BLEND ); - gl.disable( gl.CULL_FACE ); - gl.disable( gl.DEPTH_TEST ); - gl.frontFace( gl.CCW ); - - //texture - gl._current_texture_drawto = null; - gl._current_fbo_color = null; - gl._current_fbo_depth = null; - } - - gl.dump = function() - { - console.log("userAgent: ", navigator.userAgent ); - console.log("Supported extensions:"); - var extensions = gl.getSupportedExtensions(); - console.log( extensions.join(",") ); - var info = [ "VENDOR", "VERSION", "MAX_VERTEX_ATTRIBS", "MAX_VARYING_VECTORS", "MAX_VERTEX_UNIFORM_VECTORS", "MAX_VERTEX_TEXTURE_IMAGE_UNITS", "MAX_FRAGMENT_UNIFORM_VECTORS", "MAX_TEXTURE_SIZE", "MAX_TEXTURE_IMAGE_UNITS" ]; - console.log("WebGL info:"); - for(var i in info) - console.log(" * " + info[i] + ": " + gl.getParameter( gl[info[i]] )); - console.log("*************************************************") - } - - //Reset state - gl.reset(); - - //Return - return gl; -} - -GL.mapKeyCode = function(code) -{ - var named = { - 8: 'BACKSPACE', - 9: 'TAB', - 13: 'ENTER', - 16: 'SHIFT', - 17: 'CTRL', - 27: 'ESCAPE', - 32: 'SPACE', - 37: 'LEFT', - 38: 'UP', - 39: 'RIGHT', - 40: 'DOWN' - }; - return named[code] || (code >= 65 && code <= 90 ? String.fromCharCode(code) : null); -} - -//add useful info to the event -GL.dragging = false; -GL.last_pos = [0,0]; - -//adds extra info to the MouseEvent (coordinates in canvas axis, deltas and button state) -GL.augmentEvent = function(e, root_element) -{ - var offset_left = 0; - var offset_top = 0; - var b = null; - - root_element = root_element || e.target || gl.canvas; - b = root_element.getBoundingClientRect(); - - e.mousex = e.clientX - b.left; - e.mousey = e.clientY - b.top; - e.canvasx = e.mousex; - e.canvasy = b.height - e.mousey; - e.deltax = 0; - e.deltay = 0; - - if(e.type == "mousedown") - { - this.dragging = true; - //gl.mouse.buttons |= (1 << e.which); //enable - } - else if (e.type == "mousemove") - { - } - else if (e.type == "mouseup") - { - //gl.mouse.buttons = gl.mouse.buttons & ~(1 << e.which); - if(e.buttons == 0) - this.dragging = false; - } - - if( e.movementX !== undefined && !GL.isMobile() ) //pointer lock (mobile gives always zero) - { - //console.log( e.movementX ) - e.deltax = e.movementX; - e.deltay = e.movementY; - } - else - { - e.deltax = e.mousex - this.last_pos[0]; - e.deltay = e.mousey - this.last_pos[1]; - } - this.last_pos[0] = e.mousex; - this.last_pos[1] = e.mousey; - - //insert info in event - e.dragging = this.dragging; - e.leftButton = !!(gl.mouse.buttons & GL.LEFT_MOUSE_BUTTON_MASK); - e.middleButton = !!(gl.mouse.buttons & GL.MIDDLE_MOUSE_BUTTON_MASK); - e.rightButton = !!(gl.mouse.buttons & GL.RIGHT_MOUSE_BUTTON_MASK); - //shitty non-coherent API, e.buttons use 1:left,2:right,4:middle) but e.button uses (0:left,1:middle,2:right) - e.buttons_mask = 0; - if( e.leftButton ) e.buttons_mask = 1; - if( e.middleButton ) e.buttons_mask |= 2; - if( e.rightButton ) e.buttons_mask |= 4; - e.isButtonPressed = function(num) { return this.buttons_mask & (1<= 0; --j) //iterate backwards to avoid problems after removing - { - if( array[j][1] != target_instance || (callback && callback !== array[j][0]) ) - continue; - - array.splice(j,1);//remove - } - } - }, - - /** - * Unbinds all callbacks associated to one specific event from this instance - * @method LEvent.unbindAll - * @param {Object} instance where the events are binded - * @param {String} event name of the event you want to remove all binds - **/ - unbindAllEvent: function( instance, event_type ) - { - if(!instance) - throw("cannot unbind events in null"); - - var events = instance.__levents; - if(!events) - return; - delete events[ event_type ]; - if( instance.onLEventUnbindAll ) - instance.onLEventUnbindAll( event_type, target_instance, callback ); - return; - }, - - /** - * Tells if there is a binded callback that matches the criteria - * @method LEvent.isBind - * @param {Object} instance where the are the events binded - * @param {String} event_name string defining the event name - * @param {function} callback the callback - * @param {Object} target_instance [Optional] instance binded to callback - **/ - isBind: function( instance, event_type, callback, target_instance ) - { - if(!instance) - throw("LEvent cannot have null as instance"); - - var events = instance.__levents; - if( !events ) - return; - - if( !events.hasOwnProperty(event_type) ) - return false; - - for(var i = 0, l = events[event_type].length; i < l; ++i) - { - var v = events[event_type][i]; - if(v[0] === callback && v[1] === target_instance) - return true; - } - return false; - }, - - /** - * Tells if there is any callback binded to this event - * @method LEvent.hasBind - * @param {Object} instance where the are the events binded - * @param {String} event_name string defining the event name - * @return {boolean} true is there is at least one - **/ - hasBind: function( instance, event_type ) - { - if(!instance) - throw("LEvent cannot have null as instance"); - var events = instance.__levents; - if(!events || !events.hasOwnProperty( event_type ) || !events[event_type].length) - return false; - return true; - }, - - /** - * Tells if there is any callback binded to this object pointing to a method in the target object - * @method LEvent.hasBindTo - * @param {Object} instance where there are the events binded - * @param {Object} target instance to check to - * @return {boolean} true is there is at least one - **/ - hasBindTo: function( instance, target ) - { - if(!instance) - throw("LEvent cannot have null as instance"); - var events = instance.__levents; - - //no events binded - if(!events) - return false; - - for(var j in events) - { - var binds = events[j]; - for(var i = 0; i < binds.length; ++i) - { - if(binds[i][1] === target) //one found - return true; - } - } - - return false; - }, - - /** - * Triggers and event in an instance - * If the callback returns true then it will stop the propagation and return true - * @method LEvent.trigger - * @param {Object} instance that triggers the event - * @param {String} event_name string defining the event name - * @param {*} parameters that will be received by the binded function - * @param {bool} reverse_order trigger in reverse order (binded last get called first) - * @param {bool} expand_parameters parameters are passed not as one single parameter, but as many - * return {bool} true if the event passed was blocked by any binded callback - **/ - trigger: function( instance, event_type, params, reverse_order, expand_parameters ) - { - if(!instance) - throw("cannot trigger event from null"); - if(instance.constructor === String ) - throw("cannot bind event to a string"); - - var events = instance.__levents; - if( !events || !events.hasOwnProperty(event_type) ) - return false; - - var inst = events[event_type]; - if( reverse_order ) - { - for(var i = inst.length - 1; i >= 0; --i) - { - var v = inst[i]; - if(expand_parameters) - { - if( v && v[0].apply( v[1], params ) === true)// || event.stop) - return true; //stopPropagation - } - else - { - if( v && v[0].call( v[1], event_type, params) === true)// || event.stop) - return true; //stopPropagation - } - } - } - else - { - for(var i = 0, l = inst.length; i < l; ++i) - { - var v = inst[i]; - if( expand_parameters ) - { - if( v && v[0].apply( v[1], params ) === true)// || event.stop) - return true; //stopPropagation - } - else - { - if( v && v[0].call(v[1], event_type, params) === true)// || event.stop) - return true; //stopPropagation - } - } - } - - return false; - }, - - /** - * Triggers and event to every element in an array. - * If the event returns true, it must be intercepted - * @method LEvent.triggerArray - * @param {Array} array contains all instances to triggers the event - * @param {String} event_name string defining the event name - * @param {*} parameters that will be received by the binded function - * @param {bool} reverse_order trigger in reverse order (binded last get called first) - * @param {bool} expand_parameters parameters are passed not as one single parameter, but as many - * return {bool} false - **/ - triggerArray: function( instances, event_type, params, reverse_order, expand_parameters ) - { - var blocked = false; - for(var i = 0, l = instances.length; i < l; ++i) - { - var instance = instances[i]; - if(!instance) - throw("cannot trigger event from null"); - if(instance.constructor === String ) - throw("cannot bind event to a string"); - - var events = instance.__levents; - if( !events || !events.hasOwnProperty( event_type ) ) - continue; - - if( reverse_order ) - { - for(var j = events[event_type].length - 1; j >= 0; --j) - { - var v = events[event_type][j]; - if(expand_parameters) - { - if( v[0].apply(v[1], params ) === true)// || event.stop) - { - blocked = true; - break; //stopPropagation - } - } - else - { - if( v[0].call(v[1], event_type, params) === true)// || event.stop) - { - blocked = true; - break; //stopPropagation - } - } - } - } - else - { - for(var j = 0, ll = events[event_type].length; j < ll; ++j) - { - var v = events[event_type][j]; - if(expand_parameters) - { - if( v[0].apply(v[1], params ) === true)// || event.stop) - { - blocked = true; - break; //stopPropagation - } - } - else - { - if( v[0].call(v[1], event_type, params) === true)// || event.stop) - { - blocked = true; - break; //stopPropagation - } - } - } - } - } - - return blocked; - }, - - extendObject: function( object ) - { - object.bind = function( event_type, callback, instance ){ - return LEvent.bind( this, event_type, callback, instance ); - }; - - object.trigger = function( event_type, params ){ - return LEvent.trigger( this, event_type, params ); - }; - - object.unbind = function( event_type, callback, target_instance ) - { - return LEvent.unbind( this, event_type, callback, instance ); - }; - - object.unbindAll = function( target_instance, callback ) - { - return LEvent.unbindAll( this, target_instance, callback ); - }; - }, - - /** - * Adds the methods to bind, trigger and unbind to this class prototype - * @method LEvent.extendClass - * @param {Object} constructor - **/ - extendClass: function( constructor ) - { - this.extendObject( constructor.prototype ); - } -}; -/* geometric utilities */ -global.CLIP_INSIDE = GL.CLIP_INSIDE = 0; -global.CLIP_OUTSIDE = GL.CLIP_OUTSIDE = 1; -global.CLIP_OVERLAP = GL.CLIP_OVERLAP = 2; - -/** -* @namespace -*/ - - -/** -* Computational geometry algorithms, is a static class -* @class geo -*/ - -global.geo = { - - /** - * Returns a float4 containing the info about a plane with normal N and that passes through point P - * @method createPlane - * @param {vec3} P - * @param {vec3} N - * @return {vec4} plane values - */ - createPlane: function(P,N) - { - return new Float32Array([N[0],N[1],N[2],-vec3.dot(P,N)]); - }, - - /** - * Computes the distance between the point and the plane - * @method distancePointToPlane - * @param {vec3} point - * @param {vec4} plane - * @return {Number} distance - */ - distancePointToPlane: function(point, plane) - { - return (vec3.dot(point,plane) + plane[3])/Math.sqrt(plane[0]*plane[0] + plane[1]*plane[1] + plane[2]*plane[2]); - }, - - /** - * Computes the square distance between the point and the plane - * @method distance2PointToPlane - * @param {vec3} point - * @param {vec4} plane - * @return {Number} distance*distance - */ - distance2PointToPlane: function(point, plane) - { - return (vec3.dot(point,plane) + plane[3])/(plane[0]*plane[0] + plane[1]*plane[1] + plane[2]*plane[2]); - }, - - /** - * Projects a 3D point on a 3D line - * @method projectPointOnLine - * @param {vec3} P - * @param {vec3} A line start - * @param {vec3} B line end - * @param {vec3} result to store result (optional) - * @return {vec3} projectec point - */ - projectPointOnLine: function( P, A, B, result ) - { - result = result || vec3.create(); - //A + dot(AP,AB) / dot(AB,AB) * AB - var AP = vec3.fromValues( P[0] - A[0], P[1] - A[1], P[2] - A[2]); - var AB = vec3.fromValues( B[0] - A[0], B[1] - A[1], B[2] - A[2]); - var div = vec3.dot(AP,AB) / vec3.dot(AB,AB); - result[0] = A[0] + div[0] * AB[0]; - result[1] = A[1] + div[1] * AB[1]; - result[2] = A[2] + div[2] * AB[2]; - return result; - }, - - /** - * Projects a 2D point on a 2D line - * @method project2DPointOnLine - * @param {vec2} P - * @param {vec2} A line start - * @param {vec2} B line end - * @param {vec2} result to store result (optional) - * @return {vec2} projectec point - */ - project2DPointOnLine: function( P, A, B, result ) - { - result = result || vec2.create(); - //A + dot(AP,AB) / dot(AB,AB) * AB - var AP = vec2.fromValues(P[0] - A[0], P[1] - A[1]); - var AB = vec2.fromValues(B[0] - A[0], B[1] - A[1]); - var div = vec2.dot(AP,AB) / vec2.dot(AB,AB); - result[0] = A[0] + div[0] * AB[0]; - result[1] = A[1] + div[1] * AB[1]; - return result; - }, - - /** - * Projects point on plane - * @method projectPointOnPlane - * @param {vec3} point - * @param {vec3} P plane point - * @param {vec3} N plane normal - * @param {vec3} result to store result (optional) - * @return {vec3} projectec point - */ - projectPointOnPlane: function(point, P, N, result) - { - result = result || vec3.create(); - var v = vec3.subtract( vec3.create(), point, P ); - var dist = vec3.dot(v,N); - return vec3.subtract( result, point , vec3.scale( vec3.create(), N, dist ) ); - }, - - /** - * Finds the reflected point over a plane (useful for reflecting camera position when rendering reflections) - * @method reflectPointInPlane - * @param {vec3} point point to reflect - * @param {vec3} P point where the plane passes - * @param {vec3} N normal of the plane - * @return {vec3} reflected point - */ - reflectPointInPlane: function(point, P, N) - { - var d = -1 * (P[0] * N[0] + P[1] * N[1] + P[2] * N[2]); - var t = -(d + N[0]*point[0] + N[1]*point[1] + N[2]*point[2]) / (N[0]*N[0] + N[1]*N[1] + N[2]*N[2]); - //trace("T:" + t); - //var closest = [ point[0]+t*N[0], point[1]+t*N[1], point[2]+t*N[2] ]; - //trace("Closest:" + closest); - return vec3.fromValues( point[0]+t*N[0]*2, point[1]+t*N[1]*2, point[2]+t*N[2]*2 ); - }, - - /** - * test a ray plane collision and retrieves the collision point - * @method testRayPlane - * @param {vec3} start ray start - * @param {vec3} direction ray direction - * @param {vec3} P point where the plane passes - * @param {vec3} N normal of the plane - * @param {vec3} result collision position - * @return {boolean} returns if the ray collides the plane or the ray is parallel to the plane - */ - testRayPlane: function(start, direction, P, N, result) - { - var D = vec3.dot( P, N ); - var numer = D - vec3.dot(N, start); - var denom = vec3.dot(N, direction); - if( Math.abs(denom) < EPSILON) return false; - var t = (numer / denom); - if(t < 0.0) return false; //behind the ray - if(result) - vec3.add( result, start, vec3.scale( result, direction, t) ); - - return true; - }, - - /** - * test collision between segment and plane and retrieves the collision point - * @method testSegmentPlane - * @param {vec3} start segment start - * @param {vec3} end segment end - * @param {vec3} P point where the plane passes - * @param {vec3} N normal of the plane - * @param {vec3} result collision position - * @return {boolean} returns if the segment collides the plane or it is parallel to the plane - */ - testSegmentPlane: (function() { - var temp = vec3.create(); - return function(start, end, P, N, result) - { - var D = vec3.dot( P, N ); - var numer = D - vec3.dot(N, start); - var direction = vec3.sub( temp, end, start ); - var denom = vec3.dot(N, direction); - if( Math.abs(denom) < EPSILON) - return false; //parallel - var t = (numer / denom); - if(t < 0.0) - return false; //behind the start - if(t > 1.0) - return false; //after the end - if(result) - vec3.add( result, start, vec3.scale( result, direction, t) ); - return true; - }; - })(), - - /** - * test a ray sphere collision and retrieves the collision point - * @method testRaySphere - * @param {vec3} start ray start - * @param {vec3} direction ray direction (normalized) - * @param {vec3} center center of the sphere - * @param {number} radius radius of the sphere - * @param {vec3} result [optional] collision position - * @param {number} max_dist not fully tested - * @return {boolean} returns if the ray collides the sphere - */ - testRaySphere: (function() { - var temp = vec3.create(); - return function(start, direction, center, radius, result, max_dist) - { - // sphere equation (centered at origin) x2+y2+z2=r2 - // ray equation x(t) = p0 + t*dir - // substitute x(t) into sphere equation - // solution below: - - // transform ray origin into sphere local coordinates - var orig = vec3.subtract( temp , start, center); - - var a = direction[0]*direction[0] + direction[1]*direction[1] + direction[2]*direction[2]; - var b = 2*orig[0]*direction[0] + 2*orig[1]*direction[1] + 2*orig[2]*direction[2]; - var c = orig[0]*orig[0] + orig[1]*orig[1] + orig[2]*orig[2] - radius*radius; - //return quadraticFormula(a,b,c,t0,t1) ? 2 : 0; - - var q = b*b - 4*a*c; - if( q < 0.0 ) - return false; - - if(result) - { - var sq = Math.sqrt(q); - var d = 1 / (2*a); - var r1 = ( -b + sq ) * d; - var r2 = ( -b - sq ) * d; - var t = r1 < r2 ? r1 : r2; - if(max_dist !== undefined && t > max_dist) - return false; - vec3.add(result, start, vec3.scale( result, direction, t ) ); - } - return true;//real roots - }; - })(), - - /** - * test a ray cylinder collision (only vertical cylinders) and retrieves the collision point [not fully tested] - * @method testRayCylinder - * @param {vec3} start ray start - * @param {vec3} direction ray direction - * @param {vec3} p center of the cylinder - * @param {number} q height of the cylinder - * @param {number} r radius of the cylinder - * @param {vec3} result collision position - * @return {boolean} returns if the ray collides the cylinder - */ - testRayCylinder: function(start, direction, p, q, r, result) - { - var sa = vec3.clone(start); - var sb = vec3.add(vec3.create(), start, vec3.scale( vec3.create(), direction, 100000) ); - var t = 0; - var d = vec3.subtract(vec3.create(),q,p); - var m = vec3.subtract(vec3.create(),sa,p); - var n = vec3.subtract(vec3.create(),sb,sa); - //var n = vec3.create(direction); - - var md = vec3.dot(m, d); - var nd = vec3.dot(n, d); - var dd = vec3.dot(d, d); - - // Test if segment fully outside either endcap of cylinder - if (md < 0.0 && md + nd < 0.0) return false; // Segment outside ’p’ side of cylinder - if (md > dd && md + nd > dd) return false; // Segment outside ’q’ side of cylinder - - var nn = vec3.dot(n, n); - var mn = vec3.dot(m, n); - var a = dd * nn - nd * nd; - var k = vec3.dot(m,m) - r*r; - var c = dd * k - md * md; - - if (Math.abs(a) < EPSILON) - { - // Segment runs parallel to cylinder axis - if (c > 0.0) return false; - // ’a’ and thus the segment lie outside cylinder - // Now known that segment intersects cylinder; figure out how it intersects - if (md < 0.0) t = -mn/nn; - // Intersect segment against ’p’ endcap - else if (md > dd) - t=(nd-mn)/nn; - // Intersect segment against ’q’ endcap - else t = 0.0; - // ’a’ lies inside cylinder - if(result) - vec3.add(result, sa, vec3.scale(result, n,t) ); - return true; - } - var b = dd * mn - nd * md; - var discr = b*b - a*c; - if (discr < 0.0) - return false; - // No real roots; no intersection - t = (-b - Math.sqrt(discr)) / a; - if (t < 0.0 || t > 1.0) - return false; - // Intersection lies outside segment - if(md+t*nd < 0.0) - { - // Intersection outside cylinder on ’p’ side - if (nd <= 0.0) - return false; - // Segment pointing away from endcap - t = -md / nd; - // Keep intersection if Dot(S(t) - p, S(t) - p) <= r^2 - if(result) - vec3.add(result, sa, vec3.scale(result, n,t) ); - return k+2*t*(mn+t*nn) <= 0.0; - } else if (md+t*nd>dd) - { - // Intersection outside cylinder on ’q’ side - if (nd >= 0.0) return false; //Segment pointing away from endcap - t = (dd - md) / nd; - // Keep intersection if Dot(S(t) - q, S(t) - q) <= r^2 - if(result) - vec3.add(result, sa, vec3.scale(result, n,t) ); - return k+dd - 2*md+t*(2*(mn - nd)+t*nn) <= 0.0; - } - // Segment intersects cylinder between the endcaps; t is correct - if(result) - vec3.add(result, sa, vec3.scale(result, n,t) ); - return true; - }, - - - /** - * test a ray bounding-box collision and retrieves the collision point, the BB must be Axis Aligned - * @method testRayBox - * @param {vec3} start ray start - * @param {vec3} direction ray direction - * @param {vec3} minB minimum position of the bounding box - * @param {vec3} maxB maximim position of the bounding box - * @param {vec3} result collision position - * @return {boolean} returns if the ray collides the box - */ - testRayBox: (function() { - - var quadrant = new Float32Array(3); - var candidatePlane = new Float32Array(3); - var maxT = new Float32Array(3); - - return function(start, direction, minB, maxB, result, max_dist) - { - //#define NUMDIM 3 - //#define RIGHT 0 - //#define LEFT 1 - //#define MIDDLE 2 - - max_dist = max_dist || Number.MAX_VALUE; - - var inside = true; - var i = 0|0; - var whichPlane; - - quadrant.fill(0); - maxT.fill(0); - candidatePlane.fill(0); - - /* Find candidate planes; this loop can be avoided if - rays cast all from the eye(assume perpsective view) */ - for (i=0; i < 3; ++i) - if(start[i] < minB[i]) { - quadrant[i] = 1; - candidatePlane[i] = minB[i]; - inside = false; - }else if (start[i] > maxB[i]) { - quadrant[i] = 0; - candidatePlane[i] = maxB[i]; - inside = false; - }else { - quadrant[i] = 2; - } - - /* Ray origin inside bounding box */ - if(inside) { - if(result) - vec3.copy(result, start); - return true; - } - - - /* Calculate T distances to candidate planes */ - for (i = 0; i < 3; ++i) - if (quadrant[i] != 2 && direction[i] != 0.) - maxT[i] = (candidatePlane[i] - start[i]) / direction[i]; - else - maxT[i] = -1.; - - /* Get largest of the maxT's for final choice of intersection */ - whichPlane = 0; - for (i = 1; i < 3; i++) - if (maxT[whichPlane] < maxT[i]) - whichPlane = i; - - /* Check final candidate actually inside box */ - if (maxT[whichPlane] < 0.) return false; - if (maxT[whichPlane] > max_dist) return false; //NOT TESTED - - for (i = 0; i < 3; ++i) - if (whichPlane != i) { - var res = start[i] + maxT[whichPlane] * direction[i]; - if (res < minB[i] || res > maxB[i]) - return false; - if(result) - result[i] = res; - } else { - if(result) - result[i] = candidatePlane[i]; - } - return true; /* ray hits box */ - } - })(), - - /** - * test a ray bounding-box collision, it uses the BBox class and allows to use non-axis aligned bbox - * @method testRayBBox - * @param {vec3} origin ray origin - * @param {vec3} direction ray direction - * @param {BBox} box in BBox format - * @param {mat4} model transformation of the BBox [optional] - * @param {vec3} result collision position in world space unless in_local is true - * @return {boolean} returns if the ray collides the box - */ - testRayBBox: (function(){ - var inv = mat4.create(); - var end = vec3.create(); - var origin2 = vec3.create(); - return function( origin, direction, box, model, result, max_dist, in_local ) - { - if(!origin || !direction || !box) - throw("parameters missing"); - if(model) - { - mat4.invert( inv, model ); - vec3.add( end, origin, direction ); - origin = vec3.transformMat4( origin2, origin, inv); - vec3.transformMat4( end, end, inv ); - vec3.sub( end, end, origin ); - direction = vec3.normalize( end, end ); - } - var r = this.testRayBox( origin, direction, box.subarray(6,9), box.subarray(9,12), result, max_dist ); - if(!in_local && model && result) - vec3.transformMat4(result, result, model); - return r; - } - })(), - - /** - * test if a 3d point is inside a BBox - * @method testPointBBox - * @param {vec3} point - * @param {BBox} bbox - * @return {boolean} true if it is inside - */ - testPointBBox: function(point, bbox) { - if(point[0] < bbox[6] || point[0] > bbox[9] || - point[1] < bbox[7] || point[0] > bbox[10] || - point[2] < bbox[8] || point[0] > bbox[11]) - return false; - return true; - }, - - /** - * test if a BBox overlaps another BBox - * @method testBBoxBBox - * @param {BBox} a - * @param {BBox} b - * @return {boolean} true if it overlaps - */ - testBBoxBBox: function(a, b) - { - var tx = Math.abs( b[0] - a[0]); - if (tx > (a[3] + b[3])) - return false; //outside - var ty = Math.abs(b[1] - a[1]); - if (ty > (a[4] + b[4])) - return false; //outside - var tz = Math.abs( b[2] - a[2]); - if (tz > (a[5] + b[5]) ) - return false; //outside - - var vmin = BBox.getMin(b); - if ( geo.testPointBBox(vmin, a) ) - { - var vmax = BBox.getMax(b); - if (geo.testPointBBox(vmax, a)) - { - return true;// INSIDE;// this instance contains b - } - } - - return true; //OVERLAP; // this instance overlaps with b - }, - - /** - * test if a sphere overlaps a BBox - * @method testSphereBBox - * @param {vec3} point - * @param {float} radius - * @param {BBox} bounding_box - * @return {boolean} true if it overlaps - */ - testSphereBBox: function(center, radius, bbox) - { - // arvo's algorithm from gamasutra - // http://www.gamasutra.com/features/19991018/Gomez_4.htm - - var s, d = 0.0; - //find the square of the distance - //from the sphere to the box - var vmin = BBox.getMin( bbox ); - var vmax = BBox.getMax( bbox ); - for(var i = 0; i < 3; ++i) - { - if( center[i] < vmin[i] ) - { - s = center[i] - vmin[i]; - d += s*s; - } - else if( center[i] > vmax[i] ) - { - s = center[i] - vmax[i]; - d += s*s; - } - } - //return d <= r*r - - var radiusSquared = radius * radius; - if (d <= radiusSquared) - { - return true; - /* - // this is used just to know if it overlaps or is just inside, but I dont care - // make an aabb aabb test with the sphere aabb to test inside state - var halfsize = vec3.fromValues( radius, radius, radius ); - var sphere_bbox = BBox.fromCenterHalfsize( center, halfsize ); - if ( geo.testBBoxBBox(bbox, sphere_bbox) ) - return INSIDE; - return OVERLAP; - */ - } - - return false; //OUTSIDE; - }, - - closestPointBetweenLines: function(a0,a1, b0,b1, p_a, p_b) - { - var u = vec3.subtract( vec3.create(), a1, a0 ); - var v = vec3.subtract( vec3.create(), b1, b0 ); - var w = vec3.subtract( vec3.create(), a0, b0 ); - - var a = vec3.dot(u,u); // always >= 0 - var b = vec3.dot(u,v); - var c = vec3.dot(v,v); // always >= 0 - var d = vec3.dot(u,w); - var e = vec3.dot(v,w); - var D = a*c - b*b; // always >= 0 - var sc, tc; - - // compute the line parameters of the two closest points - if (D < EPSILON) { // the lines are almost parallel - sc = 0.0; - tc = (b>c ? d/b : e/c); // use the largest denominator - } - else { - sc = (b*e - c*d) / D; - tc = (a*e - b*d) / D; - } - - // get the difference of the two closest points - if(p_a) vec3.add(p_a, a0, vec3.scale(vec3.create(),u,sc)); - if(p_b) vec3.add(p_b, b0, vec3.scale(vec3.create(),v,tc)); - - var dP = vec3.add( vec3.create(), w, vec3.subtract( vec3.create(), vec3.scale(vec3.create(),u,sc) , vec3.scale(vec3.create(),v,tc)) ); // = L1(sc) - L2(tc) - return vec3.length(dP); // return the closest distance - }, - - /** - * extract frustum planes given a view-projection matrix - * @method extractPlanes - * @param {mat4} viewprojection matrix - * @return {Float32Array} returns all 6 planes in a float32array[24] - */ - extractPlanes: function(vp, planes) - { - var planes = planes || new Float32Array(4*6); - - //right - planes.set( [vp[3] - vp[0], vp[7] - vp[4], vp[11] - vp[8], vp[15] - vp[12] ], 0); - normalize(0); - - //left - planes.set( [vp[3] + vp[0], vp[ 7] + vp[ 4], vp[11] + vp[ 8], vp[15] + vp[12] ], 4); - normalize(4); - - //bottom - planes.set( [ vp[ 3] + vp[ 1], vp[ 7] + vp[ 5], vp[11] + vp[ 9], vp[15] + vp[13] ], 8); - normalize(8); - - //top - planes.set( [ vp[ 3] - vp[ 1], vp[ 7] - vp[ 5], vp[11] - vp[ 9], vp[15] - vp[13] ],12); - normalize(12); - - //back - planes.set( [ vp[ 3] - vp[ 2], vp[ 7] - vp[ 6], vp[11] - vp[10], vp[15] - vp[14] ],16); - normalize(16); - - //front - planes.set( [ vp[ 3] + vp[ 2], vp[ 7] + vp[ 6], vp[11] + vp[10], vp[15] + vp[14] ],20); - normalize(20); - - return planes; - - function normalize(pos) - { - var N = planes.subarray(pos,pos+3); - var l = vec3.length(N); - if(l === 0) return; - l = 1.0 / l; - planes[pos] *= l; - planes[pos+1] *= l; - planes[pos+2] *= l; - planes[pos+3] *= l; - } - }, - - /** - * test a BBox against the frustum - * @method frustumTestBox - * @param {Float32Array} planes frustum planes - * @param {BBox} boundindbox in BBox format - * @return {enum} CLIP_INSIDE, CLIP_OVERLAP, CLIP_OUTSIDE - */ - frustumTestBox: function(planes, box) - { - var flag = 0, o = 0; - - flag = planeBoxOverlap(planes.subarray(0,4),box); - if (flag == CLIP_OUTSIDE) return CLIP_OUTSIDE; o+= flag; - flag = planeBoxOverlap(planes.subarray(4,8),box); - if (flag == CLIP_OUTSIDE) return CLIP_OUTSIDE; o+= flag; - flag = planeBoxOverlap(planes.subarray(8,12),box); - if (flag == CLIP_OUTSIDE) return CLIP_OUTSIDE; o+= flag; - flag = planeBoxOverlap(planes.subarray(12,16),box); - if (flag == CLIP_OUTSIDE) return CLIP_OUTSIDE; o+= flag; - flag = planeBoxOverlap(planes.subarray(16,20),box); - if (flag == CLIP_OUTSIDE) return CLIP_OUTSIDE; o+= flag; - flag = planeBoxOverlap(planes.subarray(20,24),box); - if (flag == CLIP_OUTSIDE) return CLIP_OUTSIDE; o+= flag; - - return o == 0 ? CLIP_INSIDE : CLIP_OVERLAP; - }, - - /** - * test a Sphere against the frustum - * @method frustumTestSphere - * @param {vec3} center sphere center - * @param {number} radius sphere radius - * @return {enum} CLIP_INSIDE, CLIP_OVERLAP, CLIP_OUTSIDE - */ - - frustumTestSphere: function(planes, center, radius) - { - var dist; - var overlap = false; - - dist = distanceToPlane( planes.subarray(0,4), center ); - if( dist < -radius ) return CLIP_OUTSIDE; - else if(dist >= -radius && dist <= radius) overlap = true; - dist = distanceToPlane( planes.subarray(4,8), center ); - if( dist < -radius ) return CLIP_OUTSIDE; - else if(dist >= -radius && dist <= radius) overlap = true; - dist = distanceToPlane( planes.subarray(8,12), center ); - if( dist < -radius ) return CLIP_OUTSIDE; - else if(dist >= -radius && dist <= radius) overlap = true; - dist = distanceToPlane( planes.subarray(12,16), center ); - if( dist < -radius ) return CLIP_OUTSIDE; - else if(dist >= -radius && dist <= radius) overlap = true; - dist = distanceToPlane( planes.subarray(16,20), center ); - if( dist < -radius ) return CLIP_OUTSIDE; - else if(dist >= -radius && dist <= radius) overlap = true; - dist = distanceToPlane( planes.subarray(20,24), center ); - if( dist < -radius ) return CLIP_OUTSIDE; - else if(dist >= -radius && dist <= radius) overlap = true; - return overlap ? CLIP_OVERLAP : CLIP_INSIDE; - }, - - - /** - * test if a 2d point is inside a 2d polygon - * @method testPoint2DInPolygon - * @param {Array} poly array of 2d points - * @param {vec2} point - * @return {boolean} true if it is inside - */ - testPoint2DInPolygon: function(poly, pt) { - for(var c = false, i = -1, l = poly.length, j = l - 1; ++i < l; j = i) - ((poly[i][1] <= pt[1] && pt[1] < poly[j][1]) || (poly[j][1] <= pt[1] && pt[1] < poly[i][1])) - && (pt[0] < (poly[j][0] - poly[i][0]) * (pt[1] - poly[i][1]) / (poly[j][1] - poly[i][1]) + poly[i][0]) - && (c = !c); - return c; - } -}; - -/** -* BBox is a class to create BoundingBoxes but it works as glMatrix, creating Float32Array with the info inside instead of objects -* The bounding box is stored as center,halfsize,min,max,radius (total of 13 floats) -* @class BBox -*/ -global.BBox = GL.BBox = { - center:0, - halfsize:3, - min:6, - max:9, - radius:12, - data_length: 13, - - //corners: new Float32Array([1,1,1, 1,1,-1, 1,-1,1, 1,-1,-1, -1,1,1, -1,1,-1, -1,-1,1, -1,-1,-1 ]), - corners: [ vec3.fromValues(1,1,1), vec3.fromValues(1,1,-1), vec3.fromValues(1,-1,1), vec3.fromValues(1,-1,-1), vec3.fromValues(-1,1,1), vec3.fromValues(-1,1,-1), vec3.fromValues(-1,-1,1), vec3.fromValues(-1,-1,-1) ] , - - /** - * create an empty bbox - * @method create - * @return {BBox} returns a float32array with the bbox - */ - create: function() - { - return new Float32Array(13); - }, - - /** - * create an bbox copy from another one - * @method clone - * @return {BBox} returns a float32array with the bbox - */ - clone: function(bb) - { - return new Float32Array(bb); - }, - - /** - * copy one bbox into another - * @method copy - * @param {BBox} out where to store the result - * @param {BBox} where to read the bbox - * @return {BBox} returns out - */ - copy: function(out,bb) - { - out.set(bb); - return out; - }, - - /** - * create a bbox from one point - * @method fromPoint - * @param {vec3} point - * @return {BBox} returns a float32array with the bbox - */ - fromPoint: function(point) - { - var bb = this.create(); - bb.set(point, 0); //center - bb.set(point, 6); //min - bb.set(point, 9); //max - return bb; - }, - - /** - * create a bbox from min and max points - * @method fromMinMax - * @param {vec3} min - * @param {vec3} max - * @return {BBox} returns a float32array with the bbox - */ - fromMinMax: function(min,max) - { - var bb = this.create(); - this.setMinMax(bb, min, max); - return bb; - }, - - /** - * create a bbox from center and halfsize - * @method fromCenterHalfsize - * @param {vec3} center - * @param {vec3} halfsize - * @return {BBox} returns a float32array with the bbox - */ - fromCenterHalfsize: function(center, halfsize) - { - var bb = this.create(); - this.setCenterHalfsize(bb, center, halfsize); - return bb; - }, - - /** - * create a bbox from a typed-array containing points - * @method fromPoints - * @param {Float32Array} points - * @return {BBox} returns a float32array with the bbox - */ - fromPoints: function(points) - { - var bb = this.create(); - this.setFromPoints(bb, points); - return bb; - }, - - /** - * set the values to a BB from a set of points - * @method setFromPoints - * @param {BBox} out where to store the result - * @param {Float32Array} points - * @return {BBox} returns a float32array with the bbox - */ - setFromPoints: function(bb, points) - { - var min = bb.subarray(6,9); - var max = bb.subarray(9,12); - - min[0] = points[0]; //min.set( points.subarray(0,3) ); - min[1] = points[1]; - min[2] = points[2]; - max.set( min ); - - var v = 0; - for(var i = 3, l = points.length; i < l; i+=3) - { - var x = points[i]; - var y = points[i+1]; - var z = points[i+2]; - if( x < min[0] ) min[0] = x; - else if( x > max[0] ) max[0] = x; - if( y < min[1] ) min[1] = y; - else if( y > max[1] ) max[1] = y; - if( z < min[2] ) min[2] = z; - else if( z > max[2] ) max[2] = z; - /* - v = points.subarray(i,i+3); - vec3.min( min, v, min); - vec3.max( max, v, max); - */ - } - - //center - bb[0] = (min[0] + max[0]) * 0.5; - bb[1] = (min[1] + max[1]) * 0.5; - bb[2] = (min[2] + max[2]) * 0.5; - //halfsize - bb[3] = max[0] - bb[0]; - bb[4] = max[1] - bb[1]; - bb[5] = max[2] - bb[2]; - bb[12] = Math.sqrt( bb[3]*bb[3] + bb[4]*bb[4] + bb[5]*bb[5] ); - - /* - var center = vec3.add( bb.subarray(0,3), min, max ); - vec3.scale( center, center, 0.5); - vec3.subtract( bb.subarray(3,6), max, center ); - bb[12] = vec3.length(bb.subarray(3,6)); //radius - */ - return bb; - }, - - /** - * set the values to a BB from min and max - * @method setMinMax - * @param {BBox} out where to store the result - * @param {vec3} min - * @param {vec3} max - * @return {BBox} returns out - */ - setMinMax: function(bb, min, max) - { - bb[6] = min[0]; - bb[7] = min[1]; - bb[8] = min[2]; - bb[9] = max[0]; - bb[10] = max[1]; - bb[11] = max[2]; - - //halfsize - var halfsize = bb.subarray(3,6); - vec3.sub( halfsize, max, min ); //range - vec3.scale( halfsize, halfsize, 0.5 ); - - //center - bb[0] = max[0] - halfsize[0]; - bb[1] = max[1] - halfsize[1]; - bb[2] = max[2] - halfsize[2]; - - bb[12] = vec3.length(bb.subarray(3,6)); //radius - return bb; - }, - - /** - * set the values to a BB from center and halfsize - * @method setCenterHalfsize - * @param {BBox} out where to store the result - * @param {vec3} min - * @param {vec3} max - * @param {number} radius [optional] (the minimum distance from the center to the further point) - * @return {BBox} returns out - */ - setCenterHalfsize: function(bb, center, halfsize, radius) - { - bb[0] = center[0]; - bb[1] = center[1]; - bb[2] = center[2]; - bb[3] = halfsize[0]; - bb[4] = halfsize[1]; - bb[5] = halfsize[2]; - bb[6] = bb[0] - bb[3]; - bb[7] = bb[1] - bb[4]; - bb[8] = bb[2] - bb[5]; - bb[9] = bb[0] + bb[3]; - bb[10] = bb[1] + bb[4]; - bb[11] = bb[2] + bb[5]; - if(radius) - bb[12] = radius; - else - bb[12] = vec3.length(halfsize); - return bb; - }, - - /** - * Apply a matrix transformation to the BBox (applies to every corner and recomputes the BB) - * @method transformMat4 - * @param {BBox} out where to store the result - * @param {BBox} bb bbox you want to transform - * @param {mat4} mat transformation - * @return {BBox} returns out - */ - transformMat4: (function(){ - var hsx = 0; - var hsy = 0; - var hsz = 0; - var points_buffer = new Float32Array(8*3); - var points = []; - for(var i = 0; i < 24; i += 3 ) - points.push( points_buffer.subarray( i, i+3 ) ); - - return function( out, bb, mat ) - { - var centerx = bb[0]; - var centery = bb[1]; - var centerz = bb[2]; - hsx = bb[3]; - hsy = bb[4]; - hsz = bb[5]; - - var corners = this.corners; - - for(var i = 0; i < 8; ++i) - { - var corner = corners[i]; - var result = points[i]; - result[0] = hsx * corner[0] + centerx; - result[1] = hsy * corner[1] + centery; - result[2] = hsz * corner[2] + centerz; - mat4.multiplyVec3( result, mat, result ); - } - - return this.setFromPoints( out, points_buffer ); - } - })(), - - - /** - * Computes the eight corners of the BBox and returns it - * @method getCorners - * @param {BBox} bb the bounding box - * @param {Float32Array} result optional, should be 8 * 3 - * @return {Float32Array} returns the 8 corners - */ - getCorners: function( bb, result ) - { - var center = bb; //.subarray(0,3); AVOID GC - var halfsize = bb.subarray(3,6); - - var corners = null; - if(result) - { - result.set(this.corners); - corners = result; - } - else - corners = new Float32Array( this.corners ); - - for(var i = 0; i < 8; ++i) - { - var corner = corners.subarray(i*3, i*3+3); - vec3.multiply( corner, halfsize, corner ); - vec3.add( corner, corner, center ); - } - - return corners; - }, - - merge: function( out, a, b ) - { - var min = out.subarray(6,9); - var max = out.subarray(9,12); - vec3.min( min, a.subarray(6,9), b.subarray(6,9) ); - vec3.max( max, a.subarray(9,12), b.subarray(9,12) ); - return BBox.setMinMax( out, min, max ); - }, - - extendToPoint: function( out, p ) - { - if( p[0] < out[6] ) out[6] = p[0]; - else if( p[0] > out[9] ) out[9] = p[0]; - - if( p[1] < out[7] ) out[7] = p[1]; - else if( p[1] > out[10] ) out[10] = p[1]; - - - if( p[2] < out[8] ) out[8] = p[2]; - else if( p[2] > out[11] ) out[11] = p[2]; - - //recompute - var min = out.subarray(6,9); - var max = out.subarray(9,12); - var center = vec3.add( out.subarray(0,3), min, max ); - vec3.scale( center, center, 0.5); - vec3.subtract( out.subarray(3,6), max, center ); - out[12] = vec3.length( out.subarray(3,6) ); //radius - return out; - }, - - clampPoint: function(out, box, point) - { - out[0] = Math.clamp( point[0], box[0] - box[3], box[0] + box[3]); - out[1] = Math.clamp( point[1], box[1] - box[4], box[1] + box[4]); - out[2] = Math.clamp( point[2], box[2] - box[5], box[2] + box[5]); - }, - - isPointInside: function( bbox, point ) - { - if( (bbox[0] - bbox[3]) > point[0] || - (bbox[1] - bbox[4]) > point[1] || - (bbox[2] - bbox[5]) > point[2] || - (bbox[0] + bbox[3]) < point[0] || - (bbox[1] + bbox[4]) < point[1] || - (bbox[2] + bbox[5]) < point[2] ) - return false; - return true; - }, - - getCenter: function(bb) { return bb.subarray(0,3); }, - getHalfsize: function(bb) { return bb.subarray(3,6); }, - getMin: function(bb) { return bb.subarray(6,9); }, - getMax: function(bb) { return bb.subarray(9,12); }, - getRadius: function(bb) { return bb[12]; } - //setCenter,setHalfsize not coded, too much work to update all -} - -global.distanceToPlane = GL.distanceToPlane = function distanceToPlane(plane, point) -{ - return vec3.dot(plane,point) + plane[3]; -} - -global.planeBoxOverlap = GL.planeBoxOverlap = function planeBoxOverlap(plane, box) -{ - var n = plane; //.subarray(0,3); - var d = plane[3]; - //hack, to avoif GC I use indices directly - var center = box; //.subarray(0,3); - var halfsize = box; //.subarray(3,6); - - var radius = Math.abs( halfsize[3] * n[0] ) + Math.abs( halfsize[4] * n[1] ) + Math.abs( halfsize[5] * n[2] ); - var distance = vec3.dot(n,center) + d; - - if (distance <= -radius) - return CLIP_OUTSIDE; - else if (distance <= radius) - return CLIP_OVERLAP; - return CLIP_INSIDE; -} - -/** -* @namespace GL -*/ - -/** -* Octree generator for fast ray triangle collision with meshes -* Dependencies: glmatrix.js (for vector and matrix operations) -* @class Octree -* @constructor -* @param {Mesh} mesh object containing vertices buffer (indices buffer optional) -*/ - -global.Octree = GL.Octree = function Octree( mesh ) -{ - this.root = null; - this.total_depth = 0; - this.total_nodes = 0; - if(mesh) - { - this.buildFromMesh(mesh); - this.total_nodes = this.trim(); - } -} - -Octree.MAX_NODE_TRIANGLES_RATIO = 0.1; -Octree.MAX_OCTREE_DEPTH = 8; -Octree.OCTREE_MARGIN_RATIO = 0.01; -Octree.OCTREE_MIN_MARGIN = 0.1; - -var octree_tested_boxes = 0; -var octree_tested_triangles = 0; - -Octree.prototype.buildFromMesh = function( mesh ) -{ - this.total_depth = 0; - this.total_nodes = 0; - - var vertices = mesh.getBuffer("vertices").data; - var triangles = mesh.getIndexBuffer("triangles"); - if(triangles) - triangles = triangles.data; //get the internal data - - var root = this.computeAABB(vertices); - this.root = root; - this.total_nodes = 1; - this.total_triangles = triangles ? triangles.length / 3 : vertices.length / 9; - this.max_node_triangles = this.total_triangles * Octree.MAX_NODE_TRIANGLES_RATIO; - - var margin = vec3.create(); - vec3.scale( margin, root.size, Octree.OCTREE_MARGIN_RATIO ); - if(margin[0] < Octree.OCTREE_MIN_MARGIN) margin[0] = Octree.OCTREE_MIN_MARGIN; - if(margin[1] < Octree.OCTREE_MIN_MARGIN) margin[1] = Octree.OCTREE_MIN_MARGIN; - if(margin[2] < Octree.OCTREE_MIN_MARGIN) margin[2] = Octree.OCTREE_MIN_MARGIN; - - vec3.sub(root.min, root.min, margin); - vec3.add(root.max, root.max, margin); - - root.faces = []; - root.inside = 0; - - - //indexed - if(triangles) - { - for(var i = 0; i < triangles.length; i+=3) - { - var face = new Float32Array([vertices[triangles[i]*3], vertices[triangles[i]*3+1],vertices[triangles[i]*3+2], - vertices[triangles[i+1]*3], vertices[triangles[i+1]*3+1],vertices[triangles[i+1]*3+2], - vertices[triangles[i+2]*3], vertices[triangles[i+2]*3+1],vertices[triangles[i+2]*3+2],i/3]); - this.addToNode( face,root,0); - } - } - else - { - for(var i = 0; i < vertices.length; i+=9) - { - var face = new Float32Array( 10 ); - face.set( vertices.subarray(i,i+9) ); - face[9] = i/9; - this.addToNode(face,root,0); - } - } - - return root; -} - -Octree.prototype.addToNode = function( face, node, depth ) -{ - node.inside += 1; - - //has children - if(node.c) - { - var aabb = this.computeAABB(face); - var added = false; - for(var i in node.c) - { - var child = node.c[i]; - if (Octree.isInsideAABB(aabb,child)) - { - this.addToNode(face,child, depth+1); - added = true; - break; - } - } - if(!added) - { - if(node.faces == null) - node.faces = []; - node.faces.push(face); - } - } - else //add till full, then split - { - if(node.faces == null) - node.faces = []; - node.faces.push(face); - - //split - if(node.faces.length > this.max_node_triangles && depth < Octree.MAX_OCTREE_DEPTH) - { - this.splitNode(node); - if(this.total_depth < depth + 1) - this.total_depth = depth + 1; - - var faces = node.faces.concat(); - node.faces = null; - - //redistribute all nodes - for(var i in faces) - { - var face = faces[i]; - var aabb = this.computeAABB(face); - var added = false; - for(var j in node.c) - { - var child = node.c[j]; - if (Octree.isInsideAABB(aabb,child)) - { - this.addToNode(face,child, depth+1); - added = true; - break; - } - } - if (!added) - { - if(node.faces == null) - node.faces = []; - node.faces.push(face); - } - } - } - } -}; - -Octree.prototype.octree_pos_ref = [[0,0,0],[0,0,1],[0,1,0],[0,1,1],[1,0,0],[1,0,1],[1,1,0],[1,1,1]]; - -Octree.prototype.splitNode = function(node) -{ - node.c = []; - var half = [(node.max[0] - node.min[0]) * 0.5, (node.max[1] - node.min[1]) * 0.5, (node.max[2] - node.min[2]) * 0.5]; - - for(var i in this.octree_pos_ref) - { - var ref = this.octree_pos_ref[i]; - - var newnode = {}; - this.total_nodes += 1; - - newnode.min = [ node.min[0] + half[0] * ref[0], node.min[1] + half[1] * ref[1], node.min[2] + half[2] * ref[2]]; - newnode.max = [newnode.min[0] + half[0], newnode.min[1] + half[1], newnode.min[2] + half[2]]; - newnode.faces = null; - newnode.inside = 0; - node.c.push(newnode); - } -} - -Octree.prototype.computeAABB = function(vertices) -{ - var min = new Float32Array([ vertices[0], vertices[1], vertices[2] ]); - var max = new Float32Array([ vertices[0], vertices[1], vertices[2] ]); - - for(var i = 0; i < vertices.length; i+=3) - { - for(var j = 0; j < 3; j++) - { - if(min[j] > vertices[i+j]) - min[j] = vertices[i+j]; - if(max[j] < vertices[i+j]) - max[j] = vertices[i+j]; - } - } - - return {min: min, max: max, size: vec3.sub( vec3.create(), max, min) }; -} - -//remove empty nodes -Octree.prototype.trim = function(node) -{ - node = node || this.root; - if(!node.c) - return 1; - - var num = 1; - var valid = []; - var c = node.c; - for(var i = 0; i < c.length; ++i) - { - if(c[i].inside) - { - valid.push(c[i]); - num += this.trim(c[i]); - } - } - node.c = valid; - return num; -} - -/** -* Test collision between ray and triangles in the octree -* @method testRay -* @param {vec3} origin ray origin position -* @param {vec3} direction ray direction position -* @param {number} dist_min -* @param {number} dist_max -* @return {HitTest} object containing pos and normal -*/ -Octree.prototype.testRay = (function(){ - var origin_temp = vec3.create(); - var direction_temp = vec3.create(); - var min_temp = vec3.create(); - var max_temp = vec3.create(); - - return function(origin, direction, dist_min, dist_max, test_backfaces ) - { - octree_tested_boxes = 0; - octree_tested_triangles = 0; - - if(!this.root) - { - throw("Error: octree not build"); - } - - origin_temp.set( origin ); - direction_temp.set( direction ); - min_temp.set( this.root.min ); - max_temp.set( this.root.max ); - - var test = Octree.hitTestBox( origin_temp, direction_temp, min_temp, max_temp ); - if(!test) //no collision with mesh bounding box - return null; - - var test = Octree.testRayInNode( this.root, origin_temp, direction_temp, test_backfaces ); - if(test != null) - { - var pos = vec3.scale( vec3.create(), direction, test.t ); - vec3.add( pos, pos, origin ); - test.pos = pos; - return test; - } - - return null; - } -})(); - -/** -* test collision between sphere and the triangles in the octree (only test if there is any vertex inside the sphere) -* @method testSphere -* @param {vec3} origin sphere center -* @param {number} radius -* @return {Boolean} true if the sphere collided with the mesh -*/ -Octree.prototype.testSphere = function( origin, radius ) -{ - origin = vec3.clone(origin); - octree_tested_boxes = 0; - octree_tested_triangles = 0; - - if(!this.root) - throw("Error: octree not build"); - - //better to use always the radius squared, because all the calculations are going to do that - var rr = radius * radius; - - if( !Octree.testSphereBox( origin, rr, vec3.clone(this.root.min), vec3.clone(this.root.max) ) ) - return false; //out of the box - - return Octree.testSphereInNode( this.root, origin, rr ); -} - -//WARNING: cannot use static here, it uses recursion -Octree.testRayInNode = function( node, origin, direction, test_backfaces ) -{ - var test = null; - var prev_test = null; - octree_tested_boxes += 1; - - //test faces - if(node.faces) - for(var i = 0, l = node.faces.length; i < l; ++i) - { - var face = node.faces[i]; - octree_tested_triangles += 1; - test = Octree.hitTestTriangle( origin, direction, face.subarray(0,3) , face.subarray(3,6), face.subarray(6,9), test_backfaces ); - if (test==null) - continue; - test.face = face; - if(prev_test) - prev_test.mergeWith( test ); - else - prev_test = test; - } - - //WARNING: cannot use statics here, this function uses recursion - var child_min = vec3.create(); - var child_max = vec3.create(); - - //test children nodes faces - var child; - if(node.c) - for(var i = 0; i < node.c.length; ++i) - { - child = node.c[i]; - child_min.set( child.min ); - child_max.set( child.max ); - - //test with node box - test = Octree.hitTestBox( origin, direction, child_min, child_max ); - if( test == null ) - continue; - - //nodebox behind current collision, then ignore node - if(prev_test && test.t > prev_test.t) - continue; - - //test collision with node - test = Octree.testRayInNode( child, origin, direction, test_backfaces ); - if(test == null) - continue; - - if(prev_test) - prev_test.mergeWith( test ); - else - prev_test = test; - } - - return prev_test; -} - -//WARNING: cannot use static here, it uses recursion -Octree.testSphereInNode = function( node, origin, radius2 ) -{ - var test = null; - var prev_test = null; - octree_tested_boxes += 1; - - //test faces - if(node.faces) - for(var i = 0, l = node.faces.length; i < l; ++i) - { - var face = node.faces[i]; - octree_tested_triangles += 1; - if( Octree.testSphereTriangle( origin, radius2, face.subarray(0,3) , face.subarray(3,6), face.subarray(6,9) ) ) - return true; - } - - //WARNING: cannot use statics here, this function uses recursion - var child_min = vec3.create(); - var child_max = vec3.create(); - - //test children nodes faces - var child; - if(node.c) - for(var i = 0; i < node.c.length; ++i) - { - child = node.c[i]; - child_min.set( child.min ); - child_max.set( child.max ); - - //test with node box - if( !Octree.testSphereBox( origin, radius2, child_min, child_max ) ) - continue; - - //test collision with node content - if( Octree.testSphereInNode( child, origin, radius2 ) ) - return true; - } - - return false; -} - -//test if one bounding is inside or overlapping another bounding -Octree.isInsideAABB = function(a,b) -{ - if(a.min[0] < b.min[0] || a.min[1] < b.min[1] || a.min[2] < b.min[2] || - a.max[0] > b.max[0] || a.max[1] > b.max[1] || a.max[2] > b.max[2]) - return false; - return true; -} - - -Octree.hitTestBox = (function(){ - var tMin = vec3.create(); - var tMax = vec3.create(); - var inv = vec3.create(); - var t1 = vec3.create(); - var t2 = vec3.create(); - var tmp = vec3.create(); - var epsilon = 1.0e-6; - var eps = vec3.fromValues( epsilon,epsilon,epsilon ); - - return function( origin, ray, box_min, box_max ) { - vec3.subtract( tMin, box_min, origin ); - vec3.subtract( tMax, box_max, origin ); - - if( vec3.maxValue(tMin) < 0 && vec3.minValue(tMax) > 0) - return new HitTest(0,origin,ray); - - inv[0] = 1/ray[0]; inv[1] = 1/ray[1]; inv[2] = 1/ray[2]; - vec3.multiply(tMin, tMin, inv); - vec3.multiply(tMax, tMax, inv); - vec3.min(t1, tMin, tMax); - vec3.max(t2, tMin, tMax); - var tNear = vec3.maxValue(t1); - var tFar = vec3.minValue(t2); - - if (tNear > 0 && tNear < tFar) { - var hit = vec3.add( vec3.create(), vec3.scale(tmp, ray, tNear ), origin); - vec3.add( box_min, box_min, eps); - vec3.subtract(box_min, box_min, eps); - return new HitTest(tNear, hit, vec3.fromValues( - (hit[0] > box_max[0]) - (hit[0] < box_min[0]), - (hit[1] > box_max[1]) - (hit[1] < box_min[1]), - (hit[2] > box_max[2]) - (hit[2] < box_min[2]) )); - } - - return null; - } -})(); - -Octree.hitTestTriangle = (function(){ - - var AB = vec3.create(); - var AC = vec3.create(); - var toHit = vec3.create(); - var tmp = vec3.create(); - - return function( origin, ray, A, B, C, test_backfaces ) { - vec3.subtract( AB, B, A ); - vec3.subtract( AC, C, A ); - var normal = vec3.cross( vec3.create(), AB, AC ); //returned - vec3.normalize( normal, normal ); - if( !test_backfaces && vec3.dot(normal,ray) > 0) - return null; //ignore backface - - var t = vec3.dot(normal, vec3.subtract( tmp, A, origin )) / vec3.dot(normal,ray); - - if (t > 0) - { - var hit = vec3.scale(vec3.create(), ray, t); //returned - vec3.add(hit, hit, origin); - vec3.subtract( toHit, hit, A ); - var dot00 = vec3.dot(AC,AC); - var dot01 = vec3.dot(AC,AB); - var dot02 = vec3.dot(AC,toHit); - var dot11 = vec3.dot(AB,AB); - var dot12 = vec3.dot(AB,toHit); - var divide = dot00 * dot11 - dot01 * dot01; - var u = (dot11 * dot02 - dot01 * dot12) / divide; - var v = (dot00 * dot12 - dot01 * dot02) / divide; - if (u >= 0 && v >= 0 && u + v <= 1) - return new HitTest(t, hit, normal); - } - return null; - }; -})(); - -//from http://realtimecollisiondetection.net/blog/?p=103 -//radius must be squared -Octree.testSphereTriangle = (function(){ - - var A = vec3.create(); - var B = vec3.create(); - var C = vec3.create(); - var AB = vec3.create(); - var AC = vec3.create(); - var BC = vec3.create(); - var CA = vec3.create(); - var V = vec3.create(); - - return function( P, rr, A_, B_, C_ ) { - vec3.sub( A, A_, P ); - vec3.sub( B, B_, P ); - vec3.sub( C, C_, P ); - - vec3.sub( AB, B, A ); - vec3.sub( AC, C, A ); - - vec3.cross( V, AB, AC ); - var d = vec3.dot( A, V ); - var e = vec3.dot( V, V ); - var sep1 = d * d > rr * e; - var aa = vec3.dot(A, A); - var ab = vec3.dot(A, B); - var ac = vec3.dot(A, C); - var bb = vec3.dot(B, B); - var bc = vec3.dot(B, C); - var cc = vec3.dot(C, C); - var sep2 = (aa > rr) & (ab > aa) & (ac > aa); - var sep3 = (bb > rr) & (ab > bb) & (bc > bb); - var sep4 = (cc > rr) & (ac > cc) & (bc > cc); - - var d1 = ab - aa; - var d2 = bc - bb; - var d3 = ac - cc; - - vec3.sub( BC, C, B ); - vec3.sub( CA, A, C ); - - var e1 = vec3.dot(AB, AB); - var e2 = vec3.dot(BC, BC); - var e3 = vec3.dot(CA, CA); - - var Q1 = vec3.scale(vec3.create(), A, e1); vec3.sub( Q1, Q1, vec3.scale(vec3.create(), AB, d1) ); - var Q2 = vec3.scale(vec3.create(), B, e2); vec3.sub( Q2, Q2, vec3.scale(vec3.create(), BC, d2) ); - var Q3 = vec3.scale(vec3.create(), C, e3); vec3.sub( Q3, Q3, vec3.scale(vec3.create(), CA, d3) ); - - var QC = vec3.scale( vec3.create(), C, e1 ); QC = vec3.sub( QC, QC, Q1 ); - var QA = vec3.scale( vec3.create(), A, e2 ); QA = vec3.sub( QA, QA, Q2 ); - var QB = vec3.scale( vec3.create(), B, e3 ); QB = vec3.sub( QB, QB, Q3 ); - - var sep5 = ( vec3.dot(Q1, Q1) > rr * e1 * e1) & (vec3.dot(Q1, QC) > 0 ); - var sep6 = ( vec3.dot(Q2, Q2) > rr * e2 * e2) & (vec3.dot(Q2, QA) > 0 ); - var sep7 = ( vec3.dot(Q3, Q3) > rr * e3 * e3) & (vec3.dot(Q3, QB) > 0 ); - - var separated = sep1 | sep2 | sep3 | sep4 | sep5 | sep6 | sep7 - return !separated; - }; -})(); - -Octree.testSphereBox = function( center, radius2, box_min, box_max ) { - - // arvo's algorithm from gamasutra - // http://www.gamasutra.com/features/19991018/Gomez_4.htm - var s, d = 0.0; - //find the square of the distance - //from the sphere to the box - for(var i = 0; i < 3; ++i) - { - if( center[i] < box_min[i] ) - { - s = center[i] - box_min[i]; - d += s*s; - } - else if( center[i] > box_max[i] ) - { - s = center[i] - box_max[i]; - d += s*s; - } - } - //return d <= r*r - - if (d <= radius2) - { - return true; - /* - // this is used just to know if it overlaps or is just inside, but I dont care - // make an aabb aabb test with the sphere aabb to test inside state - var halfsize = vec3.fromValues( radius, radius, radius ); - var sphere_bbox = BBox.fromCenterHalfsize( center, halfsize ); - if ( geo.testBBoxBBox(bbox, sphere_bbox) ) - return INSIDE; - return OVERLAP; - */ - } - - return false; //OUTSIDE; -}; -// Provides a convenient raytracing interface. - -// ### new GL.HitTest([t, hit, normal]) -// -// This is the object used to return hit test results. If there are no -// arguments, the constructed argument represents a hit infinitely far -// away. -global.HitTest = GL.HitTest = function HitTest(t, hit, normal) { - this.t = arguments.length ? t : Number.MAX_VALUE; - this.hit = hit; - this.normal = normal; - this.face = null; -} - -// ### .mergeWith(other) -// -// Changes this object to be the closer of the two hit test results. -HitTest.prototype = { - mergeWith: function(other) { - if (other.t > 0 && other.t < this.t) { - this.t = other.t; - this.hit = other.hit; - this.normal = other.normal; - this.face = other.face; - } - } -}; - -// ### new GL.Ray( origin, direction ) -global.Ray = GL.Ray = function Ray( origin, direction ) -{ - this.origin = vec3.create(); - this.direction = vec3.create(); - this.collision_point = vec3.create(); - - if(origin) - this.origin.set( origin ); - if(direction) - this.direction.set( direction ); -} - -Ray.prototype.testPlane = function( P, N ) -{ - return geo.testRayPlane( this.origin, this.direction, P, N, this.collision_point ); -} - -Ray.prototype.testSphere = function( center, radius, max_dist ) -{ - return geo.testRaySphere( this.origin, this.direction, center, radius, this.collision_point, max_dist ); -} - -// ### new GL.Raytracer() -// -// This will read the current modelview matrix, projection matrix, and viewport, -// reconstruct the eye position, and store enough information to later generate -// per-pixel rays using `getRayForPixel()`. -// -// Example usage: -// -// var tracer = new GL.Raytracer(); -// var ray = tracer.getRayForPixel( -// gl.canvas.width / 2, -// gl.canvas.height / 2); -// var result = GL.Raytracer.hitTestSphere( -// tracer.eye, ray, new GL.Vector(0, 0, 0), 1); - -global.Raytracer = GL.Raytracer = function Raytracer( viewprojection_matrix, viewport ) { - this.viewport = vec4.create(); - this.ray00 = vec3.create(); - this.ray10 = vec3.create(); - this.ray01 = vec3.create(); - this.ray11 = vec3.create(); - this.eye = vec3.create(); - this.setup( viewprojection_matrix, viewport ); -} - -Raytracer.prototype.setup = function( viewprojection_matrix, viewport ) -{ - viewport = viewport || gl.viewport_data; - this.viewport.set( viewport ); - - var minX = viewport[0], maxX = minX + viewport[2]; - var minY = viewport[1], maxY = minY + viewport[3]; - - vec3.set( this.ray00, minX, minY, 1 ); - vec3.set( this.ray10, maxX, minY, 1 ); - vec3.set( this.ray01, minX, maxY, 1 ); - vec3.set( this.ray11, maxX, maxY, 1 ); - vec3.unproject( this.ray00, this.ray00, viewprojection_matrix, viewport); - vec3.unproject( this.ray10, this.ray10, viewprojection_matrix, viewport); - vec3.unproject( this.ray01, this.ray01, viewprojection_matrix, viewport); - vec3.unproject( this.ray11, this.ray11, viewprojection_matrix, viewport); - var eye = this.eye; - vec3.unproject(eye, eye, viewprojection_matrix, viewport); - vec3.subtract(this.ray00, this.ray00, eye); - vec3.subtract(this.ray10, this.ray10, eye); - vec3.subtract(this.ray01, this.ray01, eye); - vec3.subtract(this.ray11, this.ray11, eye); -} - - // ### .getRayForPixel(x, y) - // - // Returns the ray direction originating from the camera and traveling through the pixel `x, y`. -Raytracer.prototype.getRayForPixel = (function(){ - var ray0 = vec3.create(); - var ray1 = vec3.create(); - return function(x, y, out) { - out = out || vec3.create(); - x = (x - this.viewport[0]) / this.viewport[2]; - y = 1 - (y - this.viewport[1]) / this.viewport[3]; - vec3.lerp(ray0, this.ray00, this.ray10, x); - vec3.lerp(ray1, this.ray01, this.ray11, x); - vec3.lerp( out, ray0, ray1, y) - return vec3.normalize( out, out ); - } -})(); - -// ### GL.Raytracer.hitTestBox(origin, ray, min, max) -// -// Traces the ray starting from `origin` along `ray` against the axis-aligned box -// whose coordinates extend from `min` to `max`. Returns a `HitTest` with the -// information or `null` for no intersection. -// -// This implementation uses the [slab intersection method](http://www.siggraph.org/education/materials/HyperGraph/raytrace/rtinter3.htm). -var _hittest_inv = mat4.create(); -Raytracer.hitTestBox = function(origin, ray, min, max, model) { - var _hittest_v3 = new Float32Array(10*3); //reuse memory to speedup - - if(model) - { - var inv = mat4.invert( _hittest_inv, model ); - origin = mat4.multiplyVec3( _hittest_v3.subarray(3,6), inv, origin ); - ray = mat4.rotateVec3( _hittest_v3.subarray(6,9), inv, ray ); - } - - var tMin = vec3.subtract( _hittest_v3.subarray(9,12), min, origin ); - vec3.divide( tMin, tMin, ray ); - - var tMax = vec3.subtract( _hittest_v3.subarray(12,15), max, origin ); - vec3.divide( tMax, tMax, ray ); - - var t1 = vec3.min( _hittest_v3.subarray(15,18), tMin, tMax); - var t2 = vec3.max( _hittest_v3.subarray(18,21), tMin, tMax); - - var tNear = vec3.maxValue(t1); - var tFar = vec3.minValue(t2); - - if (tNear > 0 && tNear <= tFar) { - var epsilon = 1.0e-6; - var hit = vec3.scale( _hittest_v3.subarray(21,24), ray, tNear); - vec3.add( hit, origin, hit ); - - vec3.addValue(_hittest_v3.subarray(24,27), min, epsilon); - vec3.subValue(_hittest_v3.subarray(27,30), max, epsilon); - - return new HitTest(tNear, hit, vec3.fromValues( - (hit[0] > max[0]) - (hit[0] < min[0]), - (hit[1] > max[1]) - (hit[1] < min[1]), - (hit[2] > max[2]) - (hit[2] < min[2]) - )); - } - - return null; -}; - - - - -// ### GL.Raytracer.hitTestSphere(origin, ray, center, radius) -// -// Traces the ray starting from `origin` along `ray` against the sphere defined -// by `center` and `radius`. Returns a `HitTest` with the information or `null` -// for no intersection. -Raytracer.hitTestSphere = function(origin, ray, center, radius) { - var offset = vec3.subtract( vec3.create(), origin,center); - var a = vec3.dot(ray,ray); - var b = 2 * vec3.dot(ray,offset); - var c = vec3.dot(offset,offset) - radius * radius; - var discriminant = b * b - 4 * a * c; - - if (discriminant > 0) { - var t = (-b - Math.sqrt(discriminant)) / (2 * a), hit = vec3.add(vec3.create(),origin, vec3.scale(vec3.create(), ray, t)); - return new HitTest(t, hit, vec3.scale( vec3.create(), vec3.subtract(vec3.create(), hit,center), 1.0/radius)); - } - - return null; -}; - - -// ### GL.Raytracer.hitTestTriangle(origin, ray, a, b, c) -// -// Traces the ray starting from `origin` along `ray` against the triangle defined -// by the points `a`, `b`, and `c`. Returns a `HitTest` with the information or -// `null` for no intersection. -Raytracer.hitTestTriangle = function(origin, ray, a, b, c) { - var ab = vec3.subtract(vec3.create(), b,a ); - var ac = vec3.subtract(vec3.create(), c,a ); - var normal = vec3.cross( vec3.create(), ab,ac); - vec3.normalize( normal, normal ); - var t = vec3.dot(normal, vec3.subtract( vec3.create(), a,origin)) / vec3.dot(normal,ray); - - if (t > 0) { - var hit = vec3.add( vec3.create(), origin, vec3.scale(vec3.create(), ray,t)); - var toHit = vec3.subtract( vec3.create(), hit, a); - var dot00 = vec3.dot(ac,ac); - var dot01 = vec3.dot(ac,ab); - var dot02 = vec3.dot(ac,toHit); - var dot11 = vec3.dot(ab,ab); - var dot12 = vec3.dot(ab,toHit); - var divide = dot00 * dot11 - dot01 * dot01; - var u = (dot11 * dot02 - dot01 * dot12) / divide; - var v = (dot00 * dot12 - dot01 * dot02) / divide; - if (u >= 0 && v >= 0 && u + v <= 1) return new HitTest(t, hit, normal); - } - - return null; -}; -//***** OBJ parser adapted from SpiderGL implementation ***************** -/** -* Parses a OBJ string and returns an object with the info ready to be passed to GL.Mesh.load -* @method Mesh.parseOBJ -* @param {String} data all the OBJ info to be parsed -* @param {Object} options -* @return {Object} mesh information (vertices, coords, normals, indices) -*/ - -Mesh.parseOBJ = function( text, options ) -{ - options = options || {}; - - //final arrays (packed, lineal [ax,ay,az, bx,by,bz ...]) - var positionsArray = [ ]; - var texcoordsArray = [ ]; - var normalsArray = [ ]; - var indicesArray = [ ]; - - //unique arrays (not packed, lineal) - var positions = [ ]; - var texcoords = [ ]; - var normals = [ ]; - var facemap = { }; - var index = 0; - - var line = null; - var f = null; - var pos = 0; - var tex = 0; - var nor = 0; - var x = 0.0; - var y = 0.0; - var z = 0.0; - var tokens = null; - - var hasPos = false; - var hasTex = false; - var hasNor = false; - - var parsingFaces = false; - var indices_offset = 0; - var negative_offset = -1; //used for weird objs with negative indices - var max_index = 0; - - var skip_indices = options.noindex ? options.noindex : (text.length > 10000000 ? true : false); - //trace("SKIP INDICES: " + skip_indices); - var flip_axis = options.flipAxis; - var flip_normals = (flip_axis || options.flipNormals); - - //used for mesh groups (submeshes) - var group = null; - var groups = []; - var materials_found = {}; - - var V_CODE = 1; - var VT_CODE = 2; - var VN_CODE = 3; - var F_CODE = 4; - var G_CODE = 5; - var O_CODE = 6; - var codes = { v: V_CODE, vt: VT_CODE, vn: VN_CODE, f: F_CODE, g: G_CODE, o: O_CODE }; - - - var lines = text.split("\n"); - var length = lines.length; - for (var lineIndex = 0; lineIndex < length; ++lineIndex) { - line = lines[lineIndex].replace(/[ \t]+/g, " ").replace(/\s\s*$/, ""); //trim - - if (line[0] == "#") continue; - if(line == "") continue; - - tokens = line.split(" "); - var code = codes[ tokens[0] ]; - - if(parsingFaces && code == V_CODE) //another mesh? - { - indices_offset = index; - parsingFaces = false; - //trace("multiple meshes: " + indices_offset); - } - - //read and parse numbers - if( code <= VN_CODE ) //v,vt,vn - { - x = parseFloat(tokens[1]); - y = parseFloat(tokens[2]); - if( code != VT_CODE ) - { - if(tokens[3] == '\\') //super weird case, OBJ allows to break lines with slashes... - { - //HACK! only works if the var is the thirth position... - ++lineIndex; - line = lines[lineIndex].replace(/[ \t]+/g, " ").replace(/\s\s*$/, ""); //better than trim - z = parseFloat(line); - } - else - z = parseFloat(tokens[3]); - } - } - - if (code == V_CODE) { - if(flip_axis) //maya and max notation style - positions.push(-1*x,z,y); - else - positions.push(x,y,z); - } - else if (code == VT_CODE) { - texcoords.push(x,y); - } - else if (code == VN_CODE) { - - if(flip_normals) //maya and max notation style - normals.push(-y,-z,x); - else - normals.push(x,y,z); - } - else if (code == F_CODE) { - parsingFaces = true; - - if (tokens.length < 4) continue; //faces with less that 3 vertices? nevermind - - //for every corner of this polygon - var polygon_indices = []; - for (var i=1; i < tokens.length; ++i) - { - if (!(tokens[i] in facemap) || skip_indices) - { - f = tokens[i].split("/"); - - if (f.length == 1) { //unpacked - pos = parseInt(f[0]) - 1; - tex = pos; - nor = pos; - } - else if (f.length == 2) { //no normals - pos = parseInt(f[0]) - 1; - tex = parseInt(f[1]) - 1; - nor = -1; - } - else if (f.length == 3) { //all three indexed - pos = parseInt(f[0]) - 1; - tex = parseInt(f[1]) - 1; - nor = parseInt(f[2]) - 1; - } - else { - console.err("Problem parsing: unknown number of values per face"); - return false; - } - - if(i > 3 && skip_indices) //break polygon in triangles - { - //first - var pl = positionsArray.length; - positionsArray.push( positionsArray[pl - (i-3)*9], positionsArray[pl - (i-3)*9 + 1], positionsArray[pl - (i-3)*9 + 2]); - positionsArray.push( positionsArray[pl - 3], positionsArray[pl - 2], positionsArray[pl - 1]); - pl = texcoordsArray.length; - texcoordsArray.push( texcoordsArray[pl - (i-3)*6], texcoordsArray[pl - (i-3)*6 + 1]); - texcoordsArray.push( texcoordsArray[pl - 2], texcoordsArray[pl - 1]); - pl = normalsArray.length; - normalsArray.push( normalsArray[pl - (i-3)*9], normalsArray[pl - (i-3)*9 + 1], normalsArray[pl - (i-3)*9 + 2]); - normalsArray.push( normalsArray[pl - 3], normalsArray[pl - 2], normalsArray[pl - 1]); - } - - //add new vertex - x = 0.0; - y = 0.0; - z = 0.0; - if ((pos * 3 + 2) < positions.length) { - hasPos = true; - x = positions[pos*3+0]; - y = positions[pos*3+1]; - z = positions[pos*3+2]; - } - positionsArray.push(x,y,z); - - //add new texture coordinate - x = 0.0; - y = 0.0; - if ((tex * 2 + 1) < texcoords.length) { - hasTex = true; - x = texcoords[tex*2+0]; - y = texcoords[tex*2+1]; - } - texcoordsArray.push(x,y); - - //add new normal - x = 0.0; - y = 0.0; - z = 1.0; - if(nor != -1) - { - if ((nor * 3 + 2) < normals.length) { - hasNor = true; - x = normals[nor*3+0]; - y = normals[nor*3+1]; - z = normals[nor*3+2]; - } - - normalsArray.push(x,y,z); - } - - //Save the string "10/10/10" and tells which index represents it in the arrays - if(!skip_indices) - facemap[tokens[i]] = index++; - }//end of 'if this token is new (store and index for later reuse)' - - //store key for this triplet - if(!skip_indices) - { - var final_index = facemap[tokens[i]]; - polygon_indices.push(final_index); - if(max_index < final_index) - max_index = final_index; - } - } //end of for every token on a 'f' line - - //polygons (not just triangles) - if(!skip_indices) - { - for(var iP = 2; iP < polygon_indices.length; iP++) - { - indicesArray.push( polygon_indices[0], polygon_indices[iP-1], polygon_indices[iP] ); - //indicesArray.push( [polygon_indices[0], polygon_indices[iP-1], polygon_indices[iP]] ); - } - } - } - else if (code == G_CODE) { //tokens[0] == "usemtl" - negative_offset = positions.length / 3 - 1; - - if(tokens.length > 1) - { - if(group != null) - { - group.length = indicesArray.length - group.start; - if(group.length > 0) - groups.push(group); - } - - group = { - name: tokens[1], - start: indicesArray.length, - length: -1, - material: "" - }; - } - } - else if (tokens[0] == "usemtl") { - if(group) - group.material = tokens[1]; - } - } - - if(!positions.length) - { - console.error("OBJ doesnt have vertices, maybe the file is not a OBJ"); - return null; - } - - if(group && (indicesArray.length - group.start) > 1) - { - group.length = indicesArray.length - group.start; - groups.push(group); - } - - //deindex streams - if((max_index > 256*256 || skip_indices ) && indicesArray.length > 0) - { - console.log("Deindexing mesh...") - var finalVertices = new Float32Array(indicesArray.length * 3); - var finalNormals = normalsArray && normalsArray.length ? new Float32Array(indicesArray.length * 3) : null; - var finalTexCoords = texcoordsArray && texcoordsArray.length ? new Float32Array(indicesArray.length * 2) : null; - for(var i = 0; i < indicesArray.length; i += 1) - { - finalVertices.set( positionsArray.slice( indicesArray[i]*3,indicesArray[i]*3 + 3), i*3 ); - if(finalNormals) - finalNormals.set( normalsArray.slice( indicesArray[i]*3,indicesArray[i]*3 + 3 ), i*3 ); - if(finalTexCoords) - finalTexCoords.set( texcoordsArray.slice(indicesArray[i]*2,indicesArray[i]*2 + 2 ), i*2 ); - } - positionsArray = finalVertices; - if(finalNormals) - normalsArray = finalNormals; - if(finalTexCoords) - texcoordsArray = finalTexCoords; - indicesArray = null; - } - - //Create final mesh object - var mesh = {}; - - //create typed arrays - if (hasPos) - mesh.vertices = new Float32Array(positionsArray); - if (hasNor && normalsArray.length > 0) - mesh.normals = new Float32Array(normalsArray); - if (hasTex && texcoordsArray.length > 0) - mesh.coords = new Float32Array(texcoordsArray); - if (indicesArray && indicesArray.length > 0) - mesh.triangles = new Uint16Array(indicesArray); - - var info = {}; - if(groups.length > 1) - info.groups = groups; - mesh.info = info; - - if(options.only_data) - return mesh; - - //creates and returns a GL.Mesh - var final_mesh = null; - final_mesh = Mesh.load( mesh, null, options.mesh ); - final_mesh.updateBoundingBox(); - return final_mesh; -} - -Mesh.parsers["obj"] = Mesh.parseOBJ; - -Mesh.encoders["obj"] = function( mesh, options ) -{ - //store vertices - var verticesBuffer = mesh.getBuffer("vertices"); - if(!verticesBuffer) - return null; - - var lines = []; - lines.push("# Generated with liteGL.js by Javi Agenjo\n"); - - var vertices = verticesBuffer.data; - for (var i = 0; i < vertices.length; i+=3) - lines.push("v " + vertices[i].toFixed(4) + " " + vertices[i+1].toFixed(4) + " " + vertices[i+2].toFixed(4)); - - //store normals - var normalsBuffer = mesh.getBuffer("normals"); - if(normalsBuffer) - { - lines.push(""); - var normals = normalsBuffer.data; - for (var i = 0; i < normals.length; i+=3) - lines.push("vn " + normals[i].toFixed(4) + " " + normals[i+1].toFixed(4) + " " + normals[i+2].toFixed(4) ); - } - - //store uvs - var coordsBuffer = mesh.getBuffer("coords"); - if(coordsBuffer) - { - lines.push(""); - var coords = coordsBuffer.data; - for (var i = 0; i < coords.length; i+=2) - lines.push("vt " + coords[i].toFixed(4) + " " + coords[i+1].toFixed(4) + " " + " 0.0000"); - } - - var groups = mesh.info.groups; - - - //store faces - var indicesBuffer = mesh.getIndexBuffer("triangles"); - if(indicesBuffer) - { - var indices = indicesBuffer.data; - if(!groups || !groups.length) - groups = [{start:0, length: indices.length, name:"mesh"}]; - for(var j = 0; j < groups.length; ++j) - { - var group = groups[j]; - lines.push("g " + group.name ); - lines.push("usemtl " + (group.material || ("mat_"+j))); - var start = group.start; - var end = start + group.length; - for (var i = start; i < end; i+=3) - lines.push("f " + (indices[i]+1) + "/" + (indices[i]+1) + "/" + (indices[i]+1) + " " + (indices[i+1]+1) + "/" + (indices[i+1]+1) + "/" + (indices[i+1]+1) + " " + (indices[i+2]+1) + "/" + (indices[i+2]+1) + "/" + (indices[i+2]+1) ); - } - } - else //no indices - { - if(!groups || !groups.length) - groups = [{start:0, length: (vertices.length / 3), name:"mesh"}]; - for(var j = 0; j < groups.length; ++j) - { - var group = groups[j]; - lines.push("g " + group.name); - lines.push("usemtl " + (group.material || ("mat_"+j))); - var start = group.start; - var end = start + group.length; - for (var i = start; i < end; i+=3) - lines.push( "f " + (i+1) + "/" + (i+1) + "/" + (i+1) + " " + (i+2) + "/" + (i+2) + "/" + (i+2) + " " + (i+3) + "/" + (i+3) + "/" + (i+3) ); - } - } - - return lines.join("\n"); -} - -//simple format to output meshes in ASCII -Mesh.parsers["mesh"] = function( text, options ) -{ - var mesh = {}; - - var lines = text.split("\n"); - for(var i = 0; i < lines.length; ++i) - { - var line = lines[i]; - var type = line[0]; - var t = line.substr(1).split(","); - var name = t[0]; - - if(type == "-") //buffer - { - var data = new Float32Array( Number(t[1]) ); - for(var j = 0; j < data.length; ++j) - data[j] = Number(t[j+2]); - mesh[name] = data; - } - else if(type == "*") //index - { - var data = Number(t[1]) > 256*256 ? new Uint32Array( Number(t[1]) ) : new Uint16Array( Number(t[1]) ); - for(var j = 0; j < data.length; ++j) - data[j] = Number(t[j+2]); - mesh[name] = data; - } - else if(type == "@") //info - { - if(name == "bones") - { - var bones = []; - var num_bones = Number(t[1]); - for(var j = 0; j < num_bones; ++j) - { - var m = (t.slice(3 + j*17, 3 + (j+1)*17 - 1)).map(Number); - bones.push( [ t[2 + j*17], m ] ); - } - mesh.bones = bones; - } - else if(name == "bind_matrix") - mesh.bind_matrix = t.slice(1,17).map(Number); - else if(name == "groups") - { - mesh.info = { groups: [] }; - var num_groups = Number(t[1]); - for(var j = 0; j < num_groups; ++j) - { - var group = { name: t[2+j*4], material: t[2+j*4+1], start: Number(t[2+j*4+2]), length: Number(t[2+j*4+3]) }; - mesh.info.groups.push(group); - } - } - } - else - console.warn("type unknown: " + t[0] ); - } - - if(options.only_data) - return mesh; - - //creates and returns a GL.Mesh - var final_mesh = null; - final_mesh = Mesh.load( mesh, null, options.mesh ); - final_mesh.updateBoundingBox(); - return final_mesh; -} - -Mesh.encoders["mesh"] = function( mesh, options ) -{ - var lines = []; - for(var i in mesh.vertexBuffers ) - { - var buffer = mesh.vertexBuffers[i]; - var line = ["-"+i, buffer.data.length, buffer.data, typedArrayToArray( buffer.data ) ]; - lines.push(line.join(",")); - } - - for(var i in mesh.indexBuffers ) - { - var buffer = mesh.indexBuffers[i]; - var line = [ "*" + i, buffer.data.length, buffer.data, typedArrayToArray( buffer.data ) ]; - lines.push(line.join(",")); - } - - if(mesh.bounding) - lines.push( ["@bounding", typedArrayToArray(mesh.bounding.subarray(0,6))].join(",") ); - if(mesh.info && mesh.info.groups) - { - var groups_info = []; - for(var j = 0; j < mesh.info.groups.length; ++j) - { - var group = mesh.info.groups[j]; - groups_info.push( group.name, group.material, group.start, group.length ); - } - lines.push( ["@groups", mesh.info.groups.length ].concat( groups_info ).join(",") ); - } - - if(mesh.bones) - lines.push( ["@bones", mesh.bones.length, mesh.bones.flat()].join(",") ); - if(mesh.bind_matrix) - lines.push( ["@bind_matrix", typedArrayToArray(mesh.bind_matrix) ].join(",") ); - - return lines.join("\n"); -} - -/* BINARY FORMAT ************************************/ - -if(global.WBin) - global.WBin.classes["Mesh"] = Mesh; - -Mesh.binary_file_formats["wbin"] = true; - -Mesh.parsers["wbin"] = Mesh.fromBinary = function( data_array, options ) -{ - if(!global.WBin) - throw("To use binary meshes you need to install WBin.js from https://github.com/jagenjo/litescene.js/blob/master/src/utils/wbin.js "); - - options = options || {}; - - var o = null; - if( data_array.constructor == ArrayBuffer ) - o = WBin.load( data_array, true ); - else - o = data_array; - - if(!o.info) - console.warn("This WBin doesn't seem to contain a mesh. Classname: ", o["@classname"] ); - - if( o.format ) - GL.Mesh.decompress( o ); - - var vertex_buffers = {}; - if(o.vertex_buffers) - { - for(var i in o.vertex_buffers) - vertex_buffers[ o.vertex_buffers[i] ] = o[ o.vertex_buffers[i] ]; - } - else - { - if(o.vertices) vertex_buffers.vertices = o.vertices; - if(o.normals) vertex_buffers.normals = o.normals; - if(o.coords) vertex_buffers.coords = o.coords; - if(o.weights) vertex_buffers.weights = o.weights; - if(o.bone_indices) vertex_buffers.bone_indices = o.bone_indices; - } - - var index_buffers = {}; - if( o.index_buffers ) - { - for(var i in o.index_buffers) - index_buffers[ o.index_buffers[i] ] = o[ o.index_buffers[i] ]; - } - else - { - if(o.triangles) index_buffers.triangles = o.triangles; - if(o.wireframe) index_buffers.wireframe = o.wireframe; - } - - var mesh = { - vertex_buffers: vertex_buffers, - index_buffers: index_buffers, - bounding: o.bounding, - info: o.info - }; - - if(o.bones) - { - mesh.bones = o.bones; - //restore Float32array - for(var i = 0; i < mesh.bones.length; ++i) - mesh.bones[i][1] = mat4.clone(mesh.bones[i][1]); - if(o.bind_matrix) - mesh.bind_matrix = mat4.clone( o.bind_matrix ); - } - - if(o.morph_targets) - mesh.morph_targets = o.morph_targets; - - if(options.only_data) - return mesh; - - //build mesh object - var final_mesh = options.mesh || new GL.Mesh(); - final_mesh.configure( mesh ); - return final_mesh; -} - -Mesh.encoders["wbin"] = function( mesh, options ) -{ - return mesh.toBinary( options ); -} - -Mesh.prototype.toBinary = function( options ) -{ - if(!global.WBin) - throw("to use Mesh.toBinary you need to have WBin included. Check the repository for wbin.js"); - - if(!this.info) - this.info = {}; - - //clean data - var o = { - object_class: "Mesh", - info: this.info, - groups: this.groups - }; - - if(this.bones) - { - var bones = []; - //convert to array - for(var i = 0; i < this.bones.length; ++i) - bones.push([ this.bones[i][0], mat4.toArray( this.bones[i][1] ) ]); - o.bones = bones; - if(this.bind_matrix) - o.bind_matrix = this.bind_matrix; - } - - //bounding box - if(!this.bounding) - this.updateBoundingBox(); - o.bounding = this.bounding; - - var vertex_buffers = []; - var index_buffers = []; - - for(var i in this.vertexBuffers) - { - var stream = this.vertexBuffers[i]; - o[ stream.name ] = stream.data; - vertex_buffers.push( stream.name ); - - if(stream.name == "vertices") - o.info.num_vertices = stream.data.length / 3; - } - - for(var i in this.indexBuffers) - { - var stream = this.indexBuffers[i]; - o[i] = stream.data; - index_buffers.push( i ); - } - - o.vertex_buffers = vertex_buffers; - o.index_buffers = index_buffers; - - //compress wbin using the bounding - if( GL.Mesh.enable_wbin_compression ) //apply compression - GL.Mesh.compress( o ); - - //create pack file - var bin = WBin.create( o, "Mesh" ); - return bin; -} - -Mesh.compress = function( o, format ) -{ - format = format || "bounding_compressed"; - o.format = { - type: format - }; - - var func = Mesh.compressors[ format ]; - if(!func) - throw("compression format not supported:" + format ); - return func( o ); -} - -Mesh.decompress = function( o ) -{ - if(!o.format) - return; - var func = Mesh.decompressors[ o.format.type ]; - if(!func) - throw("decompression format not supported:" + o.format.type ); - return func( o ); -} - -Mesh.compressors["bounding_compressed"] = function(o) -{ - if(!o.vertex_buffers) - throw("buffers not found"); - - var min = BBox.getMin( o.bounding ); - var max = BBox.getMax( o.bounding ); - var range = vec3.sub( vec3.create(), max, min ); - - var vertices = o.vertices; - var new_vertices = new Uint16Array( vertices.length ); - for(var i = 0; i < vertices.length; i+=3) - { - new_vertices[i] = ((vertices[i] - min[0]) / range[0]) * 65535; - new_vertices[i+1] = ((vertices[i+1] - min[1]) / range[1]) * 65535; - new_vertices[i+2] = ((vertices[i+2] - min[2]) / range[2]) * 65535; - } - o.vertices = new_vertices; - - if( o.normals ) - { - var normals = o.normals; - var new_normals = new Uint8Array( normals.length ); - var normals_range = new_normals.constructor == Uint8Array ? 255 : 65535; - for(var i = 0; i < normals.length; i+=3) - { - new_normals[i] = (normals[i] * 0.5 + 0.5) * normals_range; - new_normals[i+1] = (normals[i+1] * 0.5 + 0.5) * normals_range; - new_normals[i+2] = (normals[i+2] * 0.5 + 0.5) * normals_range; - } - o.normals = new_normals; - } - - if( o.coords ) - { - //compute uv bounding: [minu,minv,maxu,maxv] - var coords = o.coords; - var uvs_bounding = [10000,10000,-10000,-10000]; - for(var i = 0; i < coords.length; i+=2) - { - var u = coords[i]; - if( uvs_bounding[0] > u ) uvs_bounding[0] = u; - else if( uvs_bounding[2] < u ) uvs_bounding[2] = u; - var v = coords[i+1]; - if( uvs_bounding[1] > v ) uvs_bounding[1] = v; - else if( uvs_bounding[3] < v ) uvs_bounding[3] = v; - } - o.format.uvs_bounding = uvs_bounding; - - var new_coords = new Uint16Array( coords.length ); - var range = [ uvs_bounding[2] - uvs_bounding[0], uvs_bounding[3] - uvs_bounding[1] ]; - for(var i = 0; i < coords.length; i+=2) - { - new_coords[i] = ((coords[i] - uvs_bounding[0]) / range[0]) * 65535; - new_coords[i+1] = ((coords[i+1] - uvs_bounding[1]) / range[1]) * 65535; - } - o.coords = new_coords; - } - - if( o.weights ) - { - var weights = o.weights; - var new_weights = new Uint16Array( weights.length ); //using only one byte distorts the meshes a lot - var weights_range = new_weights.constructor == Uint8Array ? 255 : 65535; - for(var i = 0; i < weights.length; i+=4) - { - new_weights[i] = weights[i] * weights_range; - new_weights[i+1] = weights[i+1] * weights_range; - new_weights[i+2] = weights[i+2] * weights_range; - new_weights[i+3] = weights[i+3] * weights_range; - } - o.weights = new_weights; - } -} - - -Mesh.decompressors["bounding_compressed"] = function(o) -{ - var bounding = o.bounding; - if(!bounding) - throw("error in mesh decompressing data: bounding not found, cannot use the bounding decompression."); - - var min = BBox.getMin( bounding ); - var max = BBox.getMax( bounding ); - var range = vec3.sub( vec3.create(), max, min ); - - var format = o.format; - - var inv8 = 1 / 255; - var inv16 = 1 / 65535; - var vertices = o.vertices; - var new_vertices = new Float32Array( vertices.length ); - for( var i = 0, l = vertices.length; i < l; i += 3 ) - { - new_vertices[i] = ((vertices[i] * inv16) * range[0]) + min[0]; - new_vertices[i+1] = ((vertices[i+1] * inv16) * range[1]) + min[1]; - new_vertices[i+2] = ((vertices[i+2] * inv16) * range[2]) + min[2]; - } - o.vertices = new_vertices; - - if( o.normals && o.normals.constructor != Float32Array ) - { - var normals = o.normals; - var new_normals = new Float32Array( normals.length ); - var inormals_range = normals.constructor == Uint8Array ? inv8 : inv16; - for( var i = 0, l = normals.length; i < l; i += 3 ) - { - new_normals[i] = (normals[i] * inormals_range) * 2.0 - 1.0; - new_normals[i+1] = (normals[i+1] * inormals_range) * 2.0 - 1.0; - new_normals[i+2] = (normals[i+2] * inormals_range) * 2.0 - 1.0; - var N = new_normals.subarray(i,i+3); - vec3.normalize(N,N); - } - o.normals = new_normals; - } - - if( o.coords && format.uvs_bounding && o.coords.constructor != Float32Array ) - { - var coords = o.coords; - var uvs_bounding = format.uvs_bounding; - var range = [ uvs_bounding[2] - uvs_bounding[0], uvs_bounding[3] - uvs_bounding[1] ]; - var new_coords = new Float32Array( coords.length ); - for( var i = 0, l = coords.length; i < l; i += 2 ) - { - new_coords[i] = (coords[i] * inv16) * range[0] + uvs_bounding[0]; - new_coords[i+1] = (coords[i+1] * inv16) * range[1] + uvs_bounding[1]; - } - o.coords = new_coords; - } - - //bones are already in Uint8 format so dont need to compress them further, but weights yes - if( o.weights && o.weights.constructor != Float32Array ) //do we really need to unpack them? what if we use them like this? - { - var weights = o.weights; - var new_weights = new Float32Array( weights.length ); - var iweights_range = weights.constructor == Uint8Array ? inv8 : inv16; - for(var i = 0, l = weights.length; i < l; i += 4 ) - { - new_weights[i] = weights[i] * iweights_range; - new_weights[i+1] = weights[i+1] * iweights_range; - new_weights[i+2] = weights[i+2] * iweights_range; - new_weights[i+3] = weights[i+3] * iweights_range; - } - o.weights = new_weights; - } -} - -//footer.js -})( typeof(window) != "undefined" ? window : (typeof(self) != "undefined" ? self : global ) ); diff --git a/editor/js/libs/midi-parser.js b/editor/js/libs/midi-parser.js deleted file mode 100755 index 6d0d53de2..000000000 --- a/editor/js/libs/midi-parser.js +++ /dev/null @@ -1,356 +0,0 @@ -/* - 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 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; - } - - - -})(); \ No newline at end of file diff --git a/editor/style.css b/editor/style.css deleted file mode 100755 index feb73e77b..000000000 --- a/editor/style.css +++ /dev/null @@ -1,260 +0,0 @@ -html,body { - width: 100%; - height: 100%; - margin: 0; - padding: 0; -} - -body { - background-color: #333; - color: #EEE; - font: 14px Tahoma; -} - -h1 { - font-family: "Metro Light",Tahoma; -} - -h2 { - font-family: "Metro Light"; -} - -#main { - width: 100%; - height: 100%; - background-color: #222; -} - - -#status { - position: absolute; - top: 10px; - right: 10px; - color: #FAA; - font-size: 18px; - padding: 5px; - /*border-radius: 5px;*/ - width: -moz-calc( 50% - 30px); - min-height: 30px; - overflow: hidden; - background-color: #644; -} - -#help-message { - padding: 2px; - font-size: 0.8em; - background-color: #464; - /*border-radius: 2px;*/ -} - -#content { - position: relative; - min-height: 500px; - overflow: hidden; -} - -.fullscreen #content { - min-height: -moz-calc(100% - 80px); - min-height: -webkit-calc(100% - 80px); - min-height: calc(100% - 80px); -} - -.info-section p { - padding-left: 20px; - margin: 2px; -} - -.info-section strong { - color: #FEA; -} - -#visual { - position: absolute; - top: 0; - left: 0; - background-color: black; - width: 100%; - height: 100%; -} - - -.item-list .item { - margin: 5px; - padding: 5px; - font-size: 1.2em; - - background-color: transparent; - color: #999; - padding-left: 5px; - transition: background-color 300ms, color 300ms, padding-left 300ms; - -moz-transition: background-color 300ms, color 300ms, padding-left 300ms; - -webkit-transition: background-color 300ms, color 300ms, padding-left 300ms; -} - -.item-list .item:hover { - background-color: #33A; - /*border-radius: 4px;*/ - color: white; - padding-left: 15px; - transition: background-color 300ms, color 300ms, padding-left 300ms; - -moz-transition: background-color 300ms, color 300ms, padding-left 300ms; - -webkit-transition: background-color 300ms, color 300ms, padding-left 300ms; - cursor: pointer; -} - -#gallery .item-list .item:hover { - background-color: #A83; -} - -.item-list .item strong { - display: inline-block; - width: 200px; -} - -.form label { - font-size: 1.2em; - width: 200px; - display: inline-block; - text-align: right; -} - -label { - font-weight: bold; - color: #AAF; -} - -.header input { - color: #EEE; - background-color: #555; - font-size: 1.2em; - border: 1px solid black; - /*border-radius: 4px;*/ - padding: 2px; - /*box-shadow: inset 0 0 3px #333; */ - font-family: Verdana; - width: 250px; -} - -textarea { - vertical-align: top; -} - -#block-app { - width:100%; - height:100%; - position: absolute; - top: 0; - left: 0; - background-color: rgba(0,0,0,0.5); - text-align: center; - z-index: 6; -} - -#block-app span { - display: block; - font-size: 30px; - margin: auto; - margin-top: 300px; -} - -#block-app span a { - display: inline-block; - /*border-radius: 4px;*/ - text-decoration: none; - color: black; - background-color: red; - padding: 0 4px 0 4px; -} - -::-webkit-scrollbar { - height: 12px; - width: 6px; - background: #222; -} -::-webkit-scrollbar-thumb { - background: rgba(200,200,200,0.4); -} -::-webkit-scrollbar-corner { - background: #766; -} - -#editor { - position: relative; - width: 50%; - height: 100%; - display: inline-block; - margin: 0; - padding: 0; -} - -#editor .toolsbar { - width: 100%; - height: 30px; - background-color: #262626; - margin: 0; - padding: 0; -} - -#editor .toolsbar button { - padding: 2px; - padding-left: 10px; - padding-right: 10px; - margin: 3px 0 0 3px; -} - -#editor .toolsbar button.enabled { - background-color: #66A; -} - -#world { - position: absolute; - top: 0; - right: 0; - width: 50%; - height: 100%; -} - -#worldcanvas { - background-color: #343; -} - -.popup { - position: absolute; - top: 0; - background-color: rgba(50,50,90,0.8); - width: 100%; - height: 100%; -} - -.popup .header, .nodepanel .header { - width: 100%; - height: 30px; - font-size: 20px; - padding: 2px; -} - -#help { - color: #eee; -} - -#help p { - margin: 10px; -} - -.selector { - font-size: 1.8em; -} - - -.selector select { - color: white; - background-color: black; - border: 0; - font-size: 1em; -} - -.graphcanvas{ - /*touch-action: manipulation; WONT WORK*/ - /*touch-action: none; DESIDERABLE: implement zoom and pan*/ - touch-action: pinch-zoom; -} \ No newline at end of file diff --git a/external/Basica.otf b/external/Basica.otf deleted file mode 100755 index 661d179eb..000000000 Binary files a/external/Basica.otf and /dev/null differ diff --git a/external/Criticized.otf b/external/Criticized.otf deleted file mode 100755 index b010281d2..000000000 Binary files a/external/Criticized.otf and /dev/null differ diff --git a/external/DS-Digital.otf b/external/DS-Digital.otf deleted file mode 100755 index 80ab49010..000000000 Binary files a/external/DS-Digital.otf and /dev/null differ diff --git a/external/beat.otf b/external/beat.otf deleted file mode 100755 index 7b6d8b051..000000000 Binary files a/external/beat.otf and /dev/null differ diff --git a/external/jquery-1.6.2.min.js b/external/jquery-1.6.2.min.js deleted file mode 100755 index 48590ecb9..000000000 --- a/external/jquery-1.6.2.min.js +++ /dev/null @@ -1,18 +0,0 @@ -/*! - * jQuery JavaScript Library v1.6.2 - * http://jquery.com/ - * - * Copyright 2011, John Resig - * Dual licensed under the MIT or GPL Version 2 licenses. - * http://jquery.org/license - * - * Includes Sizzle.js - * http://sizzlejs.com/ - * Copyright 2011, The Dojo Foundation - * Released under the MIT, BSD, and GPL Licenses. - * - * Date: Thu Jun 30 14:16:56 2011 -0400 - */ -(function(a,b){function cv(a){return f.isWindow(a)?a:a.nodeType===9?a.defaultView||a.parentWindow:!1}function cs(a){if(!cg[a]){var b=c.body,d=f("<"+a+">").appendTo(b),e=d.css("display");d.remove();if(e==="none"||e===""){ch||(ch=c.createElement("iframe"),ch.frameBorder=ch.width=ch.height=0),b.appendChild(ch);if(!ci||!ch.createElement)ci=(ch.contentWindow||ch.contentDocument).document,ci.write((c.compatMode==="CSS1Compat"?"":"")+""),ci.close();d=ci.createElement(a),ci.body.appendChild(d),e=f.css(d,"display"),b.removeChild(ch)}cg[a]=e}return cg[a]}function cr(a,b){var c={};f.each(cm.concat.apply([],cm.slice(0,b)),function(){c[this]=a});return c}function cq(){cn=b}function cp(){setTimeout(cq,0);return cn=f.now()}function cf(){try{return new a.ActiveXObject("Microsoft.XMLHTTP")}catch(b){}}function ce(){try{return new a.XMLHttpRequest}catch(b){}}function b$(a,c){a.dataFilter&&(c=a.dataFilter(c,a.dataType));var d=a.dataTypes,e={},g,h,i=d.length,j,k=d[0],l,m,n,o,p;for(g=1;g0){c!=="border"&&f.each(e,function(){c||(d-=parseFloat(f.css(a,"padding"+this))||0),c==="margin"?d+=parseFloat(f.css(a,c+this))||0:d-=parseFloat(f.css(a,"border"+this+"Width"))||0});return d+"px"}d=bx(a,b,b);if(d<0||d==null)d=a.style[b]||0;d=parseFloat(d)||0,c&&f.each(e,function(){d+=parseFloat(f.css(a,"padding"+this))||0,c!=="padding"&&(d+=parseFloat(f.css(a,"border"+this+"Width"))||0),c==="margin"&&(d+=parseFloat(f.css(a,c+this))||0)});return d+"px"}function bm(a,b){b.src?f.ajax({url:b.src,async:!1,dataType:"script"}):f.globalEval((b.text||b.textContent||b.innerHTML||"").replace(be,"/*$0*/")),b.parentNode&&b.parentNode.removeChild(b)}function bl(a){f.nodeName(a,"input")?bk(a):"getElementsByTagName"in a&&f.grep(a.getElementsByTagName("input"),bk)}function bk(a){if(a.type==="checkbox"||a.type==="radio")a.defaultChecked=a.checked}function bj(a){return"getElementsByTagName"in a?a.getElementsByTagName("*"):"querySelectorAll"in a?a.querySelectorAll("*"):[]}function bi(a,b){var c;if(b.nodeType===1){b.clearAttributes&&b.clearAttributes(),b.mergeAttributes&&b.mergeAttributes(a),c=b.nodeName.toLowerCase();if(c==="object")b.outerHTML=a.outerHTML;else if(c!=="input"||a.type!=="checkbox"&&a.type!=="radio"){if(c==="option")b.selected=a.defaultSelected;else if(c==="input"||c==="textarea")b.defaultValue=a.defaultValue}else a.checked&&(b.defaultChecked=b.checked=a.checked),b.value!==a.value&&(b.value=a.value);b.removeAttribute(f.expando)}}function bh(a,b){if(b.nodeType===1&&!!f.hasData(a)){var c=f.expando,d=f.data(a),e=f.data(b,d);if(d=d[c]){var g=d.events;e=e[c]=f.extend({},d);if(g){delete e.handle,e.events={};for(var h in g)for(var i=0,j=g[h].length;i=0===c})}function V(a){return!a||!a.parentNode||a.parentNode.nodeType===11}function N(a,b){return(a&&a!=="*"?a+".":"")+b.replace(z,"`").replace(A,"&")}function M(a){var b,c,d,e,g,h,i,j,k,l,m,n,o,p=[],q=[],r=f._data(this,"events");if(!(a.liveFired===this||!r||!r.live||a.target.disabled||a.button&&a.type==="click")){a.namespace&&(n=new RegExp("(^|\\.)"+a.namespace.split(".").join("\\.(?:.*\\.)?")+"(\\.|$)")),a.liveFired=this;var s=r.live.slice(0);for(i=0;ic)break;a.currentTarget=e.elem,a.data=e.handleObj.data,a.handleObj=e.handleObj,o=e.handleObj.origHandler.apply(e.elem,arguments);if(o===!1||a.isPropagationStopped()){c=e.level,o===!1&&(b=!1);if(a.isImmediatePropagationStopped())break}}return b}}function K(a,c,d){var e=f.extend({},d[0]);e.type=a,e.originalEvent={},e.liveFired=b,f.event.handle.call(c,e),e.isDefaultPrevented()&&d[0].preventDefault()}function E(){return!0}function D(){return!1}function m(a,c,d){var e=c+"defer",g=c+"queue",h=c+"mark",i=f.data(a,e,b,!0);i&&(d==="queue"||!f.data(a,g,b,!0))&&(d==="mark"||!f.data(a,h,b,!0))&&setTimeout(function(){!f.data(a,g,b,!0)&&!f.data(a,h,b,!0)&&(f.removeData(a,e,!0),i.resolve())},0)}function l(a){for(var b in a)if(b!=="toJSON")return!1;return!0}function k(a,c,d){if(d===b&&a.nodeType===1){var e="data-"+c.replace(j,"$1-$2").toLowerCase();d=a.getAttribute(e);if(typeof d=="string"){try{d=d==="true"?!0:d==="false"?!1:d==="null"?null:f.isNaN(d)?i.test(d)?f.parseJSON(d):d:parseFloat(d)}catch(g){}f.data(a,c,d)}else d=b}return d}var c=a.document,d=a.navigator,e=a.location,f=function(){function J(){if(!e.isReady){try{c.documentElement.doScroll("left")}catch(a){setTimeout(J,1);return}e.ready()}}var e=function(a,b){return new e.fn.init(a,b,h)},f=a.jQuery,g=a.$,h,i=/^(?:[^<]*(<[\w\W]+>)[^>]*$|#([\w\-]*)$)/,j=/\S/,k=/^\s+/,l=/\s+$/,m=/\d/,n=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,o=/^[\],:{}\s]*$/,p=/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,q=/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,r=/(?:^|:|,)(?:\s*\[)+/g,s=/(webkit)[ \/]([\w.]+)/,t=/(opera)(?:.*version)?[ \/]([\w.]+)/,u=/(msie) ([\w.]+)/,v=/(mozilla)(?:.*? rv:([\w.]+))?/,w=/-([a-z])/ig,x=function(a,b){return b.toUpperCase()},y=d.userAgent,z,A,B,C=Object.prototype.toString,D=Object.prototype.hasOwnProperty,E=Array.prototype.push,F=Array.prototype.slice,G=String.prototype.trim,H=Array.prototype.indexOf,I={};e.fn=e.prototype={constructor:e,init:function(a,d,f){var g,h,j,k;if(!a)return this;if(a.nodeType){this.context=this[0]=a,this.length=1;return this}if(a==="body"&&!d&&c.body){this.context=c,this[0]=c.body,this.selector=a,this.length=1;return this}if(typeof a=="string"){a.charAt(0)!=="<"||a.charAt(a.length-1)!==">"||a.length<3?g=i.exec(a):g=[null,a,null];if(g&&(g[1]||!d)){if(g[1]){d=d instanceof e?d[0]:d,k=d?d.ownerDocument||d:c,j=n.exec(a),j?e.isPlainObject(d)?(a=[c.createElement(j[1])],e.fn.attr.call(a,d,!0)):a=[k.createElement(j[1])]:(j=e.buildFragment([g[1]],[k]),a=(j.cacheable?e.clone(j.fragment):j.fragment).childNodes);return e.merge(this,a)}h=c.getElementById(g[2]);if(h&&h.parentNode){if(h.id!==g[2])return f.find(a);this.length=1,this[0]=h}this.context=c,this.selector=a;return this}return!d||d.jquery?(d||f).find(a):this.constructor(d).find(a)}if(e.isFunction(a))return f.ready(a);a.selector!==b&&(this.selector=a.selector,this.context=a.context);return e.makeArray(a,this)},selector:"",jquery:"1.6.2",length:0,size:function(){return this.length},toArray:function(){return F.call(this,0)},get:function(a){return a==null?this.toArray():a<0?this[this.length+a]:this[a]},pushStack:function(a,b,c){var d=this.constructor();e.isArray(a)?E.apply(d,a):e.merge(d,a),d.prevObject=this,d.context=this.context,b==="find"?d.selector=this.selector+(this.selector?" ":"")+c:b&&(d.selector=this.selector+"."+b+"("+c+")");return d},each:function(a,b){return e.each(this,a,b)},ready:function(a){e.bindReady(),A.done(a);return this},eq:function(a){return a===-1?this.slice(a):this.slice(a,+a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(F.apply(this,arguments),"slice",F.call(arguments).join(","))},map:function(a){return this.pushStack(e.map(this,function(b,c){return a.call(b,c,b)}))},end:function(){return this.prevObject||this.constructor(null)},push:E,sort:[].sort,splice:[].splice},e.fn.init.prototype=e.fn,e.extend=e.fn.extend=function(){var a,c,d,f,g,h,i=arguments[0]||{},j=1,k=arguments.length,l=!1;typeof i=="boolean"&&(l=i,i=arguments[1]||{},j=2),typeof i!="object"&&!e.isFunction(i)&&(i={}),k===j&&(i=this,--j);for(;j0)return;A.resolveWith(c,[e]),e.fn.trigger&&e(c).trigger("ready").unbind("ready")}},bindReady:function(){if(!A){A=e._Deferred();if(c.readyState==="complete")return setTimeout(e.ready,1);if(c.addEventListener)c.addEventListener("DOMContentLoaded",B,!1),a.addEventListener("load",e.ready,!1);else if(c.attachEvent){c.attachEvent("onreadystatechange",B),a.attachEvent("onload",e.ready);var b=!1;try{b=a.frameElement==null}catch(d){}c.documentElement.doScroll&&b&&J()}}},isFunction:function(a){return e.type(a)==="function"},isArray:Array.isArray||function(a){return e.type(a)==="array"},isWindow:function(a){return a&&typeof a=="object"&&"setInterval"in a},isNaN:function(a){return a==null||!m.test(a)||isNaN(a)},type:function(a){return a==null?String(a):I[C.call(a)]||"object"},isPlainObject:function(a){if(!a||e.type(a)!=="object"||a.nodeType||e.isWindow(a))return!1;if(a.constructor&&!D.call(a,"constructor")&&!D.call(a.constructor.prototype,"isPrototypeOf"))return!1;var c;for(c in a);return c===b||D.call(a,c)},isEmptyObject:function(a){for(var b in a)return!1;return!0},error:function(a){throw a},parseJSON:function(b){if(typeof b!="string"||!b)return null;b=e.trim(b);if(a.JSON&&a.JSON.parse)return a.JSON.parse(b);if(o.test(b.replace(p,"@").replace(q,"]").replace(r,"")))return(new Function("return "+b))();e.error("Invalid JSON: "+b)},parseXML:function(b,c,d){a.DOMParser?(d=new DOMParser,c=d.parseFromString(b,"text/xml")):(c=new ActiveXObject("Microsoft.XMLDOM"),c.async="false",c.loadXML(b)),d=c.documentElement,(!d||!d.nodeName||d.nodeName==="parsererror")&&e.error("Invalid XML: "+b);return c},noop:function(){},globalEval:function(b){b&&j.test(b)&&(a.execScript||function(b){a.eval.call(a,b)})(b)},camelCase:function(a){return a.replace(w,x)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toUpperCase()===b.toUpperCase()},each:function(a,c,d){var f,g=0,h=a.length,i=h===b||e.isFunction(a);if(d){if(i){for(f in a)if(c.apply(a[f],d)===!1)break}else for(;g0&&a[0]&&a[j-1]||j===0||e.isArray(a));if(k)for(;i1?h.call(arguments,0):c,--e||g.resolveWith(g,h.call(b,0))}}var b=arguments,c=0,d=b.length,e=d,g=d<=1&&a&&f.isFunction(a.promise)?a:f.Deferred();if(d>1){for(;c
    a",d=a.getElementsByTagName("*"),e=a.getElementsByTagName("a")[0];if(!d||!d.length||!e)return{};g=c.createElement("select"),h=g.appendChild(c.createElement("option")),i=a.getElementsByTagName("input")[0],k={leadingWhitespace:a.firstChild.nodeType===3,tbody:!a.getElementsByTagName("tbody").length,htmlSerialize:!!a.getElementsByTagName("link").length,style:/top/.test(e.getAttribute("style")),hrefNormalized:e.getAttribute("href")==="/a",opacity:/^0.55$/.test(e.style.opacity),cssFloat:!!e.style.cssFloat,checkOn:i.value==="on",optSelected:h.selected,getSetAttribute:a.className!=="t",submitBubbles:!0,changeBubbles:!0,focusinBubbles:!1,deleteExpando:!0,noCloneEvent:!0,inlineBlockNeedsLayout:!1,shrinkWrapBlocks:!1,reliableMarginRight:!0},i.checked=!0,k.noCloneChecked=i.cloneNode(!0).checked,g.disabled=!0,k.optDisabled=!h.disabled;try{delete a.test}catch(v){k.deleteExpando=!1}!a.addEventListener&&a.attachEvent&&a.fireEvent&&(a.attachEvent("onclick",function(){k.noCloneEvent=!1}),a.cloneNode(!0).fireEvent("onclick")),i=c.createElement("input"),i.value="t",i.setAttribute("type","radio"),k.radioValue=i.value==="t",i.setAttribute("checked","checked"),a.appendChild(i),l=c.createDocumentFragment(),l.appendChild(a.firstChild),k.checkClone=l.cloneNode(!0).cloneNode(!0).lastChild.checked,a.innerHTML="",a.style.width=a.style.paddingLeft="1px",m=c.getElementsByTagName("body")[0],o=c.createElement(m?"div":"body"),p={visibility:"hidden",width:0,height:0,border:0,margin:0},m&&f.extend(p,{position:"absolute",left:-1e3,top:-1e3});for(t in p)o.style[t]=p[t];o.appendChild(a),n=m||b,n.insertBefore(o,n.firstChild),k.appendChecked=i.checked,k.boxModel=a.offsetWidth===2,"zoom"in a.style&&(a.style.display="inline",a.style.zoom=1,k.inlineBlockNeedsLayout=a.offsetWidth===2,a.style.display="",a.innerHTML="
    ",k.shrinkWrapBlocks=a.offsetWidth!==2),a.innerHTML="
    t
    ",q=a.getElementsByTagName("td"),u=q[0].offsetHeight===0,q[0].style.display="",q[1].style.display="none",k.reliableHiddenOffsets=u&&q[0].offsetHeight===0,a.innerHTML="",c.defaultView&&c.defaultView.getComputedStyle&&(j=c.createElement("div"),j.style.width="0",j.style.marginRight="0",a.appendChild(j),k.reliableMarginRight=(parseInt((c.defaultView.getComputedStyle(j,null)||{marginRight:0}).marginRight,10)||0)===0),o.innerHTML="",n.removeChild(o);if(a.attachEvent)for(t in{submit:1,change:1,focusin:1})s="on"+t,u=s in a,u||(a.setAttribute(s,"return;"),u=typeof a[s]=="function"),k[t+"Bubbles"]=u;o=l=g=h=m=j=a=i=null;return k}(),f.boxModel=f.support.boxModel;var i=/^(?:\{.*\}|\[.*\])$/,j=/([a-z])([A-Z])/g;f.extend({cache:{},uuid:0,expando:"jQuery"+(f.fn.jquery+Math.random()).replace(/\D/g,""),noData:{embed:!0,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",applet:!0},hasData:function(a){a=a.nodeType?f.cache[a[f.expando]]:a[f.expando];return!!a&&!l(a)},data:function(a,c,d,e){if(!!f.acceptData(a)){var g=f.expando,h=typeof c=="string",i,j=a.nodeType,k=j?f.cache:a,l=j?a[f.expando]:a[f.expando]&&f.expando;if((!l||e&&l&&!k[l][g])&&h&&d===b)return;l||(j?a[f.expando]=l=++f.uuid:l=f.expando),k[l]||(k[l]={},j||(k[l].toJSON=f.noop));if(typeof c=="object"||typeof c=="function")e?k[l][g]=f.extend(k[l][g],c):k[l]=f.extend(k[l],c);i=k[l],e&&(i[g]||(i[g]={}),i=i[g]),d!==b&&(i[f.camelCase(c)]=d);if(c==="events"&&!i[c])return i[g]&&i[g].events;return h?i[f.camelCase(c)]||i[c]:i}},removeData:function(b,c,d){if(!!f.acceptData(b)){var e=f.expando,g=b.nodeType,h=g?f.cache:b,i=g?b[f.expando]:f.expando;if(!h[i])return;if(c){var j=d?h[i][e]:h[i];if(j){delete j[c];if(!l(j))return}}if(d){delete h[i][e];if(!l(h[i]))return}var k=h[i][e];f.support.deleteExpando||h!=a?delete h[i]:h[i]=null,k?(h[i]={},g||(h[i].toJSON=f.noop),h[i][e]=k):g&&(f.support.deleteExpando?delete b[f.expando]:b.removeAttribute?b.removeAttribute(f.expando):b[f.expando]=null)}},_data:function(a,b,c){return f.data(a,b,c,!0)},acceptData:function(a){if(a.nodeName){var b=f.noData[a.nodeName.toLowerCase()];if(b)return b!==!0&&a.getAttribute("classid")===b}return!0}}),f.fn.extend({data:function(a,c){var d=null;if(typeof a=="undefined"){if(this.length){d=f.data(this[0]);if(this[0].nodeType===1){var e=this[0].attributes,g;for(var h=0,i=e.length;h-1)return!0;return!1},val:function(a){var c,d,e=this[0];if(!arguments.length){if(e){c=f.valHooks[e.nodeName.toLowerCase()]||f.valHooks[e.type];if(c&&"get"in c&&(d=c.get(e,"value"))!==b)return d;d=e.value;return typeof d=="string"?d.replace(p,""):d==null?"":d}return b}var g=f.isFunction(a);return this.each(function(d){var e=f(this),h;if(this.nodeType===1){g?h=a.call(this,d,e.val()):h=a,h==null?h="":typeof h=="number"?h+="":f.isArray(h)&&(h=f.map(h,function(a){return a==null?"":a+""})),c=f.valHooks[this.nodeName.toLowerCase()]||f.valHooks[this.type];if(!c||!("set"in c)||c.set(this,h,"value")===b)this.value=h}})}}),f.extend({valHooks:{option:{get:function(a){var b=a.attributes.value;return!b||b.specified?a.value:a.text}},select:{get:function(a){var b,c=a.selectedIndex,d=[],e=a.options,g=a.type==="select-one";if(c<0)return null;for(var h=g?c:0,i=g?c+1:e.length;h=0}),c.length||(a.selectedIndex=-1);return c}}},attrFn:{val:!0,css:!0,html:!0,text:!0,data:!0,width:!0,height:!0,offset:!0},attrFix:{tabindex:"tabIndex"},attr:function(a,c,d,e){var g=a.nodeType;if(!a||g===3||g===8||g===2)return b;if(e&&c in f.attrFn)return f(a)[c](d);if(!("getAttribute"in a))return f.prop(a,c,d);var h,i,j=g!==1||!f.isXMLDoc(a);j&&(c=f.attrFix[c]||c,i=f.attrHooks[c],i||(t.test(c)?i=w:v&&c!=="className"&&(f.nodeName(a,"form")||u.test(c))&&(i=v)));if(d!==b){if(d===null){f.removeAttr(a,c);return b}if(i&&"set"in i&&j&&(h=i.set(a,d,c))!==b)return h;a.setAttribute(c,""+d);return d}if(i&&"get"in i&&j&&(h=i.get(a,c))!==null)return h;h=a.getAttribute(c);return h===null?b:h},removeAttr:function(a,b){var c;a.nodeType===1&&(b=f.attrFix[b]||b,f.support.getSetAttribute?a.removeAttribute(b):(f.attr(a,b,""),a.removeAttributeNode(a.getAttributeNode(b))),t.test(b)&&(c=f.propFix[b]||b)in a&&(a[c]=!1))},attrHooks:{type:{set:function(a,b){if(q.test(a.nodeName)&&a.parentNode)f.error("type property can't be changed");else if(!f.support.radioValue&&b==="radio"&&f.nodeName(a,"input")){var c=a.value;a.setAttribute("type",b),c&&(a.value=c);return b}}},tabIndex:{get:function(a){var c=a.getAttributeNode("tabIndex");return c&&c.specified?parseInt(c.value,10):r.test(a.nodeName)||s.test(a.nodeName)&&a.href?0:b}},value:{get:function(a,b){if(v&&f.nodeName(a,"button"))return v.get(a,b);return b in a?a.value:null},set:function(a,b,c){if(v&&f.nodeName(a,"button"))return v.set(a,b,c);a.value=b}}},propFix:{tabindex:"tabIndex",readonly:"readOnly","for":"htmlFor","class":"className",maxlength:"maxLength",cellspacing:"cellSpacing",cellpadding:"cellPadding",rowspan:"rowSpan",colspan:"colSpan",usemap:"useMap",frameborder:"frameBorder",contenteditable:"contentEditable"},prop:function(a,c,d){var e=a.nodeType;if(!a||e===3||e===8||e===2)return b;var g,h,i=e!==1||!f.isXMLDoc(a);i&&(c=f.propFix[c]||c,h=f.propHooks[c]);return d!==b?h&&"set"in h&&(g=h.set(a,d,c))!==b?g:a[c]=d:h&&"get"in h&&(g=h.get(a,c))!==b?g:a[c]},propHooks:{}}),w={get:function(a,c){return f.prop(a,c)?c.toLowerCase():b},set:function(a,b,c){var d;b===!1?f.removeAttr(a,c):(d=f.propFix[c]||c,d in a&&(a[d]=!0),a.setAttribute(c,c.toLowerCase()));return c}},f.support.getSetAttribute||(f.attrFix=f.propFix,v=f.attrHooks.name=f.attrHooks.title=f.valHooks.button={get:function(a,c){var d;d=a.getAttributeNode(c);return d&&d.nodeValue!==""?d.nodeValue:b},set:function(a,b,c){var d=a.getAttributeNode(c);if(d){d.nodeValue=b;return b}}},f.each(["width","height"],function(a,b){f.attrHooks[b]=f.extend(f.attrHooks[b],{set:function(a,c){if(c===""){a.setAttribute(b,"auto");return c}}})})),f.support.hrefNormalized||f.each(["href","src","width","height"],function(a,c){f.attrHooks[c]=f.extend(f.attrHooks[c],{get:function(a){var d=a.getAttribute(c,2);return d===null?b:d}})}),f.support.style||(f.attrHooks.style={get:function(a){return a.style.cssText.toLowerCase()||b},set:function(a,b){return a.style.cssText=""+b}}),f.support.optSelected||(f.propHooks.selected=f.extend(f.propHooks.selected,{get:function(a){var b=a.parentNode;b&&(b.selectedIndex,b.parentNode&&b.parentNode.selectedIndex)}})),f.support.checkOn||f.each(["radio","checkbox"],function(){f.valHooks[this]={get:function(a){return a.getAttribute("value")===null?"on":a.value}}}),f.each(["radio","checkbox"],function(){f.valHooks[this]=f.extend(f.valHooks[this],{set:function(a,b){if(f.isArray(b))return a.checked=f.inArray(f(a).val(),b)>=0}})});var x=/\.(.*)$/,y=/^(?:textarea|input|select)$/i,z=/\./g,A=/ /g,B=/[^\w\s.|`]/g,C=function(a){return a.replace(B,"\\$&")};f.event={add:function(a,c,d,e){if(a.nodeType!==3&&a.nodeType!==8){if(d===!1)d=D;else if(!d)return;var g,h;d.handler&&(g=d,d=g.handler),d.guid||(d.guid=f.guid++);var i=f._data(a);if(!i)return;var j=i.events,k=i.handle;j||(i.events=j={}),k||(i.handle=k=function(a){return typeof f!="undefined"&&(!a||f.event.triggered!==a.type)?f.event.handle.apply(k.elem,arguments):b}),k.elem=a,c=c.split(" ");var l,m=0,n;while(l=c[m++]){h=g?f.extend({},g):{handler:d,data:e},l.indexOf(".")>-1?(n=l.split("."),l=n.shift(),h.namespace=n.slice(0).sort().join(".")):(n=[],h.namespace=""),h.type=l,h.guid||(h.guid=d.guid);var o=j[l],p=f.event.special[l]||{};if(!o){o=j[l]=[];if(!p.setup||p.setup.call(a,e,n,k)===!1)a.addEventListener?a.addEventListener(l,k,!1):a.attachEvent&&a.attachEvent("on"+l,k)}p.add&&(p.add.call(a,h),h.handler.guid||(h.handler.guid=d.guid)),o.push(h),f.event.global[l]=!0}a=null}},global:{},remove:function(a,c,d,e){if(a.nodeType!==3&&a.nodeType!==8){d===!1&&(d=D);var g,h,i,j,k=0,l,m,n,o,p,q,r,s=f.hasData(a)&&f._data(a),t=s&&s.events;if(!s||!t)return;c&&c.type&&(d=c.handler,c=c.type);if(!c||typeof c=="string"&&c.charAt(0)==="."){c=c||"";for(h in t)f.event.remove(a,h+c);return}c=c.split(" ");while(h=c[k++]){r=h,q=null,l=h.indexOf(".")<0,m=[],l||(m=h.split("."),h=m.shift(),n=new RegExp("(^|\\.)"+f.map(m.slice(0).sort(),C).join("\\.(?:.*\\.)?")+"(\\.|$)")),p=t[h];if(!p)continue;if(!d){for(j=0;j=0&&(h=h.slice(0,-1),j=!0),h.indexOf(".")>=0&&(i=h.split("."),h=i. -shift(),i.sort());if(!!e&&!f.event.customEvent[h]||!!f.event.global[h]){c=typeof c=="object"?c[f.expando]?c:new f.Event(h,c):new f.Event(h),c.type=h,c.exclusive=j,c.namespace=i.join("."),c.namespace_re=new RegExp("(^|\\.)"+i.join("\\.(?:.*\\.)?")+"(\\.|$)");if(g||!e)c.preventDefault(),c.stopPropagation();if(!e){f.each(f.cache,function(){var a=f.expando,b=this[a];b&&b.events&&b.events[h]&&f.event.trigger(c,d,b.handle.elem)});return}if(e.nodeType===3||e.nodeType===8)return;c.result=b,c.target=e,d=d!=null?f.makeArray(d):[],d.unshift(c);var k=e,l=h.indexOf(":")<0?"on"+h:"";do{var m=f._data(k,"handle");c.currentTarget=k,m&&m.apply(k,d),l&&f.acceptData(k)&&k[l]&&k[l].apply(k,d)===!1&&(c.result=!1,c.preventDefault()),k=k.parentNode||k.ownerDocument||k===c.target.ownerDocument&&a}while(k&&!c.isPropagationStopped());if(!c.isDefaultPrevented()){var n,o=f.event.special[h]||{};if((!o._default||o._default.call(e.ownerDocument,c)===!1)&&(h!=="click"||!f.nodeName(e,"a"))&&f.acceptData(e)){try{l&&e[h]&&(n=e[l],n&&(e[l]=null),f.event.triggered=h,e[h]())}catch(p){}n&&(e[l]=n),f.event.triggered=b}}return c.result}},handle:function(c){c=f.event.fix(c||a.event);var d=((f._data(this,"events")||{})[c.type]||[]).slice(0),e=!c.exclusive&&!c.namespace,g=Array.prototype.slice.call(arguments,0);g[0]=c,c.currentTarget=this;for(var h=0,i=d.length;h-1?f.map(a.options,function(a){return a.selected}).join("-"):"":f.nodeName(a,"select")&&(c=a.selectedIndex);return c},J=function(c){var d=c.target,e,g;if(!!y.test(d.nodeName)&&!d.readOnly){e=f._data(d,"_change_data"),g=I(d),(c.type!=="focusout"||d.type!=="radio")&&f._data(d,"_change_data",g);if(e===b||g===e)return;if(e!=null||g)c.type="change",c.liveFired=b,f.event.trigger(c,arguments[1],d)}};f.event.special.change={filters:{focusout:J,beforedeactivate:J,click:function(a){var b=a.target,c=f.nodeName(b,"input")?b.type:"";(c==="radio"||c==="checkbox"||f.nodeName(b,"select"))&&J.call(this,a)},keydown:function(a){var b=a.target,c=f.nodeName(b,"input")?b.type:"";(a.keyCode===13&&!f.nodeName(b,"textarea")||a.keyCode===32&&(c==="checkbox"||c==="radio")||c==="select-multiple")&&J.call(this,a)},beforeactivate:function(a){var b=a.target;f._data(b,"_change_data",I(b))}},setup:function(a,b){if(this.type==="file")return!1;for(var c in H)f.event.add(this,c+".specialChange",H[c]);return y.test(this.nodeName)},teardown:function(a){f.event.remove(this,".specialChange");return y.test(this.nodeName)}},H=f.event.special.change.filters,H.focus=H.beforeactivate}f.support.focusinBubbles||f.each({focus:"focusin",blur:"focusout"},function(a,b){function e(a){var c=f.event.fix(a);c.type=b,c.originalEvent={},f.event.trigger(c,null,c.target),c.isDefaultPrevented()&&a.preventDefault()}var d=0;f.event.special[b]={setup:function(){d++===0&&c.addEventListener(a,e,!0)},teardown:function(){--d===0&&c.removeEventListener(a,e,!0)}}}),f.each(["bind","one"],function(a,c){f.fn[c]=function(a,d,e){var g;if(typeof a=="object"){for(var h in a)this[c](h,d,a[h],e);return this}if(arguments.length===2||d===!1)e=d,d=b;c==="one"?(g=function(a){f(this).unbind(a,g);return e.apply(this,arguments)},g.guid=e.guid||f.guid++):g=e;if(a==="unload"&&c!=="one")this.one(a,d,e);else for(var i=0,j=this.length;i0?this.bind(b,a,c):this.trigger(b)},f.attrFn&&(f.attrFn[b]=!0)}),function(){function u(a,b,c,d,e,f){for(var g=0,h=d.length;g0){j=i;break}}i=i[a]}d[g]=j}}}function t(a,b,c,d,e,f){for(var g=0,h=d.length;g+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,d=0,e=Object.prototype.toString,g=!1,h=!0,i=/\\/g,j=/\W/;[0,0].sort(function(){h=!1;return 0});var k=function(b,d,f,g){f=f||[],d=d||c;var h=d;if(d.nodeType!==1&&d.nodeType!==9)return[];if(!b||typeof b!="string")return f;var i,j,n,o,q,r,s,t,u=!0,w=k.isXML(d),x=[],y=b;do{a.exec(""),i=a.exec(y);if(i){y=i[3],x.push(i[1]);if(i[2]){o=i[3];break}}}while(i);if(x.length>1&&m.exec(b))if(x.length===2&&l.relative[x[0]])j=v(x[0]+x[1],d);else{j=l.relative[x[0]]?[d]:k(x.shift(),d);while(x.length)b=x.shift(),l.relative[b]&&(b+=x.shift()),j=v(b,j)}else{!g&&x.length>1&&d.nodeType===9&&!w&&l.match.ID.test(x[0])&&!l.match.ID.test(x[x.length-1])&&(q=k.find(x.shift(),d,w),d=q.expr?k.filter(q.expr,q.set)[0]:q.set[0]);if(d){q=g?{expr:x.pop(),set:p(g)}:k.find(x.pop(),x.length===1&&(x[0]==="~"||x[0]==="+")&&d.parentNode?d.parentNode:d,w),j=q.expr?k.filter(q.expr,q.set):q.set,x.length>0?n=p(j):u=!1;while(x.length)r=x.pop(),s=r,l.relative[r]?s=x.pop():r="",s==null&&(s=d),l.relative[r](n,s,w)}else n=x=[]}n||(n=j),n||k.error(r||b);if(e.call(n)==="[object Array]")if(!u)f.push.apply(f,n);else if(d&&d.nodeType===1)for(t=0;n[t]!=null;t++)n[t]&&(n[t]===!0||n[t].nodeType===1&&k.contains(d,n[t]))&&f.push(j[t]);else for(t=0;n[t]!=null;t++)n[t]&&n[t].nodeType===1&&f.push(j[t]);else p(n,f);o&&(k(o,h,f,g),k.uniqueSort(f));return f};k.uniqueSort=function(a){if(r){g=h,a.sort(r);if(g)for(var b=1;b0},k.find=function(a,b,c){var d;if(!a)return[];for(var e=0,f=l.order.length;e":function(a,b){var c,d=typeof b=="string",e=0,f=a.length;if(d&&!j.test(b)){b=b.toLowerCase();for(;e=0)?c||d.push(h):c&&(b[g]=!1));return!1},ID:function(a){return a[1].replace(i,"")},TAG:function(a,b){return a[1].replace(i,"").toLowerCase()},CHILD:function(a){if(a[1]==="nth"){a[2]||k.error(a[0]),a[2]=a[2].replace(/^\+|\s*/g,"");var b=/(-?)(\d*)(?:n([+\-]?\d*))?/.exec(a[2]==="even"&&"2n"||a[2]==="odd"&&"2n+1"||!/\D/.test(a[2])&&"0n+"+a[2]||a[2]);a[2]=b[1]+(b[2]||1)-0,a[3]=b[3]-0}else a[2]&&k.error(a[0]);a[0]=d++;return a},ATTR:function(a,b,c,d,e,f){var g=a[1]=a[1].replace(i,"");!f&&l.attrMap[g]&&(a[1]=l.attrMap[g]),a[4]=(a[4]||a[5]||"").replace(i,""),a[2]==="~="&&(a[4]=" "+a[4]+" ");return a},PSEUDO:function(b,c,d,e,f){if(b[1]==="not")if((a.exec(b[3])||"").length>1||/^\w/.test(b[3]))b[3]=k(b[3],null,null,c);else{var g=k.filter(b[3],c,d,!0^f);d||e.push.apply(e,g);return!1}else if(l.match.POS.test(b[0])||l.match.CHILD.test(b[0]))return!0;return b},POS:function(a){a.unshift(!0);return a}},filters:{enabled:function(a){return a.disabled===!1&&a.type!=="hidden"},disabled:function(a){return a.disabled===!0},checked:function(a){return a.checked===!0},selected:function(a){a.parentNode&&a.parentNode.selectedIndex;return a.selected===!0},parent:function(a){return!!a.firstChild},empty:function(a){return!a.firstChild},has:function(a,b,c){return!!k(c[3],a).length},header:function(a){return/h\d/i.test(a.nodeName)},text:function(a){var b=a.getAttribute("type"),c=a.type;return a.nodeName.toLowerCase()==="input"&&"text"===c&&(b===c||b===null)},radio:function(a){return a.nodeName.toLowerCase()==="input"&&"radio"===a.type},checkbox:function(a){return a.nodeName.toLowerCase()==="input"&&"checkbox"===a.type},file:function(a){return a.nodeName.toLowerCase()==="input"&&"file"===a.type},password:function(a){return a.nodeName.toLowerCase()==="input"&&"password"===a.type},submit:function(a){var b=a.nodeName.toLowerCase();return(b==="input"||b==="button")&&"submit"===a.type},image:function(a){return a.nodeName.toLowerCase()==="input"&&"image"===a.type},reset:function(a){var b=a.nodeName.toLowerCase();return(b==="input"||b==="button")&&"reset"===a.type},button:function(a){var b=a.nodeName.toLowerCase();return b==="input"&&"button"===a.type||b==="button"},input:function(a){return/input|select|textarea|button/i.test(a.nodeName)},focus:function(a){return a===a.ownerDocument.activeElement}},setFilters:{first:function(a,b){return b===0},last:function(a,b,c,d){return b===d.length-1},even:function(a,b){return b%2===0},odd:function(a,b){return b%2===1},lt:function(a,b,c){return bc[3]-0},nth:function(a,b,c){return c[3]-0===b},eq:function(a,b,c){return c[3]-0===b}},filter:{PSEUDO:function(a,b,c,d){var e=b[1],f=l.filters[e];if(f)return f(a,c,b,d);if(e==="contains")return(a.textContent||a.innerText||k.getText([a])||"").indexOf(b[3])>=0;if(e==="not"){var g=b[3];for(var h=0,i=g.length;h=0}},ID:function(a,b){return a.nodeType===1&&a.getAttribute("id")===b},TAG:function(a,b){return b==="*"&&a.nodeType===1||a.nodeName.toLowerCase()===b},CLASS:function(a,b){return(" "+(a.className||a.getAttribute("class"))+" ").indexOf(b)>-1},ATTR:function(a,b){var c=b[1],d=l.attrHandle[c]?l.attrHandle[c](a):a[c]!=null?a[c]:a.getAttribute(c),e=d+"",f=b[2],g=b[4];return d==null?f==="!=":f==="="?e===g:f==="*="?e.indexOf(g)>=0:f==="~="?(" "+e+" ").indexOf(g)>=0:g?f==="!="?e!==g:f==="^="?e.indexOf(g)===0:f==="$="?e.substr(e.length-g.length)===g:f==="|="?e===g||e.substr(0,g.length+1)===g+"-":!1:e&&d!==!1},POS:function(a,b,c,d){var e=b[2],f=l.setFilters[e];if(f)return f(a,c,b,d)}}},m=l.match.POS,n=function(a,b){return"\\"+(b-0+1)};for(var o in l.match)l.match[o]=new RegExp(l.match[o].source+/(?![^\[]*\])(?![^\(]*\))/.source),l.leftMatch[o]=new RegExp(/(^(?:.|\r|\n)*?)/.source+l.match[o].source.replace(/\\(\d+)/g,n));var p=function(a,b){a=Array.prototype.slice.call(a,0);if(b){b.push.apply(b,a);return b}return a};try{Array.prototype.slice.call(c.documentElement.childNodes,0)[0].nodeType}catch(q){p=function(a,b){var c=0,d=b||[];if(e.call(a)==="[object Array]")Array.prototype.push.apply(d,a);else if(typeof a.length=="number")for(var f=a.length;c",e.insertBefore(a,e.firstChild),c.getElementById(d)&&(l.find.ID=function(a,c,d){if(typeof c.getElementById!="undefined"&&!d){var e=c.getElementById(a[1]);return e?e.id===a[1]||typeof e.getAttributeNode!="undefined"&&e.getAttributeNode("id").nodeValue===a[1]?[e]:b:[]}},l.filter.ID=function(a,b){var c=typeof a.getAttributeNode!="undefined"&&a.getAttributeNode("id");return a.nodeType===1&&c&&c.nodeValue===b}),e.removeChild(a),e=a=null}(),function(){var a=c.createElement("div");a.appendChild(c.createComment("")),a.getElementsByTagName("*").length>0&&(l.find.TAG=function(a,b){var c=b.getElementsByTagName(a[1]);if(a[1]==="*"){var d=[];for(var e=0;c[e];e++)c[e].nodeType===1&&d.push(c[e]);c=d}return c}),a.innerHTML="",a.firstChild&&typeof a.firstChild.getAttribute!="undefined"&&a.firstChild.getAttribute("href")!=="#"&&(l.attrHandle.href=function(a){return a.getAttribute("href",2)}),a=null}(),c.querySelectorAll&&function(){var a=k,b=c.createElement("div"),d="__sizzle__";b.innerHTML="

    ";if(!b.querySelectorAll||b.querySelectorAll(".TEST").length!==0){k=function(b,e,f,g){e=e||c;if(!g&&!k.isXML(e)){var h=/^(\w+$)|^\.([\w\-]+$)|^#([\w\-]+$)/.exec(b);if(h&&(e.nodeType===1||e.nodeType===9)){if(h[1])return p(e.getElementsByTagName(b),f);if(h[2]&&l.find.CLASS&&e.getElementsByClassName)return p(e.getElementsByClassName(h[2]),f)}if(e.nodeType===9){if(b==="body"&&e.body)return p([e.body],f);if(h&&h[3]){var i=e.getElementById(h[3]);if(!i||!i.parentNode)return p([],f);if(i.id===h[3])return p([i],f)}try{return p(e.querySelectorAll(b),f)}catch(j){}}else if(e.nodeType===1&&e.nodeName.toLowerCase()!=="object"){var m=e,n=e.getAttribute("id"),o=n||d,q=e.parentNode,r=/^\s*[+~]/.test(b);n?o=o.replace(/'/g,"\\$&"):e.setAttribute("id",o),r&&q&&(e=e.parentNode);try{if(!r||q)return p(e.querySelectorAll("[id='"+o+"'] "+b),f)}catch(s){}finally{n||m.removeAttribute("id")}}}return a(b,e,f,g)};for(var e in a)k[e]=a[e];b=null}}(),function(){var a=c.documentElement,b=a.matchesSelector||a.mozMatchesSelector||a.webkitMatchesSelector||a.msMatchesSelector;if(b){var d=!b.call(c.createElement("div"),"div"),e=!1;try{b.call(c.documentElement,"[test!='']:sizzle")}catch(f){e=!0}k.matchesSelector=function(a,c){c=c.replace(/\=\s*([^'"\]]*)\s*\]/g,"='$1']");if(!k.isXML(a))try{if(e||!l.match.PSEUDO.test(c)&&!/!=/.test(c)){var f=b.call(a,c);if(f||!d||a.document&&a.document.nodeType!==11)return f}}catch(g){}return k(c,null,null,[a]).length>0}}}(),function(){var a=c.createElement("div");a.innerHTML="
    ";if(!!a.getElementsByClassName&&a.getElementsByClassName("e").length!==0){a.lastChild.className="e";if(a.getElementsByClassName("e").length===1)return;l.order.splice(1,0,"CLASS"),l.find.CLASS=function(a,b,c){if(typeof b.getElementsByClassName!="undefined"&&!c)return b.getElementsByClassName(a[1])},a=null}}(),c.documentElement.contains?k.contains=function(a,b){return a!==b&&(a.contains?a.contains(b):!0)}:c.documentElement.compareDocumentPosition?k.contains=function(a,b){return!!(a.compareDocumentPosition(b)&16)}:k.contains=function(){return!1},k.isXML=function(a){var b=(a?a.ownerDocument||a:0).documentElement;return b?b.nodeName!=="HTML":!1};var v=function(a,b){var c,d=[],e="",f=b.nodeType?[b]:b;while(c=l.match.PSEUDO.exec(a))e+=c[0],a=a.replace(l.match.PSEUDO,"");a=l.relative[a]?a+"*":a;for(var g=0,h=f.length;g0)for(h=g;h0:this.filter(a).length>0)},closest:function(a,b){var c=[],d,e,g=this[0];if(f.isArray(a)){var h,i,j={},k=1;if(g&&a.length){for(d=0,e=a.length;d-1:f(g).is(h))&&c.push({selector:i,elem:g,level:k});g=g.parentNode,k++}}return c}var l=T.test(a)||typeof a!="string"?f(a,b||this.context):0;for(d=0,e=this.length;d-1:f.find.matchesSelector(g,a)){c.push(g);break}g=g.parentNode;if(!g||!g.ownerDocument||g===b||g.nodeType===11)break}}c=c.length>1?f.unique(c):c;return this.pushStack(c,"closest",a)},index:function(a){if(!a||typeof a=="string")return f.inArray(this[0],a?f(a):this.parent().children());return f.inArray(a.jquery?a[0]:a,this)},add:function(a,b){var c=typeof a=="string"?f(a,b):f.makeArray(a&&a.nodeType?[a]:a),d=f.merge(this.get(),c);return this.pushStack(V(c[0])||V(d[0])?d:f.unique(d))},andSelf:function(){return this.add(this.prevObject)}}),f.each({parent:function(a){var b=a.parentNode;return b&&b.nodeType!==11?b:null},parents:function(a){return f.dir(a,"parentNode")},parentsUntil:function(a,b,c){return f.dir(a,"parentNode",c)},next:function(a){return f.nth(a,2,"nextSibling")},prev:function(a){return f.nth(a,2,"previousSibling")},nextAll:function(a){return f.dir(a,"nextSibling")},prevAll:function(a){return f.dir(a,"previousSibling")},nextUntil:function(a,b,c){return f.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return f.dir(a,"previousSibling",c)},siblings:function(a){return f.sibling(a.parentNode.firstChild,a)},children:function(a){return f.sibling(a.firstChild)},contents:function(a){return f.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:f.makeArray(a.childNodes)}},function(a,b){f.fn[a]=function(c,d){var e=f.map(this,b,c),g=S.call(arguments);O.test(a)||(d=c),d&&typeof d=="string"&&(e=f.filter(d,e)),e=this.length>1&&!U[a]?f.unique(e):e,(this.length>1||Q.test(d))&&P.test(a)&&(e=e.reverse());return this.pushStack(e,a,g.join(","))}}),f.extend({filter:function(a,b,c){c&&(a=":not("+a+")");return b.length===1?f.find.matchesSelector(b[0],a)?[b[0]]:[]:f.find.matches(a,b)},dir:function(a,c,d){var e=[],g=a[c];while(g&&g.nodeType!==9&&(d===b||g.nodeType!==1||!f(g).is(d)))g.nodeType===1&&e.push(g),g=g[c];return e},nth:function(a,b,c,d){b=b||1;var e=0;for(;a;a=a[c])if(a.nodeType===1&&++e===b)break;return a},sibling:function(a,b){var c=[];for(;a;a=a.nextSibling)a.nodeType===1&&a!==b&&c.push(a);return c}});var X=/ jQuery\d+="(?:\d+|null)"/g,Y=/^\s+/,Z=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig,$=/<([\w:]+)/,_=/",""],legend:[1,"
    ","
    "],thead:[1,"","
    "],tr:[2,"","
    "],td:[3,"","
    "],col:[2,"","
    "],area:[1,"",""],_default:[0,"",""]};bf.optgroup=bf.option,bf.tbody=bf.tfoot=bf.colgroup=bf.caption=bf.thead,bf.th=bf.td,f.support.htmlSerialize||(bf._default=[1,"div
    ","
    "]),f.fn.extend({text:function(a){if(f.isFunction(a))return this.each(function(b){var c=f(this);c.text(a.call(this,b,c.text()))});if(typeof a!="object"&&a!==b)return this.empty().append((this[0]&&this[0].ownerDocument||c).createTextNode(a));return f.text(this)},wrapAll:function(a){if(f.isFunction(a))return this.each(function(b){f(this).wrapAll(a.call(this,b))});if(this[0]){var b=f(a,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&b.insertBefore(this[0]),b.map(function(){var a=this;while(a.firstChild&&a.firstChild.nodeType===1)a=a.firstChild;return a}).append(this)}return this},wrapInner:function(a){if(f.isFunction(a))return this.each(function(b){f(this).wrapInner(a.call(this,b))});return this.each(function(){var b=f(this),c=b.contents();c.length?c.wrapAll(a):b.append(a)})},wrap:function(a){return this.each(function(){f(this).wrapAll(a)})},unwrap:function(){return this.parent().each(function(){f.nodeName(this,"body")||f(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,!0,function(a){this.nodeType===1&&this.appendChild(a)})},prepend:function(){return this.domManip(arguments,!0,function(a){this.nodeType===1&&this.insertBefore(a,this.firstChild)})},before:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this)});if(arguments.length){var a=f(arguments[0]);a.push.apply(a,this.toArray());return this.pushStack(a,"before",arguments)}},after:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this.nextSibling)});if(arguments.length){var a=this.pushStack(this,"after",arguments);a.push.apply(a,f(arguments[0]).toArray());return a}},remove:function(a,b){for(var c=0,d;(d=this[c])!=null;c++)if(!a||f.filter(a,[d]).length)!b&&d.nodeType===1&&(f.cleanData(d.getElementsByTagName("*")),f.cleanData([d])),d.parentNode&&d.parentNode.removeChild(d);return this},empty:function(){for(var a=0,b;(b=this[a])!=null;a++){b.nodeType===1&&f.cleanData(b.getElementsByTagName("*"));while(b.firstChild)b.removeChild(b.firstChild)}return this},clone:function(a,b){a=a==null?!1:a,b=b==null?a:b;return this.map(function(){return f.clone(this,a,b)})},html:function(a){if(a===b)return this[0]&&this[0].nodeType===1?this[0].innerHTML.replace(X,""):null;if(typeof a=="string"&&!bb.test(a)&&(f.support.leadingWhitespace||!Y.test(a))&&!bf[($.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(Z,"<$1>");try{for(var c=0,d=this.length;c1&&l0?this.clone(!0):this).get();f(e[h])[b](j),d=d.concat(j -)}return this.pushStack(d,a,e.selector)}}),f.extend({clone:function(a,b,c){var d=a.cloneNode(!0),e,g,h;if((!f.support.noCloneEvent||!f.support.noCloneChecked)&&(a.nodeType===1||a.nodeType===11)&&!f.isXMLDoc(a)){bi(a,d),e=bj(a),g=bj(d);for(h=0;e[h];++h)bi(e[h],g[h])}if(b){bh(a,d);if(c){e=bj(a),g=bj(d);for(h=0;e[h];++h)bh(e[h],g[h])}}e=g=null;return d},clean:function(a,b,d,e){var g;b=b||c,typeof b.createElement=="undefined"&&(b=b.ownerDocument||b[0]&&b[0].ownerDocument||c);var h=[],i;for(var j=0,k;(k=a[j])!=null;j++){typeof k=="number"&&(k+="");if(!k)continue;if(typeof k=="string")if(!ba.test(k))k=b.createTextNode(k);else{k=k.replace(Z,"<$1>");var l=($.exec(k)||["",""])[1].toLowerCase(),m=bf[l]||bf._default,n=m[0],o=b.createElement("div");o.innerHTML=m[1]+k+m[2];while(n--)o=o.lastChild;if(!f.support.tbody){var p=_.test(k),q=l==="table"&&!p?o.firstChild&&o.firstChild.childNodes:m[1]===""&&!p?o.childNodes:[];for(i=q.length-1;i>=0;--i)f.nodeName(q[i],"tbody")&&!q[i].childNodes.length&&q[i].parentNode.removeChild(q[i])}!f.support.leadingWhitespace&&Y.test(k)&&o.insertBefore(b.createTextNode(Y.exec(k)[0]),o.firstChild),k=o.childNodes}var r;if(!f.support.appendChecked)if(k[0]&&typeof (r=k.length)=="number")for(i=0;i=0)return b+"px"}}}),f.support.opacity||(f.cssHooks.opacity={get:function(a,b){return bo.test((b&&a.currentStyle?a.currentStyle.filter:a.style.filter)||"")?parseFloat(RegExp.$1)/100+"":b?"1":""},set:function(a,b){var c=a.style,d=a.currentStyle;c.zoom=1;var e=f.isNaN(b)?"":"alpha(opacity="+b*100+")",g=d&&d.filter||c.filter||"";c.filter=bn.test(g)?g.replace(bn,e):g+" "+e}}),f(function(){f.support.reliableMarginRight||(f.cssHooks.marginRight={get:function(a,b){var c;f.swap(a,{display:"inline-block"},function(){b?c=bx(a,"margin-right","marginRight"):c=a.style.marginRight});return c}})}),c.defaultView&&c.defaultView.getComputedStyle&&(by=function(a,c){var d,e,g;c=c.replace(bp,"-$1").toLowerCase();if(!(e=a.ownerDocument.defaultView))return b;if(g=e.getComputedStyle(a,null))d=g.getPropertyValue(c),d===""&&!f.contains(a.ownerDocument.documentElement,a)&&(d=f.style(a,c));return d}),c.documentElement.currentStyle&&(bz=function(a,b){var c,d=a.currentStyle&&a.currentStyle[b],e=a.runtimeStyle&&a.runtimeStyle[b],f=a.style;!bq.test(d)&&br.test(d)&&(c=f.left,e&&(a.runtimeStyle.left=a.currentStyle.left),f.left=b==="fontSize"?"1em":d||0,d=f.pixelLeft+"px",f.left=c,e&&(a.runtimeStyle.left=e));return d===""?"auto":d}),bx=by||bz,f.expr&&f.expr.filters&&(f.expr.filters.hidden=function(a){var b=a.offsetWidth,c=a.offsetHeight;return b===0&&c===0||!f.support.reliableHiddenOffsets&&(a.style.display||f.css(a,"display"))==="none"},f.expr.filters.visible=function(a){return!f.expr.filters.hidden(a)});var bB=/%20/g,bC=/\[\]$/,bD=/\r?\n/g,bE=/#.*$/,bF=/^(.*?):[ \t]*([^\r\n]*)\r?$/mg,bG=/^(?:color|date|datetime|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,bH=/^(?:about|app|app\-storage|.+\-extension|file|widget):$/,bI=/^(?:GET|HEAD)$/,bJ=/^\/\//,bK=/\?/,bL=/)<[^<]*)*<\/script>/gi,bM=/^(?:select|textarea)/i,bN=/\s+/,bO=/([?&])_=[^&]*/,bP=/^([\w\+\.\-]+:)(?:\/\/([^\/?#:]*)(?::(\d+))?)?/,bQ=f.fn.load,bR={},bS={},bT,bU;try{bT=e.href}catch(bV){bT=c.createElement("a"),bT.href="",bT=bT.href}bU=bP.exec(bT.toLowerCase())||[],f.fn.extend({load:function(a,c,d){if(typeof a!="string"&&bQ)return bQ.apply(this,arguments);if(!this.length)return this;var e=a.indexOf(" ");if(e>=0){var g=a.slice(e,a.length);a=a.slice(0,e)}var h="GET";c&&(f.isFunction(c)?(d=c,c=b):typeof c=="object"&&(c=f.param(c,f.ajaxSettings.traditional),h="POST"));var i=this;f.ajax({url:a,type:h,dataType:"html",data:c,complete:function(a,b,c){c=a.responseText,a.isResolved()&&(a.done(function(a){c=a}),i.html(g?f("
    ").append(c.replace(bL,"")).find(g):c)),d&&i.each(d,[c,b,a])}});return this},serialize:function(){return f.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?f.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||bM.test(this.nodeName)||bG.test(this.type))}).map(function(a,b){var c=f(this).val();return c==null?null:f.isArray(c)?f.map(c,function(a,c){return{name:b.name,value:a.replace(bD,"\r\n")}}):{name:b.name,value:c.replace(bD,"\r\n")}}).get()}}),f.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),function(a,b){f.fn[b]=function(a){return this.bind(b,a)}}),f.each(["get","post"],function(a,c){f[c]=function(a,d,e,g){f.isFunction(d)&&(g=g||e,e=d,d=b);return f.ajax({type:c,url:a,data:d,success:e,dataType:g})}}),f.extend({getScript:function(a,c){return f.get(a,b,c,"script")},getJSON:function(a,b,c){return f.get(a,b,c,"json")},ajaxSetup:function(a,b){b?f.extend(!0,a,f.ajaxSettings,b):(b=a,a=f.extend(!0,f.ajaxSettings,b));for(var c in{context:1,url:1})c in b?a[c]=b[c]:c in f.ajaxSettings&&(a[c]=f.ajaxSettings[c]);return a},ajaxSettings:{url:bT,isLocal:bH.test(bU[1]),global:!0,type:"GET",contentType:"application/x-www-form-urlencoded",processData:!0,async:!0,accepts:{xml:"application/xml, text/xml",html:"text/html",text:"text/plain",json:"application/json, text/javascript","*":"*/*"},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText"},converters:{"* text":a.String,"text html":!0,"text json":f.parseJSON,"text xml":f.parseXML}},ajaxPrefilter:bW(bR),ajaxTransport:bW(bS),ajax:function(a,c){function w(a,c,l,m){if(s!==2){s=2,q&&clearTimeout(q),p=b,n=m||"",v.readyState=a?4:0;var o,r,u,w=l?bZ(d,v,l):b,x,y;if(a>=200&&a<300||a===304){if(d.ifModified){if(x=v.getResponseHeader("Last-Modified"))f.lastModified[k]=x;if(y=v.getResponseHeader("Etag"))f.etag[k]=y}if(a===304)c="notmodified",o=!0;else try{r=b$(d,w),c="success",o=!0}catch(z){c="parsererror",u=z}}else{u=c;if(!c||a)c="error",a<0&&(a=0)}v.status=a,v.statusText=c,o?h.resolveWith(e,[r,c,v]):h.rejectWith(e,[v,c,u]),v.statusCode(j),j=b,t&&g.trigger("ajax"+(o?"Success":"Error"),[v,d,o?r:u]),i.resolveWith(e,[v,c]),t&&(g.trigger("ajaxComplete",[v,d]),--f.active||f.event.trigger("ajaxStop"))}}typeof a=="object"&&(c=a,a=b),c=c||{};var d=f.ajaxSetup({},c),e=d.context||d,g=e!==d&&(e.nodeType||e instanceof f)?f(e):f.event,h=f.Deferred(),i=f._Deferred(),j=d.statusCode||{},k,l={},m={},n,o,p,q,r,s=0,t,u,v={readyState:0,setRequestHeader:function(a,b){if(!s){var c=a.toLowerCase();a=m[c]=m[c]||a,l[a]=b}return this},getAllResponseHeaders:function(){return s===2?n:null},getResponseHeader:function(a){var c;if(s===2){if(!o){o={};while(c=bF.exec(n))o[c[1].toLowerCase()]=c[2]}c=o[a.toLowerCase()]}return c===b?null:c},overrideMimeType:function(a){s||(d.mimeType=a);return this},abort:function(a){a=a||"abort",p&&p.abort(a),w(0,a);return this}};h.promise(v),v.success=v.done,v.error=v.fail,v.complete=i.done,v.statusCode=function(a){if(a){var b;if(s<2)for(b in a)j[b]=[j[b],a[b]];else b=a[v.status],v.then(b,b)}return this},d.url=((a||d.url)+"").replace(bE,"").replace(bJ,bU[1]+"//"),d.dataTypes=f.trim(d.dataType||"*").toLowerCase().split(bN),d.crossDomain==null&&(r=bP.exec(d.url.toLowerCase()),d.crossDomain=!(!r||r[1]==bU[1]&&r[2]==bU[2]&&(r[3]||(r[1]==="http:"?80:443))==(bU[3]||(bU[1]==="http:"?80:443)))),d.data&&d.processData&&typeof d.data!="string"&&(d.data=f.param(d.data,d.traditional)),bX(bR,d,c,v);if(s===2)return!1;t=d.global,d.type=d.type.toUpperCase(),d.hasContent=!bI.test(d.type),t&&f.active++===0&&f.event.trigger("ajaxStart");if(!d.hasContent){d.data&&(d.url+=(bK.test(d.url)?"&":"?")+d.data),k=d.url;if(d.cache===!1){var x=f.now(),y=d.url.replace(bO,"$1_="+x);d.url=y+(y===d.url?(bK.test(d.url)?"&":"?")+"_="+x:"")}}(d.data&&d.hasContent&&d.contentType!==!1||c.contentType)&&v.setRequestHeader("Content-Type",d.contentType),d.ifModified&&(k=k||d.url,f.lastModified[k]&&v.setRequestHeader("If-Modified-Since",f.lastModified[k]),f.etag[k]&&v.setRequestHeader("If-None-Match",f.etag[k])),v.setRequestHeader("Accept",d.dataTypes[0]&&d.accepts[d.dataTypes[0]]?d.accepts[d.dataTypes[0]]+(d.dataTypes[0]!=="*"?", */*; q=0.01":""):d.accepts["*"]);for(u in d.headers)v.setRequestHeader(u,d.headers[u]);if(d.beforeSend&&(d.beforeSend.call(e,v,d)===!1||s===2)){v.abort();return!1}for(u in{success:1,error:1,complete:1})v[u](d[u]);p=bX(bS,d,c,v);if(!p)w(-1,"No Transport");else{v.readyState=1,t&&g.trigger("ajaxSend",[v,d]),d.async&&d.timeout>0&&(q=setTimeout(function(){v.abort("timeout")},d.timeout));try{s=1,p.send(l,w)}catch(z){status<2?w(-1,z):f.error(z)}}return v},param:function(a,c){var d=[],e=function(a,b){b=f.isFunction(b)?b():b,d[d.length]=encodeURIComponent(a)+"="+encodeURIComponent(b)};c===b&&(c=f.ajaxSettings.traditional);if(f.isArray(a)||a.jquery&&!f.isPlainObject(a))f.each(a,function(){e(this.name,this.value)});else for(var g in a)bY(g,a[g],c,e);return d.join("&").replace(bB,"+")}}),f.extend({active:0,lastModified:{},etag:{}});var b_=f.now(),ca=/(\=)\?(&|$)|\?\?/i;f.ajaxSetup({jsonp:"callback",jsonpCallback:function(){return f.expando+"_"+b_++}}),f.ajaxPrefilter("json jsonp",function(b,c,d){var e=b.contentType==="application/x-www-form-urlencoded"&&typeof b.data=="string";if(b.dataTypes[0]==="jsonp"||b.jsonp!==!1&&(ca.test(b.url)||e&&ca.test(b.data))){var g,h=b.jsonpCallback=f.isFunction(b.jsonpCallback)?b.jsonpCallback():b.jsonpCallback,i=a[h],j=b.url,k=b.data,l="$1"+h+"$2";b.jsonp!==!1&&(j=j.replace(ca,l),b.url===j&&(e&&(k=k.replace(ca,l)),b.data===k&&(j+=(/\?/.test(j)?"&":"?")+b.jsonp+"="+h))),b.url=j,b.data=k,a[h]=function(a){g=[a]},d.always(function(){a[h]=i,g&&f.isFunction(i)&&a[h](g[0])}),b.converters["script json"]=function(){g||f.error(h+" was not called");return g[0]},b.dataTypes[0]="json";return"script"}}),f.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/javascript|ecmascript/},converters:{"text script":function(a){f.globalEval(a);return a}}}),f.ajaxPrefilter("script",function(a){a.cache===b&&(a.cache=!1),a.crossDomain&&(a.type="GET",a.global=!1)}),f.ajaxTransport("script",function(a){if(a.crossDomain){var d,e=c.head||c.getElementsByTagName("head")[0]||c.documentElement;return{send:function(f,g){d=c.createElement("script"),d.async="async",a.scriptCharset&&(d.charset=a.scriptCharset),d.src=a.url,d.onload=d.onreadystatechange=function(a,c){if(c||!d.readyState||/loaded|complete/.test(d.readyState))d.onload=d.onreadystatechange=null,e&&d.parentNode&&e.removeChild(d),d=b,c||g(200,"success")},e.insertBefore(d,e.firstChild)},abort:function(){d&&d.onload(0,1)}}}});var cb=a.ActiveXObject?function(){for(var a in cd)cd[a](0,1)}:!1,cc=0,cd;f.ajaxSettings.xhr=a.ActiveXObject?function(){return!this.isLocal&&ce()||cf()}:ce,function(a){f.extend(f.support,{ajax:!!a,cors:!!a&&"withCredentials"in a})}(f.ajaxSettings.xhr()),f.support.ajax&&f.ajaxTransport(function(c){if(!c.crossDomain||f.support.cors){var d;return{send:function(e,g){var h=c.xhr(),i,j;c.username?h.open(c.type,c.url,c.async,c.username,c.password):h.open(c.type,c.url,c.async);if(c.xhrFields)for(j in c.xhrFields)h[j]=c.xhrFields[j];c.mimeType&&h.overrideMimeType&&h.overrideMimeType(c.mimeType),!c.crossDomain&&!e["X-Requested-With"]&&(e["X-Requested-With"]="XMLHttpRequest");try{for(j in e)h.setRequestHeader(j,e[j])}catch(k){}h.send(c.hasContent&&c.data||null),d=function(a,e){var j,k,l,m,n;try{if(d&&(e||h.readyState===4)){d=b,i&&(h.onreadystatechange=f.noop,cb&&delete cd[i]);if(e)h.readyState!==4&&h.abort();else{j=h.status,l=h.getAllResponseHeaders(),m={},n=h.responseXML,n&&n.documentElement&&(m.xml=n),m.text=h.responseText;try{k=h.statusText}catch(o){k=""}!j&&c.isLocal&&!c.crossDomain?j=m.text?200:404:j===1223&&(j=204)}}}catch(p){e||g(-1,p)}m&&g(j,k,m,l)},!c.async||h.readyState===4?d():(i=++cc,cb&&(cd||(cd={},f(a).unload(cb)),cd[i]=d),h.onreadystatechange=d)},abort:function(){d&&d(0,1)}}}});var cg={},ch,ci,cj=/^(?:toggle|show|hide)$/,ck=/^([+\-]=)?([\d+.\-]+)([a-z%]*)$/i,cl,cm=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]],cn,co=a.webkitRequestAnimationFrame||a.mozRequestAnimationFrame||a.oRequestAnimationFrame;f.fn.extend({show:function(a,b,c){var d,e;if(a||a===0)return this.animate(cr("show",3),a,b,c);for(var g=0,h=this.length;g=e.duration+this.startTime){this.now=this.end,this.pos=this.state=1,this.update(),e.animatedProperties[this.prop]=!0;for(g in e.animatedProperties)e.animatedProperties[g]!==!0&&(c=!1);if(c){e.overflow!=null&&!f.support.shrinkWrapBlocks&&f.each(["","X","Y"],function(a,b){d.style["overflow"+b]=e.overflow[a]}),e.hide&&f(d).hide();if(e.hide||e.show)for(var i in e.animatedProperties)f.style(d,i,e.orig[i]);e.complete.call(d)}return!1}e.duration==Infinity?this.now=b:(h=b-this.startTime,this.state=h/e.duration,this.pos=f.easing[e.animatedProperties[this.prop]](this.state,h,0,1,e.duration),this.now=this.start+(this.end-this.start)*this.pos),this.update();return!0}},f.extend(f.fx,{tick:function(){for(var a=f.timers,b=0;b
    ";f.extend(b.style,{position:"absolute",top:0,left:0,margin:0,border:0,width:"1px",height:"1px",visibility:"hidden"}),b.innerHTML=j,a.insertBefore(b,a.firstChild),d=b.firstChild,e=d.firstChild,h=d.nextSibling.firstChild.firstChild,this.doesNotAddBorder=e.offsetTop!==5,this.doesAddBorderForTableAndCells=h.offsetTop===5,e.style.position="fixed",e.style.top="20px",this.supportsFixedPosition=e.offsetTop===20||e.offsetTop===15,e.style.position=e.style.top="",d.style.overflow="hidden",d.style.position="relative",this.subtractsBorderForOverflowNotVisible=e.offsetTop===-5,this.doesNotIncludeMarginInBodyOffset=a.offsetTop!==i,a.removeChild(b),f.offset.initialize=f.noop},bodyOffset:function(a){var b=a.offsetTop,c=a.offsetLeft;f.offset.initialize(),f.offset.doesNotIncludeMarginInBodyOffset&&(b+=parseFloat(f.css(a,"marginTop"))||0,c+=parseFloat(f.css(a,"marginLeft"))||0);return{top:b,left:c}},setOffset:function(a,b,c){var d=f.css(a,"position");d==="static"&&(a.style.position="relative");var e=f(a),g=e.offset(),h=f.css(a,"top"),i=f.css(a,"left"),j=(d==="absolute"||d==="fixed")&&f.inArray("auto",[h,i])>-1,k={},l={},m,n;j?(l=e.position(),m=l.top,n=l.left):(m=parseFloat(h)||0,n=parseFloat(i)||0),f.isFunction(b)&&(b=b.call(a,c,g)),b.top!=null&&(k.top=b.top-g.top+m),b.left!=null&&(k.left=b.left-g.left+n),"using"in b?b.using.call(a,k):e.css(k)}},f.fn.extend({position:function(){if(!this[0])return null;var a=this[0],b=this.offsetParent(),c=this.offset(),d=cu.test(b[0].nodeName)?{top:0,left:0}:b.offset();c.top-=parseFloat(f.css(a,"marginTop"))||0,c.left-=parseFloat(f.css(a,"marginLeft"))||0,d.top+=parseFloat(f.css(b[0],"borderTopWidth"))||0,d.left+=parseFloat(f.css(b[0],"borderLeftWidth"))||0;return{top:c.top-d.top,left:c.left-d.left}},offsetParent:function(){return this.map(function(){var a=this.offsetParent||c.body;while(a&&!cu.test(a.nodeName)&&f.css(a,"position")==="static")a=a.offsetParent;return a})}}),f.each(["Left","Top"],function(a,c){var d="scroll"+c;f.fn[d]=function(c){var e,g;if(c===b){e=this[0];if(!e)return null;g=cv(e);return g?"pageXOffset"in g?g[a?"pageYOffset":"pageXOffset"]:f.support.boxModel&&g.document.documentElement[d]||g.document.body[d]:e[d]}return this.each(function(){g=cv(this),g?g.scrollTo(a?f(g).scrollLeft():c,a?c:f(g).scrollTop()):this[d]=c})}}),f.each(["Height","Width"],function(a,c){var d=c.toLowerCase();f.fn["inner"+c]=function(){var a=this[0];return a&&a.style?parseFloat(f.css(a,d,"padding")):null},f.fn["outer"+c]=function(a){var b=this[0];return b&&b.style?parseFloat(f.css(b,d,a?"margin":"border")):null},f.fn[d]=function(a){var e=this[0];if(!e)return a==null?null:this;if(f.isFunction(a))return this.each(function(b){var c=f(this);c[d](a.call(this,b,c[d]()))});if(f.isWindow(e)){var g=e.document.documentElement["client"+c];return e.document.compatMode==="CSS1Compat"&&g||e.document.body["client"+c]||g}if(e.nodeType===9)return Math.max(e.documentElement["client"+c],e.body["scroll"+c],e.documentElement["scroll"+c],e.body["offset"+c],e.documentElement["offset"+c]);if(a===b){var h=f.css(e,d),i=parseFloat(h);return f.isNaN(i)?h:i}return this.css(d,typeof a=="string"?a:a+"px")}}),a.jQuery=a.$=f})(window); \ No newline at end of file diff --git a/gruntfile.js b/gruntfile.js deleted file mode 100755 index 7deb42905..000000000 --- a/gruntfile.js +++ /dev/null @@ -1,47 +0,0 @@ -module.exports = function (grunt) { - grunt.initConfig({ - pkg: grunt.file.readJSON('package.json'), - projectFiles: ['src/litegraph.js', - 'src/nodes/base.js', - 'src/nodes/events.js', - 'src/nodes/interface.js', - 'src/nodes/input.js', - 'src/nodes/math.js', - 'src/nodes/logic.js', - 'src/nodes/image.js', - 'src/nodes/gltextures.js', - 'src/nodes/glfx.js', - 'src/nodes/midi.js', - 'src/nodes/audio.js', - 'src/nodes/network.js' - ], - concat: { - build: { - src: '<%= projectFiles %>', - dest: 'build/litegraph.js' - } - }, - closureCompiler: { - - options: { - compilerFile: 'node_modules/google-closure-compiler/compiler.jar', - compilerOpts: { - formatting: 'pretty_print', - warning_level: 'default' - }, - d32: false, // will use 'java -client -d32 -jar compiler.jar' - TieredCompilation: false// will use 'java -server -XX:+TieredCompilation -jar compiler.jar', - // ,output_wrapper: '"var LiteGraph = (function(){%output% return LiteGraph;}).call(this);"' //* Make container for all - }, - targetName: { - src: '<%= projectFiles %>', - dest: 'build/litegraph.min.js' - } - } - }) - - grunt.loadNpmTasks('grunt-contrib-concat') - grunt.loadNpmTasks('grunt-closure-tools') - - grunt.registerTask('build', ['concat:build', 'closureCompiler']) -} diff --git a/guides/README.md b/guides/README.md deleted file mode 100755 index e7875d260..000000000 --- a/guides/README.md +++ /dev/null @@ -1,344 +0,0 @@ -# LiteGraph - -Here is a list of useful info when working with LiteGraph. -The library is divided in four levels: -* **LGraphNode**: the base class of a node (this library uses is own system of inheritance) -* **LGraph**: the container of a whole graph made of nodes -* **LGraphCanvas**: the class in charge of rendering/interaction with the nodes inside the browser. - -And in ```the src/``` folder there is also another class included: -* **LiteGraph.Editor**: A wrapper around LGraphCanvas that adds buttons around it. - -## 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 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(...)```. - -Here is an example of how to create your own node: - -```javascript -//your node constructor class -function MyAddNode() -{ - //add some input slots - this.addInput("A","number"); - this.addInput("B","number"); - //add some output slots - this.addOutput("A+B","number"); - //add some properties - this.properties = { precision: 1 }; -} - -//name to show on the canvas -MyAddNode.title = "Sum"; - -//function to call when the node is executed -MyAddNode.prototype.onExecute = function() -{ - //retrieve data from inputs - var A = this.getInputData(0); - if( A === undefined ) - A = 0; - var B = this.getInputData(1); - if( B === undefined ) - B = 0; - //assing data to outputs - this.setOutputData( 0, A + B ); -} - -//register in the system -LiteGraph.registerNodeType("basic/sum", MyAddNode ); - -``` - - -## Node settings - -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_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 -* **onRemoved**: called when removed from graph -* **onStart**: called when the graph starts playing -* **onStop**: called when the graph stops playing -* **onDrawBackground**: render custom node content on canvas (not visible in Live mode) -* **onDrawForeground**: render custom node content on canvas (on top of slots) -* **onMouseDown,onMouseMove,onMouseUp,onMouseEnter,onMouseLeave** to catch mouse events -* **onDblClick**: double clicked in the editor -* **onExecute**: called when it is time to execute the node -* **onPropertyChanged**: when a property is changed in the panel (return true to skip default behaviour) -* **onGetInputs**: returns an array of possible inputs in the form of [ ["name","type"], [...], [...] ] -* **onGetOutputs**: returns an array of possible outputs -* **onSerialize**: before serializing, receives an object where to store data -* **onSelected**: selected in the editor, receives an object where to read data -* **onDeselected**: deselected from the editor -* **onDropItem**: DOM item dropped over the node -* **onDropFile**: file dropped over the node -* **onConnectInput**: if returns false the incoming connection will be canceled -* **onConnectionsChange**: a connection changed (new one or removed) (LiteGraph.INPUT or LiteGraph.OUTPUT, slot, true if connected, link_info, input_info ) - - -### Node slots - -Every node could have several slots, stored in node.inputs and node.outputs. - -You can add new slots by calling node.addInput or node.addOutput - -The main difference between inputs and outputs is that an input can only have one connection link while outputs could have several. - -To get information about an slot you can access node.inputs[ slot_index ] or node.outputs[ slot_index ] - -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 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 - * **color_off**: color to render when it is not connected - - To retrieve the data traveling through a link you can call ```node.getInputData``` or ```node.getOutputData``` - -### Define your Graph Node - -When creating a class for a graph node here are some useful points: - -- The constructor should create the default inputs and outputs (use ```addInput``` and ```addOutput```) -- Properties that can be edited are stored in ```this.properties = {};``` -- the ```onExecute``` is the method that will be called when the graph is executed -- you can catch if a property was changed defining a ```onPropertyChanged``` -- you must register your node using ```LiteGraph.registerNodeType("type/name", MyGraphNodeClass );``` -- you can alter the default priority of execution by defining the ```MyGraphNodeClass.priority``` (default is 0) -- you can overwrite how the node is rendered using the ```onDrawBackground``` and ```onDrawForeground``` - -### Custom Node Appearance - -You can configure the node shape or the title color if you want it to be different from the body color: -```js -MyNodeClass.title_color = "#345"; -MyNodeClass.shape = LiteGraph.ROUND_SHAPE; -``` - -You can draw something inside a node using the callbacks ```onDrawForeground``` and ```onDrawBackground```. The only difference is that onDrawForeground gets called in Live Mode and onDrawBackground not. - -Both functions receive the [Canvas2D rendering context](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D) and the LGraphCanvas instance where the node is being rendered. - -You do not have to worry about the coordinates system, (0,0) is the top-left corner of the node content area (not the title). - -```js -node.onDrawForeground = function(ctx, graphcanvas) -{ - if(this.flags.collapsed) - return; - ctx.save(); - ctx.fillColor = "black"; - ctx.fillRect(0,0,10,this.size[1]); - ctx.restore(); -} -``` - -### Custom Node Behaviour - -You can also grab events from the mouse in case your node has some sort of special interactivity. - -The second parameter is the position in node coordinates, where 0,0 represents the top-left corner of the node content (below the title). - -```js -node.onMouseDown = function( event, pos, graphcanvas ) -{ - return true; //return true is the event was used by your node, to block other behaviours -} -``` - -Other methods are: -- onMouseMove -- onMouseUp -- onMouseEnter -- onMouseLeave -- onKey - -### Node Widgets - -You can add widgets inside the node to edit text, values, etc. - -To do so you must create them in the constructor by calling ```node.addWidget```, the returned value is the object containing all the info about the widget, it is handy to store it in case you want to change the value later from code. - -The syntax is: - -```js -function MyNodeType() -{ - this.slider_widget = this.addWidget("slider","Slider", 0.5, function(value, widget, node){ /* do something with the value */ }, { min: 0, max: 1} ); -} -``` - -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, precision: 3 } );``` -* **"slider"** to change a number by dragging 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"]} );``` - - 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"** - -The fourth optional parameter could be options for the widget, the parameters accepted are: -* **property**: specifies the name of a property to modify when the widget changes -* **min**: min value -* **max**: max value -* **precision**: set the number of digits after decimal point -* **callback**: function to call when the value changes. - -Widget's value is not serialized by default when storing the node state, but if you want to store the value of widgets just set serialize_widgets to true: - -```js -function MyNode() -{ - this.addWidget("text","name",""); - this.serialize_widgets = true; -} -``` - -Or if you want to associate a widget with a property of the node, then specify it in the options: - -```js -function MyNode() -{ - this.properties = { surname: "smith" }; - this.addWidget("text","Surname","", { property: "surname"}); //this will modify the node.properties -} -``` -## LGraphCanvas -LGraphCanvas is the class in charge of rendering/interaction with the nodes inside the browser. - -## LGraphCanvas settings -There are graph canvas settings that could be defined or modified to change behaviour: - -* **allow_interaction**: when set to `false` disable interaction with the canvas (`flags.allow_interaction` on node can be used to override graph canvas setting) - -### Canvas Shortcuts -* Space - Holding space key while moving the cursor moves the canvas around. It works when holding the mouse button down so it is easier to connect different nodes when the canvas gets too large. -* Ctrl/Shift + Click - Add clicked node to selection. -* Ctrl + A - Select all nodes -* Ctrl + C/Ctrl + V - Copy and paste selected nodes, without maintaining the connection to the outputs of unselected nodes. -* Ctrl + C/Ctrl + Shift + V - Copy and paste selected nodes, and maintaining the connection from the outputs of unselected nodes to the inputs of the newly pasted nodes. -* Holding Shift and drag selected nodes - Move multiple selected nodes at the same time. - -# Execution Flow -To execute a graph you must call ```graph.runStep()```. - -This function will call the method ```node.onExecute()``` for every node in the graph. - -The order of execution is determined by the system according to the morphology of the graph (nodes without inputs are considered level 0, then nodes connected to nodes of level 0 are level 1, and so on). This order is computed only when the graph morphology changes (new nodes are created, connections change). - -It is up to the developer to decide how to handle inputs and outputs from inside the node. - -The data send through outputs using ```this.setOutputData(0,data)``` is stored in the link, so if the node connected through that link does ```this.getInputData(0)``` it will receive the same data sent. - -For rendering, the nodes are executed according to their order in the ```graph._nodes``` array, which changes when the user interact with the GraphCanvas (clicked nodes are moved to the back of the array so they are rendered the last). - - -## Integration - -To integrate in you HTML application: - -```js -var graph = new LiteGraph.LGraph(); -var graph_canvas = new LiteGraph.LGraphCanvas( canvas, graph ); -``` - -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. - - -### Customising Link Tooltips - -When hovering over a link that connects two nodes together, a tooltip will be shown allowing the user to see the data that is being output from one node to the other. - -Sometimes, you may have a node that outputs an object, rather than a primitive value that can be easily represented (like a string). In these instances, the tooltip will default to showing `[Object]`. - -If you need a more descriptive tooltip, you can achieve this by adding a `toToolTip` function to your object which returns the text you wish to display in the tooltip. - -For example, to ensure the link from output slot 0 shows `A useful description`, the output object would look like this: - -```javascript -this.setOutputData(0, { - complexObject: { - yes: true, - }, - toToolTip: () => 'A useful description', -}); -``` - - - - - - - - - - diff --git a/index.html b/index.html deleted file mode 100755 index f46847f37..000000000 --- a/index.html +++ /dev/null @@ -1,86 +0,0 @@ - - - - - - -litegraph.js - - - - - - - - -
    -
    -
    -

    litegraph.js

    -

    Litegraph.js is a library that allows to create modular graphs on the web, similar to PureData.

    -

    Graphs can be used to create workflows, image processing, audio, or any kind of network of modules interacting with each other.

    -

    Some of the main features:

    -
      -
    • Automatic sorting of modules according to basic rules.
    • -
    • Dynamic number of input/outputs per module.
    • -
    • Persistence, graphs can be serialized in a JSON.
    • -
    • Optimized render in a HTML5 Canvas (supports hundres of modules on the screen).
    • -
    • Allows to run the graphs without the need of the canvas (standalone mode).
    • -
    • Simple API. It is very easy to create new modules.
    • -
    • Edit and Live mode, (in live mode only modules with widgets are rendered.
    • -
    • Contextual menu in the editor.
    • -
    - -

    Usage

    - -

    Include the library

    -
    -<script type="text/javascript" src="../src/litegraph.js"></script>
    - -

    Create a graph

    -
    -var graph = new LGraph();
    - -

    Create a canvas renderer

    -
    -var canvas = new LGraphCanvas("#mycanvas");
    - -

    Add some nodes to the graph

    -
    -var node_const = LiteGraph.createNode("basic/const");
    -node_const.pos = [200,200];
    -graph.add(node_const);
    -node_const.setValue(4.5);
    -
    -var node_watch = LiteGraph.createNode("basic/watch");
    -node_watch.pos = [700,200];
    -graph.add(node_watch);
    - -

    Connect them

    -
    -node_const.connect(0, node_watch, 0 );
    - -

    Run the graph

    -
    -graph.start();
    - -

    Example of editor

    - - -

    Documentation

    -

    Here you can check automatically generated documentation.

    - -
    -
    -
    - - - - diff --git a/nodes_data/imgs/icon-sin.png b/nodes_data/imgs/icon-sin.png deleted file mode 100755 index 06751eafc..000000000 Binary files a/nodes_data/imgs/icon-sin.png and /dev/null differ diff --git a/nodes_data/imgs/knob2_bg.png b/nodes_data/imgs/knob2_bg.png deleted file mode 100755 index bc190eacc..000000000 Binary files a/nodes_data/imgs/knob2_bg.png and /dev/null differ diff --git a/nodes_data/imgs/knob2_fg.png b/nodes_data/imgs/knob2_fg.png deleted file mode 100755 index 8b84162c8..000000000 Binary files a/nodes_data/imgs/knob2_fg.png and /dev/null differ diff --git a/nodes_data/imgs/knob_bg.png b/nodes_data/imgs/knob_bg.png deleted file mode 100755 index fe0169252..000000000 Binary files a/nodes_data/imgs/knob_bg.png and /dev/null differ diff --git a/nodes_data/imgs/knob_fg.png b/nodes_data/imgs/knob_fg.png deleted file mode 100755 index 4a8d6d6f9..000000000 Binary files a/nodes_data/imgs/knob_fg.png and /dev/null differ diff --git a/nodes_data/imgs/slider_fg.png b/nodes_data/imgs/slider_fg.png deleted file mode 100755 index 5d9f3cec1..000000000 Binary files a/nodes_data/imgs/slider_fg.png and /dev/null differ diff --git a/package.json b/package.json index 91181e351..0cb04028e 100755 --- a/package.json +++ b/package.json @@ -23,8 +23,6 @@ "build": "tsc && vite build", "dev": "vite", "preview": "vite preview", - "deprecated-prebuild": "rimraf build", - "deprecated-start": "nodemon utils/server.js", "deprecated-test": "jest", "deprecated-test:allVersions": "./utils/test.sh", "deprecated-prettier": "npx prettier --write src/**/*.* css/**/*.*", diff --git a/src/litegraph-editor.js b/src/litegraph-editor.js deleted file mode 100755 index d47ad6f42..000000000 --- a/src/litegraph-editor.js +++ /dev/null @@ -1,283 +0,0 @@ -//Creates an interface to access extra features from a graph (like play, stop, live, etc) -function Editor(container_id, options) { - options = options || {}; - - //fill container - var html = "
    "; - html += "
    "; - html += ""; - - var root = document.createElement("div"); - this.root = root; - 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 = this.canvas = root.querySelector(".graphcanvas"); - - //create graph - var graph = (this.graph = new LGraph()); - var graphcanvas = this.graphcanvas = new LGraphCanvas(canvas, graph); - graphcanvas.background_image = "imgs/grid.png"; - graph.onAfterExecute = function() { - graphcanvas.draw(true); - }; - - 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" ); - this.addLoadCounter(); - this.addToolsButton( - "playnode_button", - "Play", - "imgs/icon-play.png", - this.onPlayButton.bind(this), - ".tools-right" - ); - this.addToolsButton( - "playstepnode_button", - "Step", - "imgs/icon-playstep.png", - this.onPlayStepButton.bind(this), - ".tools-right" - ); - - if (!options.skip_livemode) { - this.addToolsButton( - "livemode_button", - "Live", - "imgs/icon-record.png", - this.onLiveButton.bind(this), - ".tools-right" - ); - } - if (!options.skip_maximize) { - this.addToolsButton( - "maximize_button", - "", - "imgs/icon-maximize.png", - this.onFullscreenButton.bind(this), - ".tools-right" - ); - } - if (options.miniwindow) { - this.addMiniWindow(300, 200); - } - - //append to DOM - var parent = document.getElementById(container_id); - if (parent) { - parent.appendChild(root); - } - - graphcanvas.resize(); - //graphcanvas.draw(true,true); -} - -Editor.prototype.addLoadCounter = function() { - var meter = document.createElement("div"); - meter.className = "headerpanel loadmeter toolbar-widget"; - - var html = - "
    CPU
    "; - html += - "
    GFX
    "; - - meter.innerHTML = html; - this.root.querySelector(".header .tools-left").appendChild(meter); - var self = this; - - setInterval(function() { - meter.querySelector(".cpuload .fgload").style.width = - 2 * self.graph.execution_time * 90 + "px"; - if (self.graph.status == LGraph.STATUS_RUNNING) { - meter.querySelector(".gpuload .fgload").style.width = - self.graphcanvas.render_time * 10 * 90 + "px"; - } else { - meter.querySelector(".gpuload .fgload").style.width = 4 + "px"; - } - }, 200); -}; - -Editor.prototype.addToolsButton = function( id, name, icon_url, callback, container ) { - if (!container) { - container = ".tools"; - } - - var button = this.createButton(name, icon_url, callback); - button.id = id; - this.root.querySelector(container).appendChild(button); -}; - -Editor.prototype.createButton = function(name, icon_url, callback) { - var button = document.createElement("button"); - if (icon_url) { - button.innerHTML = " "; - } - button.classList.add("btn"); - button.innerHTML += name; - if(callback) - button.addEventListener("click", callback ); - return button; -}; - -Editor.prototype.onLoadButton = function() { - var panel = this.graphcanvas.createPanel("Load session",{closable:true}); - //TO DO - - this.root.appendChild(panel); -}; - -Editor.prototype.onSaveButton = function() {}; - -Editor.prototype.onPlayButton = function() { - var graph = this.graph; - var button = this.root.querySelector("#playnode_button"); - - if (graph.status == LGraph.STATUS_STOPPED) { - button.innerHTML = " Stop"; - graph.start(); - } else { - button.innerHTML = " Play"; - graph.stop(); - } -}; - -Editor.prototype.onPlayStepButton = function() { - var graph = this.graph; - graph.runStep(1); - this.graphcanvas.draw(true, true); -}; - -Editor.prototype.onLiveButton = function() { - var is_live_mode = !this.graphcanvas.live_mode; - this.graphcanvas.switchLiveMode(true); - this.graphcanvas.draw(); - var url = this.graphcanvas.live_mode - ? "imgs/gauss_bg_medium.jpg" - : "imgs/gauss_bg.jpg"; - var button = this.root.querySelector("#livemode_button"); - button.innerHTML = !is_live_mode - ? " Live" - : " Edit"; -}; - -Editor.prototype.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); - } else if (this.root.mozRequestFullscreen) { - this.root.requestFullscreen(Element.ALLOW_KEYBOARD_INPUT); - } else if (this.root.webkitRequestFullscreen) { - this.root.webkitRequestFullscreen(Element.ALLOW_KEYBOARD_INPUT); - } else { - throw "Fullscreen not supported"; - } - - var self = this; - setTimeout(function() { - self.graphcanvas.resize(); - }, 100); -}; - -Editor.prototype.onFullscreenButton = function() { - this.goFullscreen(); -}; - -Editor.prototype.addMiniWindow = function(w, h) { - var miniwindow = document.createElement("div"); - miniwindow.className = "litegraph miniwindow"; - miniwindow.innerHTML = - ""; - var canvas = miniwindow.querySelector("canvas"); - var that = this; - - var graphcanvas = new LGraphCanvas( canvas, this.graph ); - graphcanvas.show_info = false; - graphcanvas.background_image = "imgs/grid.png"; - graphcanvas.scale = 0.25; - graphcanvas.allow_dragnodes = false; - graphcanvas.allow_interaction = false; - graphcanvas.render_shadows = false; - graphcanvas.max_zoom = 0.25; - this.miniwindow_graphcanvas = graphcanvas; - graphcanvas.onClear = function() { - graphcanvas.scale = 0.25; - graphcanvas.allow_dragnodes = false; - graphcanvas.allow_interaction = false; - }; - graphcanvas.onRenderBackground = function(canvas, ctx) { - ctx.strokeStyle = "#567"; - var tl = that.graphcanvas.convertOffsetToCanvas([0, 0]); - var br = that.graphcanvas.convertOffsetToCanvas([ - that.graphcanvas.canvas.width, - that.graphcanvas.canvas.height - ]); - tl = this.convertCanvasToOffset(tl); - br = this.convertCanvasToOffset(br); - ctx.lineWidth = 1; - ctx.strokeRect( - Math.floor(tl[0]) + 0.5, - Math.floor(tl[1]) + 0.5, - Math.floor(br[0] - tl[0]), - Math.floor(br[1] - tl[1]) - ); - }; - - miniwindow.style.position = "absolute"; - miniwindow.style.top = "4px"; - miniwindow.style.right = "4px"; - - var close_button = document.createElement("div"); - close_button.className = "corner-button"; - close_button.innerHTML = "❌"; - close_button.addEventListener("click", function(e) { - graphcanvas.setGraph(null); - miniwindow.parentNode.removeChild(miniwindow); - }); - miniwindow.appendChild(close_button); - - this.root.querySelector(".content").appendChild(miniwindow); -}; - -Editor.prototype.addMultiview = function() -{ - var canvas = this.canvas; - this.graphcanvas.ctx.fillStyle = "black"; - this.graphcanvas.ctx.fillRect(0,0,canvas.width,canvas.height); - this.graphcanvas.viewport = [0,0,canvas.width*0.5-2,canvas.height]; - - var graphcanvas = new LGraphCanvas( canvas, this.graph ); - graphcanvas.background_image = "imgs/grid.png"; - this.graphcanvas2 = graphcanvas; - this.graphcanvas2.viewport = [canvas.width*0.5,0,canvas.width*0.5,canvas.height]; -} - -LiteGraph.Editor = Editor; diff --git a/src/nodes/audio.js b/src/nodes/audio.js deleted file mode 100755 index 2c02bdf33..000000000 --- a/src/nodes/audio.js +++ /dev/null @@ -1,1456 +0,0 @@ -(function(global) { - var LiteGraph = global.LiteGraph; - - var LGAudio = {}; - global.LGAudio = LGAudio; - - LGAudio.getAudioContext = function() { - if (!this._audio_context) { - window.AudioContext = - window.AudioContext || window.webkitAudioContext; - if (!window.AudioContext) { - console.error("AudioContext not supported by browser"); - return null; - } - this._audio_context = new AudioContext(); - this._audio_context.onmessage = function(msg) { - console.log("msg", msg); - }; - this._audio_context.onended = function(msg) { - console.log("ended", msg); - }; - this._audio_context.oncomplete = function(msg) { - console.log("complete", msg); - }; - } - - //in case it crashes - //if(this._audio_context.state == "suspended") - // this._audio_context.resume(); - return this._audio_context; - }; - - LGAudio.connect = function(audionodeA, audionodeB) { - try { - audionodeA.connect(audionodeB); - } catch (err) { - console.warn("LGraphAudio:", err); - } - }; - - LGAudio.disconnect = function(audionodeA, audionodeB) { - try { - audionodeA.disconnect(audionodeB); - } catch (err) { - console.warn("LGraphAudio:", err); - } - }; - - LGAudio.changeAllAudiosConnections = function(node, connect) { - if (node.inputs) { - for (var i = 0; i < node.inputs.length; ++i) { - var input = node.inputs[i]; - var link_info = node.graph.links[input.link]; - if (!link_info) { - continue; - } - - var origin_node = node.graph.getNodeById(link_info.origin_id); - var origin_audionode = null; - if (origin_node.getAudioNodeInOutputSlot) { - origin_audionode = origin_node.getAudioNodeInOutputSlot( - link_info.origin_slot - ); - } else { - origin_audionode = origin_node.audionode; - } - - var target_audionode = null; - if (node.getAudioNodeInInputSlot) { - target_audionode = node.getAudioNodeInInputSlot(i); - } else { - target_audionode = node.audionode; - } - - if (connect) { - LGAudio.connect(origin_audionode, target_audionode); - } else { - LGAudio.disconnect(origin_audionode, target_audionode); - } - } - } - - if (node.outputs) { - for (var i = 0; i < node.outputs.length; ++i) { - var output = node.outputs[i]; - for (var j = 0; j < output.links.length; ++j) { - var link_info = node.graph.links[output.links[j]]; - if (!link_info) { - continue; - } - - var origin_audionode = null; - if (node.getAudioNodeInOutputSlot) { - origin_audionode = node.getAudioNodeInOutputSlot(i); - } else { - origin_audionode = node.audionode; - } - - var target_node = node.graph.getNodeById( - link_info.target_id - ); - var target_audionode = null; - if (target_node.getAudioNodeInInputSlot) { - target_audionode = target_node.getAudioNodeInInputSlot( - link_info.target_slot - ); - } else { - target_audionode = target_node.audionode; - } - - if (connect) { - LGAudio.connect(origin_audionode, target_audionode); - } else { - LGAudio.disconnect(origin_audionode, target_audionode); - } - } - } - } - }; - - //used by many nodes - LGAudio.onConnectionsChange = function( - connection, - slot, - connected, - link_info - ) { - //only process the outputs events - if (connection != LiteGraph.OUTPUT) { - return; - } - - var target_node = null; - if (link_info) { - target_node = this.graph.getNodeById(link_info.target_id); - } - - if (!target_node) { - return; - } - - //get origin audionode - var local_audionode = null; - if (this.getAudioNodeInOutputSlot) { - local_audionode = this.getAudioNodeInOutputSlot(slot); - } else { - local_audionode = this.audionode; - } - - //get target audionode - var target_audionode = null; - if (target_node.getAudioNodeInInputSlot) { - target_audionode = target_node.getAudioNodeInInputSlot( - link_info.target_slot - ); - } else { - target_audionode = target_node.audionode; - } - - //do the connection/disconnection - if (connected) { - LGAudio.connect(local_audionode, target_audionode); - } else { - LGAudio.disconnect(local_audionode, target_audionode); - } - }; - - //this function helps creating wrappers to existing classes - LGAudio.createAudioNodeWrapper = function(class_object) { - var old_func = class_object.prototype.onPropertyChanged; - - class_object.prototype.onPropertyChanged = function(name, value) { - if (old_func) { - old_func.call(this, name, value); - } - - if (!this.audionode) { - return; - } - - if (this.audionode[name] === undefined) { - return; - } - - if (this.audionode[name].value !== undefined) { - this.audionode[name].value = value; - } else { - this.audionode[name] = value; - } - }; - - class_object.prototype.onConnectionsChange = - LGAudio.onConnectionsChange; - }; - - //contains the samples decoded of the loaded audios in AudioBuffer format - LGAudio.cached_audios = {}; - - LGAudio.loadSound = function(url, on_complete, on_error) { - if (LGAudio.cached_audios[url] && url.indexOf("blob:") == -1) { - if (on_complete) { - on_complete(LGAudio.cached_audios[url]); - } - return; - } - - if (LGAudio.onProcessAudioURL) { - url = LGAudio.onProcessAudioURL(url); - } - - //load new sample - var request = new XMLHttpRequest(); - request.open("GET", url, true); - request.responseType = "arraybuffer"; - - var context = LGAudio.getAudioContext(); - - // Decode asynchronously - request.onload = function() { - console.log("AudioSource loaded"); - context.decodeAudioData( - request.response, - function(buffer) { - console.log("AudioSource decoded"); - LGAudio.cached_audios[url] = buffer; - if (on_complete) { - on_complete(buffer); - } - }, - onError - ); - }; - request.send(); - - function onError(err) { - console.log("Audio loading sample error:", err); - if (on_error) { - on_error(err); - } - } - - return request; - }; - - //**************************************************** - - function LGAudioSource() { - this.properties = { - src: "", - gain: 0.5, - loop: true, - autoplay: true, - playbackRate: 1 - }; - - this._loading_audio = false; - this._audiobuffer = null; //points to AudioBuffer with the audio samples decoded - this._audionodes = []; - this._last_sourcenode = null; //the last AudioBufferSourceNode (there could be more if there are several sounds playing) - - this.addOutput("out", "audio"); - this.addInput("gain", "number"); - - //init context - var context = LGAudio.getAudioContext(); - - //create gain node to control volume - this.audionode = context.createGain(); - this.audionode.graphnode = this; - this.audionode.gain.value = this.properties.gain; - - //debug - if (this.properties.src) { - this.loadSound(this.properties.src); - } - } - - LGAudioSource.desc = "Plays an audio file"; - LGAudioSource["@src"] = { widget: "resource" }; - LGAudioSource.supported_extensions = ["wav", "ogg", "mp3"]; - - LGAudioSource.prototype.onAdded = function(graph) { - if (graph.status === LGraph.STATUS_RUNNING) { - this.onStart(); - } - }; - - LGAudioSource.prototype.onStart = function() { - if (!this._audiobuffer) { - return; - } - - if (this.properties.autoplay) { - this.playBuffer(this._audiobuffer); - } - }; - - LGAudioSource.prototype.onStop = function() { - this.stopAllSounds(); - }; - - LGAudioSource.prototype.onPause = function() { - this.pauseAllSounds(); - }; - - LGAudioSource.prototype.onUnpause = function() { - this.unpauseAllSounds(); - //this.onStart(); - }; - - LGAudioSource.prototype.onRemoved = function() { - this.stopAllSounds(); - if (this._dropped_url) { - URL.revokeObjectURL(this._url); - } - }; - - LGAudioSource.prototype.stopAllSounds = function() { - //iterate and stop - for (var i = 0; i < this._audionodes.length; ++i) { - if (this._audionodes[i].started) { - this._audionodes[i].started = false; - this._audionodes[i].stop(); - } - //this._audionodes[i].disconnect( this.audionode ); - } - this._audionodes.length = 0; - }; - - LGAudioSource.prototype.pauseAllSounds = function() { - LGAudio.getAudioContext().suspend(); - }; - - LGAudioSource.prototype.unpauseAllSounds = function() { - LGAudio.getAudioContext().resume(); - }; - - LGAudioSource.prototype.onExecute = function() { - if (this.inputs) { - for (var i = 0; i < this.inputs.length; ++i) { - var input = this.inputs[i]; - if (input.link == null) { - continue; - } - var v = this.getInputData(i); - if (v === undefined) { - continue; - } - if (input.name == "gain") - this.audionode.gain.value = v; - else if (input.name == "src") { - this.setProperty("src",v); - } else if (input.name == "playbackRate") { - this.properties.playbackRate = v; - for (var j = 0; j < this._audionodes.length; ++j) { - this._audionodes[j].playbackRate.value = v; - } - } - } - } - - if (this.outputs) { - for (var i = 0; i < this.outputs.length; ++i) { - var output = this.outputs[i]; - if (output.name == "buffer" && this._audiobuffer) { - this.setOutputData(i, this._audiobuffer); - } - } - } - }; - - LGAudioSource.prototype.onAction = function(event) { - if (this._audiobuffer) { - if (event == "Play") { - this.playBuffer(this._audiobuffer); - } else if (event == "Stop") { - this.stopAllSounds(); - } - } - }; - - LGAudioSource.prototype.onPropertyChanged = function(name, value) { - if (name == "src") { - this.loadSound(value); - } else if (name == "gain") { - this.audionode.gain.value = value; - } else if (name == "playbackRate") { - for (var j = 0; j < this._audionodes.length; ++j) { - this._audionodes[j].playbackRate.value = value; - } - } - }; - - LGAudioSource.prototype.playBuffer = function(buffer) { - var that = this; - var context = LGAudio.getAudioContext(); - - //create a new audionode (this is mandatory, AudioAPI doesnt like to reuse old ones) - var audionode = context.createBufferSource(); //create a AudioBufferSourceNode - this._last_sourcenode = audionode; - audionode.graphnode = this; - audionode.buffer = buffer; - audionode.loop = this.properties.loop; - audionode.playbackRate.value = this.properties.playbackRate; - this._audionodes.push(audionode); - audionode.connect(this.audionode); //connect to gain - - this._audionodes.push(audionode); - - this.trigger("start"); - - audionode.onended = function() { - //console.log("ended!"); - that.trigger("ended"); - //remove - var index = that._audionodes.indexOf(audionode); - if (index != -1) { - that._audionodes.splice(index, 1); - } - }; - - if (!audionode.started) { - audionode.started = true; - audionode.start(); - } - return audionode; - }; - - LGAudioSource.prototype.loadSound = function(url) { - var that = this; - - //kill previous load - if (this._request) { - this._request.abort(); - this._request = null; - } - - this._audiobuffer = null; //points to the audiobuffer once the audio is loaded - this._loading_audio = false; - - if (!url) { - return; - } - - this._request = LGAudio.loadSound(url, inner); - - this._loading_audio = true; - this.boxcolor = "#AA4"; - - function inner(buffer) { - this.boxcolor = LiteGraph.NODE_DEFAULT_BOXCOLOR; - that._audiobuffer = buffer; - that._loading_audio = false; - //if is playing, then play it - if (that.graph && that.graph.status === LGraph.STATUS_RUNNING) { - that.onStart(); - } //this controls the autoplay already - } - }; - - //Helps connect/disconnect AudioNodes when new connections are made in the node - LGAudioSource.prototype.onConnectionsChange = LGAudio.onConnectionsChange; - - LGAudioSource.prototype.onGetInputs = function() { - return [ - ["playbackRate", "number"], - ["src","string"], - ["Play", LiteGraph.ACTION], - ["Stop", LiteGraph.ACTION] - ]; - }; - - LGAudioSource.prototype.onGetOutputs = function() { - return [["buffer", "audiobuffer"], ["start", LiteGraph.EVENT], ["ended", LiteGraph.EVENT]]; - }; - - LGAudioSource.prototype.onDropFile = function(file) { - if (this._dropped_url) { - URL.revokeObjectURL(this._dropped_url); - } - var url = URL.createObjectURL(file); - this.properties.src = url; - this.loadSound(url); - this._dropped_url = url; - }; - - LGAudioSource.title = "Source"; - LGAudioSource.desc = "Plays audio"; - LiteGraph.registerNodeType("audio/source", LGAudioSource); - - //**************************************************** - - function LGAudioMediaSource() { - this.properties = { - gain: 0.5 - }; - - this._audionodes = []; - this._media_stream = null; - - this.addOutput("out", "audio"); - this.addInput("gain", "number"); - - //create gain node to control volume - var context = LGAudio.getAudioContext(); - this.audionode = context.createGain(); - this.audionode.graphnode = this; - this.audionode.gain.value = this.properties.gain; - } - - LGAudioMediaSource.prototype.onAdded = function(graph) { - if (graph.status === LGraph.STATUS_RUNNING) { - this.onStart(); - } - }; - - LGAudioMediaSource.prototype.onStart = function() { - if (this._media_stream == null && !this._waiting_confirmation) { - this.openStream(); - } - }; - - LGAudioMediaSource.prototype.onStop = function() { - this.audionode.gain.value = 0; - }; - - LGAudioMediaSource.prototype.onPause = function() { - this.audionode.gain.value = 0; - }; - - LGAudioMediaSource.prototype.onUnpause = function() { - this.audionode.gain.value = this.properties.gain; - }; - - LGAudioMediaSource.prototype.onRemoved = function() { - this.audionode.gain.value = 0; - if (this.audiosource_node) { - this.audiosource_node.disconnect(this.audionode); - this.audiosource_node = null; - } - if (this._media_stream) { - var tracks = this._media_stream.getTracks(); - if (tracks.length) { - tracks[0].stop(); - } - } - }; - - LGAudioMediaSource.prototype.openStream = function() { - if (!navigator.mediaDevices) { - console.log( - "getUserMedia() is not supported in your browser, use chrome and enable WebRTC from about://flags" - ); - return; - } - - this._waiting_confirmation = true; - - // Not showing vendor prefixes. - navigator.mediaDevices - .getUserMedia({ audio: true, video: false }) - .then(this.streamReady.bind(this)) - .catch(onFailSoHard); - - var that = this; - function onFailSoHard(err) { - console.log("Media rejected", err); - that._media_stream = false; - that.boxcolor = "red"; - } - }; - - LGAudioMediaSource.prototype.streamReady = function(localMediaStream) { - this._media_stream = localMediaStream; - //this._waiting_confirmation = false; - - //init context - if (this.audiosource_node) { - this.audiosource_node.disconnect(this.audionode); - } - var context = LGAudio.getAudioContext(); - this.audiosource_node = context.createMediaStreamSource( - localMediaStream - ); - this.audiosource_node.graphnode = this; - this.audiosource_node.connect(this.audionode); - this.boxcolor = "white"; - }; - - LGAudioMediaSource.prototype.onExecute = function() { - if (this._media_stream == null && !this._waiting_confirmation) { - this.openStream(); - } - - if (this.inputs) { - for (var i = 0; i < this.inputs.length; ++i) { - var input = this.inputs[i]; - if (input.link == null) { - continue; - } - var v = this.getInputData(i); - if (v === undefined) { - continue; - } - if (input.name == "gain") { - this.audionode.gain.value = this.properties.gain = v; - } - } - } - }; - - LGAudioMediaSource.prototype.onAction = function(event) { - if (event == "Play") { - this.audionode.gain.value = this.properties.gain; - } else if (event == "Stop") { - this.audionode.gain.value = 0; - } - }; - - LGAudioMediaSource.prototype.onPropertyChanged = function(name, value) { - if (name == "gain") { - this.audionode.gain.value = value; - } - }; - - //Helps connect/disconnect AudioNodes when new connections are made in the node - LGAudioMediaSource.prototype.onConnectionsChange = - LGAudio.onConnectionsChange; - - LGAudioMediaSource.prototype.onGetInputs = function() { - return [ - ["playbackRate", "number"], - ["Play", LiteGraph.ACTION], - ["Stop", LiteGraph.ACTION] - ]; - }; - - LGAudioMediaSource.title = "MediaSource"; - LGAudioMediaSource.desc = "Plays microphone"; - LiteGraph.registerNodeType("audio/media_source", LGAudioMediaSource); - - //***************************************************** - - function LGAudioAnalyser() { - this.properties = { - fftSize: 2048, - minDecibels: -100, - maxDecibels: -10, - smoothingTimeConstant: 0.5 - }; - - var context = LGAudio.getAudioContext(); - - this.audionode = context.createAnalyser(); - this.audionode.graphnode = this; - this.audionode.fftSize = this.properties.fftSize; - this.audionode.minDecibels = this.properties.minDecibels; - this.audionode.maxDecibels = this.properties.maxDecibels; - this.audionode.smoothingTimeConstant = this.properties.smoothingTimeConstant; - - this.addInput("in", "audio"); - this.addOutput("freqs", "array"); - this.addOutput("samples", "array"); - - this._freq_bin = null; - this._time_bin = null; - } - - LGAudioAnalyser.prototype.onPropertyChanged = function(name, value) { - this.audionode[name] = value; - }; - - LGAudioAnalyser.prototype.onExecute = function() { - if (this.isOutputConnected(0)) { - //send FFT - var bufferLength = this.audionode.frequencyBinCount; - if (!this._freq_bin || this._freq_bin.length != bufferLength) { - this._freq_bin = new Uint8Array(bufferLength); - } - this.audionode.getByteFrequencyData(this._freq_bin); - this.setOutputData(0, this._freq_bin); - } - - //send analyzer - if (this.isOutputConnected(1)) { - //send Samples - var bufferLength = this.audionode.frequencyBinCount; - if (!this._time_bin || this._time_bin.length != bufferLength) { - this._time_bin = new Uint8Array(bufferLength); - } - this.audionode.getByteTimeDomainData(this._time_bin); - this.setOutputData(1, this._time_bin); - } - - //properties - for (var i = 1; i < this.inputs.length; ++i) { - var input = this.inputs[i]; - if (input.link == null) { - continue; - } - var v = this.getInputData(i); - if (v !== undefined) { - this.audionode[input.name].value = v; - } - } - - //time domain - //this.audionode.getFloatTimeDomainData( dataArray ); - }; - - LGAudioAnalyser.prototype.onGetInputs = function() { - return [ - ["minDecibels", "number"], - ["maxDecibels", "number"], - ["smoothingTimeConstant", "number"] - ]; - }; - - LGAudioAnalyser.prototype.onGetOutputs = function() { - return [["freqs", "array"], ["samples", "array"]]; - }; - - LGAudioAnalyser.title = "Analyser"; - LGAudioAnalyser.desc = "Audio Analyser"; - LiteGraph.registerNodeType("audio/analyser", LGAudioAnalyser); - - //***************************************************** - - function LGAudioGain() { - //default - this.properties = { - gain: 1 - }; - - this.audionode = LGAudio.getAudioContext().createGain(); - this.addInput("in", "audio"); - this.addInput("gain", "number"); - this.addOutput("out", "audio"); - } - - LGAudioGain.prototype.onExecute = function() { - if (!this.inputs || !this.inputs.length) { - return; - } - - for (var i = 1; i < this.inputs.length; ++i) { - var input = this.inputs[i]; - var v = this.getInputData(i); - if (v !== undefined) { - this.audionode[input.name].value = v; - } - } - }; - - LGAudio.createAudioNodeWrapper(LGAudioGain); - - LGAudioGain.title = "Gain"; - LGAudioGain.desc = "Audio gain"; - LiteGraph.registerNodeType("audio/gain", LGAudioGain); - - function LGAudioConvolver() { - //default - this.properties = { - impulse_src: "", - normalize: true - }; - - this.audionode = LGAudio.getAudioContext().createConvolver(); - this.addInput("in", "audio"); - this.addOutput("out", "audio"); - } - - LGAudio.createAudioNodeWrapper(LGAudioConvolver); - - LGAudioConvolver.prototype.onRemove = function() { - if (this._dropped_url) { - URL.revokeObjectURL(this._dropped_url); - } - }; - - LGAudioConvolver.prototype.onPropertyChanged = function(name, value) { - if (name == "impulse_src") { - this.loadImpulse(value); - } else if (name == "normalize") { - this.audionode.normalize = value; - } - }; - - LGAudioConvolver.prototype.onDropFile = function(file) { - if (this._dropped_url) { - URL.revokeObjectURL(this._dropped_url); - } - this._dropped_url = URL.createObjectURL(file); - this.properties.impulse_src = this._dropped_url; - this.loadImpulse(this._dropped_url); - }; - - LGAudioConvolver.prototype.loadImpulse = function(url) { - var that = this; - - //kill previous load - if (this._request) { - this._request.abort(); - this._request = null; - } - - this._impulse_buffer = null; - this._loading_impulse = false; - - if (!url) { - return; - } - - //load new sample - this._request = LGAudio.loadSound(url, inner); - this._loading_impulse = true; - - // Decode asynchronously - function inner(buffer) { - that._impulse_buffer = buffer; - that.audionode.buffer = buffer; - console.log("Impulse signal set"); - that._loading_impulse = false; - } - }; - - LGAudioConvolver.title = "Convolver"; - LGAudioConvolver.desc = "Convolves the signal (used for reverb)"; - LiteGraph.registerNodeType("audio/convolver", LGAudioConvolver); - - function LGAudioDynamicsCompressor() { - //default - this.properties = { - threshold: -50, - knee: 40, - ratio: 12, - reduction: -20, - attack: 0, - release: 0.25 - }; - - this.audionode = LGAudio.getAudioContext().createDynamicsCompressor(); - this.addInput("in", "audio"); - this.addOutput("out", "audio"); - } - - LGAudio.createAudioNodeWrapper(LGAudioDynamicsCompressor); - - LGAudioDynamicsCompressor.prototype.onExecute = function() { - if (!this.inputs || !this.inputs.length) { - return; - } - for (var i = 1; i < this.inputs.length; ++i) { - var input = this.inputs[i]; - if (input.link == null) { - continue; - } - var v = this.getInputData(i); - if (v !== undefined) { - this.audionode[input.name].value = v; - } - } - }; - - LGAudioDynamicsCompressor.prototype.onGetInputs = function() { - return [ - ["threshold", "number"], - ["knee", "number"], - ["ratio", "number"], - ["reduction", "number"], - ["attack", "number"], - ["release", "number"] - ]; - }; - - LGAudioDynamicsCompressor.title = "DynamicsCompressor"; - LGAudioDynamicsCompressor.desc = "Dynamics Compressor"; - LiteGraph.registerNodeType( - "audio/dynamicsCompressor", - LGAudioDynamicsCompressor - ); - - function LGAudioWaveShaper() { - //default - this.properties = {}; - - this.audionode = LGAudio.getAudioContext().createWaveShaper(); - this.addInput("in", "audio"); - this.addInput("shape", "waveshape"); - this.addOutput("out", "audio"); - } - - LGAudioWaveShaper.prototype.onExecute = function() { - if (!this.inputs || !this.inputs.length) { - return; - } - var v = this.getInputData(1); - if (v === undefined) { - return; - } - this.audionode.curve = v; - }; - - LGAudioWaveShaper.prototype.setWaveShape = function(shape) { - this.audionode.curve = shape; - }; - - LGAudio.createAudioNodeWrapper(LGAudioWaveShaper); - - /* disabled till I dont find a way to do a wave shape -LGAudioWaveShaper.title = "WaveShaper"; -LGAudioWaveShaper.desc = "Distortion using wave shape"; -LiteGraph.registerNodeType("audio/waveShaper", LGAudioWaveShaper); -*/ - - function LGAudioMixer() { - //default - this.properties = { - gain1: 0.5, - gain2: 0.5 - }; - - this.audionode = LGAudio.getAudioContext().createGain(); - - this.audionode1 = LGAudio.getAudioContext().createGain(); - this.audionode1.gain.value = this.properties.gain1; - this.audionode2 = LGAudio.getAudioContext().createGain(); - this.audionode2.gain.value = this.properties.gain2; - - this.audionode1.connect(this.audionode); - this.audionode2.connect(this.audionode); - - this.addInput("in1", "audio"); - this.addInput("in1 gain", "number"); - this.addInput("in2", "audio"); - this.addInput("in2 gain", "number"); - - this.addOutput("out", "audio"); - } - - LGAudioMixer.prototype.getAudioNodeInInputSlot = function(slot) { - if (slot == 0) { - return this.audionode1; - } else if (slot == 2) { - return this.audionode2; - } - }; - - LGAudioMixer.prototype.onPropertyChanged = function(name, value) { - if (name == "gain1") { - this.audionode1.gain.value = value; - } else if (name == "gain2") { - this.audionode2.gain.value = value; - } - }; - - LGAudioMixer.prototype.onExecute = function() { - if (!this.inputs || !this.inputs.length) { - return; - } - - for (var i = 1; i < this.inputs.length; ++i) { - var input = this.inputs[i]; - - if (input.link == null || input.type == "audio") { - continue; - } - - var v = this.getInputData(i); - if (v === undefined) { - continue; - } - - if (i == 1) { - this.audionode1.gain.value = v; - } else if (i == 3) { - this.audionode2.gain.value = v; - } - } - }; - - LGAudio.createAudioNodeWrapper(LGAudioMixer); - - LGAudioMixer.title = "Mixer"; - LGAudioMixer.desc = "Audio mixer"; - LiteGraph.registerNodeType("audio/mixer", LGAudioMixer); - - function LGAudioADSR() { - //default - this.properties = { - A: 0.1, - D: 0.1, - S: 0.1, - R: 0.1 - }; - - this.audionode = LGAudio.getAudioContext().createGain(); - this.audionode.gain.value = 0; - this.addInput("in", "audio"); - this.addInput("gate", "boolean"); - this.addOutput("out", "audio"); - this.gate = false; - } - - LGAudioADSR.prototype.onExecute = function() { - var audioContext = LGAudio.getAudioContext(); - var now = audioContext.currentTime; - var node = this.audionode; - var gain = node.gain; - var current_gate = this.getInputData(1); - - var A = this.getInputOrProperty("A"); - var D = this.getInputOrProperty("D"); - var S = this.getInputOrProperty("S"); - var R = this.getInputOrProperty("R"); - - if (!this.gate && current_gate) { - gain.cancelScheduledValues(0); - gain.setValueAtTime(0, now); - gain.linearRampToValueAtTime(1, now + A); - gain.linearRampToValueAtTime(S, now + A + D); - } else if (this.gate && !current_gate) { - gain.cancelScheduledValues(0); - gain.setValueAtTime(gain.value, now); - gain.linearRampToValueAtTime(0, now + R); - } - - this.gate = current_gate; - }; - - LGAudioADSR.prototype.onGetInputs = function() { - return [ - ["A", "number"], - ["D", "number"], - ["S", "number"], - ["R", "number"] - ]; - }; - - LGAudio.createAudioNodeWrapper(LGAudioADSR); - - LGAudioADSR.title = "ADSR"; - LGAudioADSR.desc = "Audio envelope"; - LiteGraph.registerNodeType("audio/adsr", LGAudioADSR); - - function LGAudioDelay() { - //default - this.properties = { - delayTime: 0.5 - }; - - this.audionode = LGAudio.getAudioContext().createDelay(10); - this.audionode.delayTime.value = this.properties.delayTime; - this.addInput("in", "audio"); - this.addInput("time", "number"); - this.addOutput("out", "audio"); - } - - LGAudio.createAudioNodeWrapper(LGAudioDelay); - - LGAudioDelay.prototype.onExecute = function() { - var v = this.getInputData(1); - if (v !== undefined) { - this.audionode.delayTime.value = v; - } - }; - - LGAudioDelay.title = "Delay"; - LGAudioDelay.desc = "Audio delay"; - LiteGraph.registerNodeType("audio/delay", LGAudioDelay); - - function LGAudioBiquadFilter() { - //default - this.properties = { - frequency: 350, - detune: 0, - Q: 1 - }; - this.addProperty("type", "lowpass", "enum", { - values: [ - "lowpass", - "highpass", - "bandpass", - "lowshelf", - "highshelf", - "peaking", - "notch", - "allpass" - ] - }); - - //create node - this.audionode = LGAudio.getAudioContext().createBiquadFilter(); - - //slots - this.addInput("in", "audio"); - this.addOutput("out", "audio"); - } - - LGAudioBiquadFilter.prototype.onExecute = function() { - if (!this.inputs || !this.inputs.length) { - return; - } - - for (var i = 1; i < this.inputs.length; ++i) { - var input = this.inputs[i]; - if (input.link == null) { - continue; - } - var v = this.getInputData(i); - if (v !== undefined) { - this.audionode[input.name].value = v; - } - } - }; - - LGAudioBiquadFilter.prototype.onGetInputs = function() { - return [["frequency", "number"], ["detune", "number"], ["Q", "number"]]; - }; - - LGAudio.createAudioNodeWrapper(LGAudioBiquadFilter); - - LGAudioBiquadFilter.title = "BiquadFilter"; - LGAudioBiquadFilter.desc = "Audio filter"; - LiteGraph.registerNodeType("audio/biquadfilter", LGAudioBiquadFilter); - - function LGAudioOscillatorNode() { - //default - this.properties = { - frequency: 440, - detune: 0, - type: "sine" - }; - this.addProperty("type", "sine", "enum", { - values: ["sine", "square", "sawtooth", "triangle", "custom"] - }); - - //create node - this.audionode = LGAudio.getAudioContext().createOscillator(); - - //slots - this.addOutput("out", "audio"); - } - - LGAudioOscillatorNode.prototype.onStart = function() { - if (!this.audionode.started) { - this.audionode.started = true; - try { - this.audionode.start(); - } catch (err) {} - } - }; - - LGAudioOscillatorNode.prototype.onStop = function() { - if (this.audionode.started) { - this.audionode.started = false; - this.audionode.stop(); - } - }; - - LGAudioOscillatorNode.prototype.onPause = function() { - this.onStop(); - }; - - LGAudioOscillatorNode.prototype.onUnpause = function() { - this.onStart(); - }; - - LGAudioOscillatorNode.prototype.onExecute = function() { - if (!this.inputs || !this.inputs.length) { - return; - } - - for (var i = 0; i < this.inputs.length; ++i) { - var input = this.inputs[i]; - if (input.link == null) { - continue; - } - var v = this.getInputData(i); - if (v !== undefined) { - this.audionode[input.name].value = v; - } - } - }; - - LGAudioOscillatorNode.prototype.onGetInputs = function() { - return [ - ["frequency", "number"], - ["detune", "number"], - ["type", "string"] - ]; - }; - - LGAudio.createAudioNodeWrapper(LGAudioOscillatorNode); - - LGAudioOscillatorNode.title = "Oscillator"; - LGAudioOscillatorNode.desc = "Oscillator"; - LiteGraph.registerNodeType("audio/oscillator", LGAudioOscillatorNode); - - //***************************************************** - - //EXTRA - - function LGAudioVisualization() { - this.properties = { - continuous: true, - mark: -1 - }; - - this.addInput("data", "array"); - this.addInput("mark", "number"); - this.size = [300, 200]; - this._last_buffer = null; - } - - LGAudioVisualization.prototype.onExecute = function() { - this._last_buffer = this.getInputData(0); - var v = this.getInputData(1); - if (v !== undefined) { - this.properties.mark = v; - } - this.setDirtyCanvas(true, false); - }; - - LGAudioVisualization.prototype.onDrawForeground = function(ctx) { - if (!this._last_buffer) { - return; - } - - var buffer = this._last_buffer; - - //delta represents how many samples we advance per pixel - var delta = buffer.length / this.size[0]; - var h = this.size[1]; - - ctx.fillStyle = "black"; - ctx.fillRect(0, 0, this.size[0], this.size[1]); - ctx.strokeStyle = "white"; - ctx.beginPath(); - var x = 0; - - if (this.properties.continuous) { - ctx.moveTo(x, h); - for (var i = 0; i < buffer.length; i += delta) { - ctx.lineTo(x, h - (buffer[i | 0] / 255) * h); - x++; - } - } else { - for (var i = 0; i < buffer.length; i += delta) { - ctx.moveTo(x + 0.5, h); - ctx.lineTo(x + 0.5, h - (buffer[i | 0] / 255) * h); - x++; - } - } - ctx.stroke(); - - if (this.properties.mark >= 0) { - var samplerate = LGAudio.getAudioContext().sampleRate; - var binfreq = samplerate / buffer.length; - var x = (2 * (this.properties.mark / binfreq)) / delta; - if (x >= this.size[0]) { - x = this.size[0] - 1; - } - ctx.strokeStyle = "red"; - ctx.beginPath(); - ctx.moveTo(x, h); - ctx.lineTo(x, 0); - ctx.stroke(); - } - }; - - LGAudioVisualization.title = "Visualization"; - LGAudioVisualization.desc = "Audio Visualization"; - LiteGraph.registerNodeType("audio/visualization", LGAudioVisualization); - - function LGAudioBandSignal() { - //default - this.properties = { - band: 440, - amplitude: 1 - }; - - this.addInput("freqs", "array"); - this.addOutput("signal", "number"); - } - - LGAudioBandSignal.prototype.onExecute = function() { - this._freqs = this.getInputData(0); - if (!this._freqs) { - return; - } - - var band = this.properties.band; - var v = this.getInputData(1); - if (v !== undefined) { - band = v; - } - - var samplerate = LGAudio.getAudioContext().sampleRate; - var binfreq = samplerate / this._freqs.length; - var index = 2 * (band / binfreq); - var v = 0; - if (index < 0) { - v = this._freqs[0]; - } - if (index >= this._freqs.length) { - v = this._freqs[this._freqs.length - 1]; - } else { - var pos = index | 0; - var v0 = this._freqs[pos]; - var v1 = this._freqs[pos + 1]; - var f = index - pos; - v = v0 * (1 - f) + v1 * f; - } - - this.setOutputData(0, (v / 255) * this.properties.amplitude); - }; - - LGAudioBandSignal.prototype.onGetInputs = function() { - return [["band", "number"]]; - }; - - LGAudioBandSignal.title = "Signal"; - LGAudioBandSignal.desc = "extract the signal of some frequency"; - LiteGraph.registerNodeType("audio/signal", LGAudioBandSignal); - - function LGAudioScript() { - if (!LGAudioScript.default_code) { - var code = LGAudioScript.default_function.toString(); - var index = code.indexOf("{") + 1; - var index2 = code.lastIndexOf("}"); - LGAudioScript.default_code = code.substr(index, index2 - index); - } - - //default - this.properties = { - code: LGAudioScript.default_code - }; - - //create node - var ctx = LGAudio.getAudioContext(); - if (ctx.createScriptProcessor) { - this.audionode = ctx.createScriptProcessor(4096, 1, 1); - } - //buffer size, input channels, output channels - else { - console.warn("ScriptProcessorNode deprecated"); - this.audionode = ctx.createGain(); //bypass audio - } - - this.processCode(); - if (!LGAudioScript._bypass_function) { - LGAudioScript._bypass_function = this.audionode.onaudioprocess; - } - - //slots - this.addInput("in", "audio"); - this.addOutput("out", "audio"); - } - - LGAudioScript.prototype.onAdded = function(graph) { - if (graph.status == LGraph.STATUS_RUNNING) { - this.audionode.onaudioprocess = this._callback; - } - }; - - LGAudioScript["@code"] = { widget: "code", type: "code" }; - - LGAudioScript.prototype.onStart = function() { - this.audionode.onaudioprocess = this._callback; - }; - - LGAudioScript.prototype.onStop = function() { - this.audionode.onaudioprocess = LGAudioScript._bypass_function; - }; - - LGAudioScript.prototype.onPause = function() { - this.audionode.onaudioprocess = LGAudioScript._bypass_function; - }; - - LGAudioScript.prototype.onUnpause = function() { - this.audionode.onaudioprocess = this._callback; - }; - - LGAudioScript.prototype.onExecute = function() { - //nothing! because we need an onExecute to receive onStart... fix that - }; - - LGAudioScript.prototype.onRemoved = function() { - this.audionode.onaudioprocess = LGAudioScript._bypass_function; - }; - - LGAudioScript.prototype.processCode = function() { - try { - var func = new Function("properties", this.properties.code); - this._script = new func(this.properties); - this._old_code = this.properties.code; - this._callback = this._script.onaudioprocess; - } catch (err) { - console.error("Error in onaudioprocess code", err); - this._callback = LGAudioScript._bypass_function; - this.audionode.onaudioprocess = this._callback; - } - }; - - LGAudioScript.prototype.onPropertyChanged = function(name, value) { - if (name == "code") { - this.properties.code = value; - this.processCode(); - if (this.graph && this.graph.status == LGraph.STATUS_RUNNING) { - this.audionode.onaudioprocess = this._callback; - } - } - }; - - LGAudioScript.default_function = function() { - this.onaudioprocess = function(audioProcessingEvent) { - // The input buffer is the song we loaded earlier - var inputBuffer = audioProcessingEvent.inputBuffer; - - // The output buffer contains the samples that will be modified and played - var outputBuffer = audioProcessingEvent.outputBuffer; - - // Loop through the output channels (in this case there is only one) - for ( - var channel = 0; - channel < outputBuffer.numberOfChannels; - channel++ - ) { - var inputData = inputBuffer.getChannelData(channel); - var outputData = outputBuffer.getChannelData(channel); - - // Loop through the 4096 samples - for (var sample = 0; sample < inputBuffer.length; sample++) { - // make output equal to the same as the input - outputData[sample] = inputData[sample]; - } - } - }; - }; - - LGAudio.createAudioNodeWrapper(LGAudioScript); - - LGAudioScript.title = "Script"; - LGAudioScript.desc = "apply script to signal"; - LiteGraph.registerNodeType("audio/script", LGAudioScript); - - function LGAudioDestination() { - this.audionode = LGAudio.getAudioContext().destination; - this.addInput("in", "audio"); - } - - LGAudioDestination.title = "Destination"; - LGAudioDestination.desc = "Audio output"; - LiteGraph.registerNodeType("audio/destination", LGAudioDestination); -})(this); diff --git a/src/nodes/base.js b/src/nodes/base.js deleted file mode 100755 index bc6c2cd1b..000000000 --- a/src/nodes/base.js +++ /dev/null @@ -1,1770 +0,0 @@ -//basic nodes -(function(global) { - var LiteGraph = global.LiteGraph; - - //Constant - function Time() { - this.addOutput("in ms", "number"); - this.addOutput("in sec", "number"); - } - - Time.title = "Time"; - Time.desc = "Time"; - - Time.prototype.onExecute = function() { - this.setOutputData(0, this.graph.globaltime * 1000); - this.setOutputData(1, this.graph.globaltime); - }; - - LiteGraph.registerNodeType("basic/time", Time); - - //Subgraph: a node that contains a graph - function Subgraph() { - var that = this; - this.size = [140, 80]; - this.properties = { enabled: true }; - this.enabled = true; - - //create inner graph - this.subgraph = new LiteGraph.LGraph(); - this.subgraph._subgraph_node = this; - this.subgraph._is_subgraph = true; - - this.subgraph.onTrigger = this.onSubgraphTrigger.bind(this); - - //nodes input node added inside - this.subgraph.onInputAdded = this.onSubgraphNewInput.bind(this); - this.subgraph.onInputRenamed = this.onSubgraphRenamedInput.bind(this); - this.subgraph.onInputTypeChanged = this.onSubgraphTypeChangeInput.bind(this); - this.subgraph.onInputRemoved = this.onSubgraphRemovedInput.bind(this); - - this.subgraph.onOutputAdded = this.onSubgraphNewOutput.bind(this); - this.subgraph.onOutputRenamed = this.onSubgraphRenamedOutput.bind(this); - this.subgraph.onOutputTypeChanged = this.onSubgraphTypeChangeOutput.bind(this); - this.subgraph.onOutputRemoved = this.onSubgraphRemovedOutput.bind(this); - } - - Subgraph.title = "Subgraph"; - Subgraph.desc = "Graph inside a node"; - Subgraph.title_color = "#334"; - - Subgraph.prototype.onGetInputs = function() { - return [["enabled", "boolean"]]; - }; - - /* - Subgraph.prototype.onDrawTitle = function(ctx) { - if (this.flags.collapsed) { - return; - } - - ctx.fillStyle = "#555"; - var w = LiteGraph.NODE_TITLE_HEIGHT; - var x = this.size[0] - w; - ctx.fillRect(x, -w, w, w); - ctx.fillStyle = "#333"; - ctx.beginPath(); - ctx.moveTo(x + w * 0.2, -w * 0.6); - ctx.lineTo(x + w * 0.8, -w * 0.6); - ctx.lineTo(x + w * 0.5, -w * 0.3); - ctx.fill(); - }; - */ - - Subgraph.prototype.onDblClick = function(e, pos, graphcanvas) { - var that = this; - setTimeout(function() { - graphcanvas.openSubgraph(that.subgraph); - }, 10); - }; - - /* - Subgraph.prototype.onMouseDown = function(e, pos, graphcanvas) { - if ( - !this.flags.collapsed && - pos[0] > this.size[0] - LiteGraph.NODE_TITLE_HEIGHT && - pos[1] < 0 - ) { - var that = this; - setTimeout(function() { - graphcanvas.openSubgraph(that.subgraph); - }, 10); - } - }; - */ - - Subgraph.prototype.onAction = function(action, param) { - this.subgraph.onAction(action, param); - }; - - Subgraph.prototype.onExecute = function() { - this.enabled = this.getInputOrProperty("enabled"); - if (!this.enabled) { - return; - } - - //send inputs to subgraph global inputs - if (this.inputs) { - for (var i = 0; i < this.inputs.length; i++) { - var input = this.inputs[i]; - var value = this.getInputData(i); - this.subgraph.setInputData(input.name, value); - } - } - - //execute - this.subgraph.runStep(); - - //send subgraph global outputs to outputs - if (this.outputs) { - for (var i = 0; i < this.outputs.length; i++) { - var output = this.outputs[i]; - var value = this.subgraph.getOutputData(output.name); - this.setOutputData(i, value); - } - } - }; - - Subgraph.prototype.sendEventToAllNodes = function(eventname, param, mode) { - if (this.enabled) { - this.subgraph.sendEventToAllNodes(eventname, param, mode); - } - }; - - Subgraph.prototype.onDrawBackground = function (ctx, graphcanvas, canvas, pos) { - if (this.flags.collapsed) - return; - var y = this.size[1] - LiteGraph.NODE_TITLE_HEIGHT + 0.5; - // button - var over = LiteGraph.isInsideRectangle(pos[0], pos[1], this.pos[0], this.pos[1] + y, this.size[0], LiteGraph.NODE_TITLE_HEIGHT); - let overleft = LiteGraph.isInsideRectangle(pos[0], pos[1], this.pos[0], this.pos[1] + y, this.size[0] / 2, LiteGraph.NODE_TITLE_HEIGHT) - ctx.fillStyle = over ? "#555" : "#222"; - ctx.beginPath(); - if (this._shape == LiteGraph.BOX_SHAPE) { - if (overleft) { - ctx.rect(0, y, this.size[0] / 2 + 1, LiteGraph.NODE_TITLE_HEIGHT); - } else { - ctx.rect(this.size[0] / 2, y, this.size[0] / 2 + 1, LiteGraph.NODE_TITLE_HEIGHT); - } - } - else { - if (overleft) { - ctx.roundRect(0, y, this.size[0] / 2 + 1, LiteGraph.NODE_TITLE_HEIGHT, [0,0, 8,8]); - } else { - ctx.roundRect(this.size[0] / 2, y, this.size[0] / 2 + 1, LiteGraph.NODE_TITLE_HEIGHT, [0,0, 8,8]); - } - } - if (over) { - ctx.fill(); - } else { - ctx.fillRect(0, y, this.size[0] + 1, LiteGraph.NODE_TITLE_HEIGHT); - } - // button - ctx.textAlign = "center"; - ctx.font = "24px Arial"; - ctx.fillStyle = over ? "#DDD" : "#999"; - ctx.fillText("+", this.size[0] * 0.25, y + 24); - ctx.fillText("+", this.size[0] * 0.75, y + 24); - } - - // Subgraph.prototype.onMouseDown = function(e, localpos, graphcanvas) - // { - // var y = this.size[1] - LiteGraph.NODE_TITLE_HEIGHT + 0.5; - // if(localpos[1] > y) - // { - // graphcanvas.showSubgraphPropertiesDialog(this); - // } - // } - Subgraph.prototype.onMouseDown = function (e, localpos, graphcanvas) { - var y = this.size[1] - LiteGraph.NODE_TITLE_HEIGHT + 0.5; - console.log(0) - if (localpos[1] > y) { - if (localpos[0] < this.size[0] / 2) { - console.log(1) - graphcanvas.showSubgraphPropertiesDialog(this); - } else { - console.log(2) - graphcanvas.showSubgraphPropertiesDialogRight(this); - } - } - } - Subgraph.prototype.computeSize = function() - { - var num_inputs = this.inputs ? this.inputs.length : 0; - var num_outputs = this.outputs ? this.outputs.length : 0; - return [ 200, Math.max(num_inputs,num_outputs) * LiteGraph.NODE_SLOT_HEIGHT + LiteGraph.NODE_TITLE_HEIGHT ]; - } - - //**** INPUTS *********************************** - Subgraph.prototype.onSubgraphTrigger = function(event, param) { - var slot = this.findOutputSlot(event); - if (slot != -1) { - this.triggerSlot(slot); - } - }; - - Subgraph.prototype.onSubgraphNewInput = function(name, type) { - var slot = this.findInputSlot(name); - if (slot == -1) { - //add input to the node - this.addInput(name, type); - } - }; - - Subgraph.prototype.onSubgraphRenamedInput = function(oldname, name) { - var slot = this.findInputSlot(oldname); - if (slot == -1) { - return; - } - var info = this.getInputInfo(slot); - info.name = name; - }; - - Subgraph.prototype.onSubgraphTypeChangeInput = function(name, type) { - var slot = this.findInputSlot(name); - if (slot == -1) { - return; - } - var info = this.getInputInfo(slot); - info.type = type; - }; - - Subgraph.prototype.onSubgraphRemovedInput = function(name) { - var slot = this.findInputSlot(name); - if (slot == -1) { - return; - } - this.removeInput(slot); - }; - - //**** OUTPUTS *********************************** - Subgraph.prototype.onSubgraphNewOutput = function(name, type) { - var slot = this.findOutputSlot(name); - if (slot == -1) { - this.addOutput(name, type); - } - }; - - Subgraph.prototype.onSubgraphRenamedOutput = function(oldname, name) { - var slot = this.findOutputSlot(oldname); - if (slot == -1) { - return; - } - var info = this.getOutputInfo(slot); - info.name = name; - }; - - Subgraph.prototype.onSubgraphTypeChangeOutput = function(name, type) { - var slot = this.findOutputSlot(name); - if (slot == -1) { - return; - } - var info = this.getOutputInfo(slot); - info.type = type; - }; - - Subgraph.prototype.onSubgraphRemovedOutput = function(name) { - var slot = this.findOutputSlot(name); - if (slot == -1) { - return; - } - this.removeOutput(slot); - }; - // ***************************************************** - - Subgraph.prototype.getExtraMenuOptions = function(graphcanvas) { - var that = this; - return [ - { - content: "Open", - callback: function() { - graphcanvas.openSubgraph(that.subgraph); - } - } - ]; - }; - - Subgraph.prototype.onResize = function(size) { - size[1] += 20; - }; - - Subgraph.prototype.serialize = function() { - var data = LiteGraph.LGraphNode.prototype.serialize.call(this); - data.subgraph = this.subgraph.serialize(); - return data; - }; - //no need to define node.configure, the default method detects node.subgraph and passes the object to node.subgraph.configure() - - Subgraph.prototype.reassignSubgraphUUIDs = function(graph) { - const idMap = { nodeIDs: {}, linkIDs: {} } - - for (const node of graph.nodes) { - const oldID = node.id - const newID = LiteGraph.uuidv4() - node.id = newID - - if (idMap.nodeIDs[oldID] || idMap.nodeIDs[newID]) { - throw new Error(`New/old node UUID wasn't unique in changed map! ${oldID} ${newID}`) - } - - idMap.nodeIDs[oldID] = newID - idMap.nodeIDs[newID] = oldID - } - - for (const link of graph.links) { - const oldID = link[0] - const newID = LiteGraph.uuidv4(); - link[0] = newID - - if (idMap.linkIDs[oldID] || idMap.linkIDs[newID]) { - throw new Error(`New/old link UUID wasn't unique in changed map! ${oldID} ${newID}`) - } - - idMap.linkIDs[oldID] = newID - idMap.linkIDs[newID] = oldID - - const nodeFrom = link[1] - const nodeTo = link[3] - - if (!idMap.nodeIDs[nodeFrom]) { - throw new Error(`Old node UUID not found in mapping! ${nodeFrom}`) - } - - link[1] = idMap.nodeIDs[nodeFrom] - - if (!idMap.nodeIDs[nodeTo]) { - throw new Error(`Old node UUID not found in mapping! ${nodeTo}`) - } - - link[3] = idMap.nodeIDs[nodeTo] - } - - // Reconnect links - for (const node of graph.nodes) { - if (node.inputs) { - for (const input of node.inputs) { - if (input.link) { - input.link = idMap.linkIDs[input.link] - } - } - } - if (node.outputs) { - for (const output of node.outputs) { - if (output.links) { - output.links = output.links.map(l => idMap.linkIDs[l]); - } - } - } - } - - // Recurse! - for (const node of graph.nodes) { - if (node.type === "graph/subgraph") { - const merge = reassignGraphUUIDs(node.subgraph); - idMap.nodeIDs.assign(merge.nodeIDs) - idMap.linkIDs.assign(merge.linkIDs) - } - } - }; - - Subgraph.prototype.clone = function() { - var node = LiteGraph.createNode(this.type); - var data = this.serialize(); - - if (LiteGraph.use_uuids) { - // LGraph.serialize() seems to reuse objects in the original graph. But we - // need to change node IDs here, so clone it first. - const subgraph = LiteGraph.cloneObject(data.subgraph) - - this.reassignSubgraphUUIDs(subgraph); - - data.subgraph = subgraph; - } - - delete data["id"]; - delete data["inputs"]; - delete data["outputs"]; - node.configure(data); - return node; - }; - - Subgraph.prototype.buildFromNodes = function(nodes) - { - //clear all? - //TODO - - //nodes that connect data between parent graph and subgraph - var subgraph_inputs = []; - var subgraph_outputs = []; - - //mark inner nodes - var ids = {}; - var min_x = 0; - var max_x = 0; - for(var i = 0; i < nodes.length; ++i) - { - var node = nodes[i]; - ids[ node.id ] = node; - min_x = Math.min( node.pos[0], min_x ); - max_x = Math.max( node.pos[0], min_x ); - } - - var last_input_y = 0; - var last_output_y = 0; - - for(var i = 0; i < nodes.length; ++i) - { - var node = nodes[i]; - //check inputs - if( node.inputs ) - for(var j = 0; j < node.inputs.length; ++j) - { - var input = node.inputs[j]; - if( !input || !input.link ) - continue; - var link = node.graph.links[ input.link ]; - if(!link) - continue; - if( ids[ link.origin_id ] ) - continue; - //this.addInput(input.name,link.type); - this.subgraph.addInput(input.name,link.type); - /* - var input_node = LiteGraph.createNode("graph/input"); - this.subgraph.add( input_node ); - input_node.pos = [min_x - 200, last_input_y ]; - last_input_y += 100; - */ - } - - //check outputs - if( node.outputs ) - for(var j = 0; j < node.outputs.length; ++j) - { - var output = node.outputs[j]; - if( !output || !output.links || !output.links.length ) - continue; - var is_external = false; - for(var k = 0; k < output.links.length; ++k) - { - var link = node.graph.links[ output.links[k] ]; - if(!link) - continue; - if( ids[ link.target_id ] ) - continue; - is_external = true; - break; - } - if(!is_external) - continue; - //this.addOutput(output.name,output.type); - /* - var output_node = LiteGraph.createNode("graph/output"); - this.subgraph.add( output_node ); - output_node.pos = [max_x + 50, last_output_y ]; - last_output_y += 100; - */ - } - } - - //detect inputs and outputs - //split every connection in two data_connection nodes - //keep track of internal connections - //connect external connections - - //clone nodes inside subgraph and try to reconnect them - - //connect edge subgraph nodes to extarnal connections nodes - } - - LiteGraph.Subgraph = Subgraph; - LiteGraph.registerNodeType("graph/subgraph", Subgraph); - - //Input for a subgraph - function GraphInput() { - this.addOutput("", "number"); - - this.name_in_graph = ""; - this.properties = { - name: "", - type: "number", - value: 0 - }; - - var that = this; - - this.name_widget = this.addWidget( - "text", - "Name", - this.properties.name, - function(v) { - if (!v) { - return; - } - that.setProperty("name",v); - } - ); - this.type_widget = this.addWidget( - "text", - "Type", - this.properties.type, - function(v) { - that.setProperty("type",v); - } - ); - - this.value_widget = this.addWidget( - "number", - "Value", - this.properties.value, - function(v) { - that.setProperty("value",v); - } - ); - - this.widgets_up = true; - this.size = [180, 90]; - } - - GraphInput.title = "Input"; - GraphInput.desc = "Input of the graph"; - - GraphInput.prototype.onConfigure = function() - - { - this.updateType(); - } - - //ensures the type in the node output and the type in the associated graph input are the same - GraphInput.prototype.updateType = function() - { - var type = this.properties.type; - this.type_widget.value = type; - - //update output - if(this.outputs[0].type != type) - { - if (!LiteGraph.isValidConnection(this.outputs[0].type,type)) - this.disconnectOutput(0); - this.outputs[0].type = type; - } - - //update widget - if(type == "number") - { - this.value_widget.type = "number"; - this.value_widget.value = 0; - } - else if(type == "boolean") - { - this.value_widget.type = "toggle"; - this.value_widget.value = true; - } - else if(type == "string") - { - this.value_widget.type = "text"; - this.value_widget.value = ""; - } - else - { - this.value_widget.type = null; - this.value_widget.value = null; - } - this.properties.value = this.value_widget.value; - - //update graph - if (this.graph && this.name_in_graph) { - this.graph.changeInputType(this.name_in_graph, type); - } - } - - //this is executed AFTER the property has changed - GraphInput.prototype.onPropertyChanged = function(name,v) - { - if( name == "name" ) - { - if (v == "" || v == this.name_in_graph || v == "enabled") { - return false; - } - if(this.graph) - { - if (this.name_in_graph) { - //already added - this.graph.renameInput( this.name_in_graph, v ); - } else { - this.graph.addInput( v, this.properties.type ); - } - } //what if not?! - this.name_widget.value = v; - this.name_in_graph = v; - } - else if( name == "type" ) - { - this.updateType(); - } - else if( name == "value" ) - { - } - } - - GraphInput.prototype.getTitle = function() { - if (this.flags.collapsed) { - return this.properties.name; - } - return this.title; - }; - - GraphInput.prototype.onAction = function(action, param) { - if (this.properties.type == LiteGraph.EVENT) { - this.triggerSlot(0, param); - } - }; - - GraphInput.prototype.onExecute = function() { - var name = this.properties.name; - //read from global input - var data = this.graph.inputs[name]; - if (!data) { - this.setOutputData(0, this.properties.value ); - return; - } - - this.setOutputData(0, data.value !== undefined ? data.value : this.properties.value ); - }; - - GraphInput.prototype.onRemoved = function() { - if (this.name_in_graph) { - this.graph.removeInput(this.name_in_graph); - } - }; - - LiteGraph.GraphInput = GraphInput; - LiteGraph.registerNodeType("graph/input", GraphInput); - - //Output for a subgraph - function GraphOutput() { - this.addInput("", ""); - - this.name_in_graph = ""; - this.properties = { name: "", type: "" }; - var that = this; - - // Object.defineProperty(this.properties, "name", { - // get: function() { - // return that.name_in_graph; - // }, - // set: function(v) { - // if (v == "" || v == that.name_in_graph) { - // return; - // } - // if (that.name_in_graph) { - // //already added - // that.graph.renameOutput(that.name_in_graph, v); - // } else { - // that.graph.addOutput(v, that.properties.type); - // } - // that.name_widget.value = v; - // that.name_in_graph = v; - // }, - // enumerable: true - // }); - - // Object.defineProperty(this.properties, "type", { - // get: function() { - // return that.inputs[0].type; - // }, - // set: function(v) { - // if (v == "action" || v == "event") { - // v = LiteGraph.ACTION; - // } - // if (!LiteGraph.isValidConnection(that.inputs[0].type,v)) - // that.disconnectInput(0); - // that.inputs[0].type = v; - // if (that.name_in_graph) { - // //already added - // that.graph.changeOutputType( - // that.name_in_graph, - // that.inputs[0].type - // ); - // } - // that.type_widget.value = v || ""; - // }, - // enumerable: true - // }); - - this.name_widget = this.addWidget("text","Name",this.properties.name,"name"); - this.type_widget = this.addWidget("text","Type",this.properties.type,"type"); - this.widgets_up = true; - this.size = [180, 60]; - } - - GraphOutput.title = "Output"; - GraphOutput.desc = "Output of the graph"; - - GraphOutput.prototype.onPropertyChanged = function (name, v) { - if (name == "name") { - if (v == "" || v == this.name_in_graph || v == "enabled") { - return false; - } - if (this.graph) { - if (this.name_in_graph) { - //already added - this.graph.renameOutput(this.name_in_graph, v); - } else { - this.graph.addOutput(v, this.properties.type); - } - } //what if not?! - this.name_widget.value = v; - this.name_in_graph = v; - } - else if (name == "type") { - this.updateType(); - } - else if (name == "value") { - } - } - - GraphOutput.prototype.updateType = function () { - var type = this.properties.type; - if (this.type_widget) - this.type_widget.value = type; - - //update output - if (this.inputs[0].type != type) { - - if ( type == "action" || type == "event") - type = LiteGraph.EVENT; - if (!LiteGraph.isValidConnection(this.inputs[0].type, type)) - this.disconnectInput(0); - this.inputs[0].type = type; - } - - //update graph - if (this.graph && this.name_in_graph) { - this.graph.changeOutputType(this.name_in_graph, type); - } - } - - - - GraphOutput.prototype.onExecute = function() { - this._value = this.getInputData(0); - this.graph.setOutputData(this.properties.name, this._value); - }; - - GraphOutput.prototype.onAction = function(action, param) { - if (this.properties.type == LiteGraph.ACTION) { - this.graph.trigger( this.properties.name, param ); - } - }; - - GraphOutput.prototype.onRemoved = function() { - if (this.name_in_graph) { - this.graph.removeOutput(this.name_in_graph); - } - }; - - GraphOutput.prototype.getTitle = function() { - if (this.flags.collapsed) { - return this.properties.name; - } - return this.title; - }; - - LiteGraph.GraphOutput = GraphOutput; - LiteGraph.registerNodeType("graph/output", GraphOutput); - - //Constant - function ConstantNumber() { - this.addOutput("value", "number"); - this.addProperty("value", 1.0); - this.widget = this.addWidget("number","value",1,"value"); - this.widgets_up = true; - this.size = [180, 30]; - } - - ConstantNumber.title = "Const Number"; - ConstantNumber.desc = "Constant number"; - - ConstantNumber.prototype.onExecute = function() { - this.setOutputData(0, parseFloat(this.properties["value"])); - }; - - ConstantNumber.prototype.getTitle = function() { - if (this.flags.collapsed) { - return this.properties.value; - } - return this.title; - }; - - ConstantNumber.prototype.setValue = function(v) - { - this.setProperty("value",v); - } - - ConstantNumber.prototype.onDrawBackground = function(ctx) { - //show the current value - this.outputs[0].label = this.properties["value"].toFixed(3); - }; - - LiteGraph.registerNodeType("basic/const", ConstantNumber); - - function ConstantBoolean() { - this.addOutput("bool", "boolean"); - this.addProperty("value", true); - this.widget = this.addWidget("toggle","value",true,"value"); - this.serialize_widgets = true; - this.widgets_up = true; - this.size = [140, 30]; - } - - ConstantBoolean.title = "Const Boolean"; - ConstantBoolean.desc = "Constant boolean"; - ConstantBoolean.prototype.getTitle = ConstantNumber.prototype.getTitle; - - ConstantBoolean.prototype.onExecute = function() { - this.setOutputData(0, this.properties["value"]); - }; - - ConstantBoolean.prototype.setValue = ConstantNumber.prototype.setValue; - - ConstantBoolean.prototype.onGetInputs = function() { - return [["toggle", LiteGraph.ACTION]]; - }; - - ConstantBoolean.prototype.onAction = function(action) - { - this.setValue( !this.properties.value ); - } - - LiteGraph.registerNodeType("basic/boolean", ConstantBoolean); - - function ConstantString() { - this.addOutput("string", "string"); - this.addProperty("value", ""); - this.widget = this.addWidget("text","value","","value"); //link to property value - this.widgets_up = true; - this.size = [180, 30]; - } - - ConstantString.title = "Const String"; - ConstantString.desc = "Constant string"; - - ConstantString.prototype.getTitle = ConstantNumber.prototype.getTitle; - - ConstantString.prototype.onExecute = function() { - this.setOutputData(0, this.properties["value"]); - }; - - ConstantString.prototype.setValue = ConstantNumber.prototype.setValue; - - ConstantString.prototype.onDropFile = function(file) - { - var that = this; - var reader = new FileReader(); - reader.onload = function(e) - { - that.setProperty("value",e.target.result); - } - reader.readAsText(file); - } - - LiteGraph.registerNodeType("basic/string", ConstantString); - - function ConstantObject() { - this.addOutput("obj", "object"); - this.size = [120, 30]; - this._object = {}; - } - - ConstantObject.title = "Const Object"; - ConstantObject.desc = "Constant Object"; - - ConstantObject.prototype.onExecute = function() { - this.setOutputData(0, this._object); - }; - - LiteGraph.registerNodeType( "basic/object", ConstantObject ); - - function ConstantFile() { - this.addInput("url", "string"); - this.addOutput("file", "string"); - this.addProperty("url", ""); - this.addProperty("type", "text"); - this.widget = this.addWidget("text","url","","url"); - this._data = null; - } - - ConstantFile.title = "Const File"; - ConstantFile.desc = "Fetches a file from an url"; - ConstantFile["@type"] = { type: "enum", values: ["text","arraybuffer","blob","json"] }; - - ConstantFile.prototype.onPropertyChanged = function(name, value) { - if (name == "url") - { - if( value == null || value == "") - this._data = null; - else - { - this.fetchFile(value); - } - } - } - - ConstantFile.prototype.onExecute = function() { - var url = this.getInputData(0) || this.properties.url; - if(url && (url != this._url || this._type != this.properties.type)) - this.fetchFile(url); - this.setOutputData(0, this._data ); - }; - - ConstantFile.prototype.setValue = ConstantNumber.prototype.setValue; - - ConstantFile.prototype.fetchFile = function(url) { - var that = this; - if(!url || url.constructor !== String) - { - that._data = null; - that.boxcolor = null; - return; - } - - this._url = url; - this._type = this.properties.type; - if (url.substr(0, 4) == "http" && LiteGraph.proxy) { - url = LiteGraph.proxy + url.substr(url.indexOf(":") + 3); - } - fetch(url) - .then(function(response) { - if(!response.ok) - throw new Error("File not found"); - - if(that.properties.type == "arraybuffer") - return response.arrayBuffer(); - else if(that.properties.type == "text") - return response.text(); - else if(that.properties.type == "json") - return response.json(); - else if(that.properties.type == "blob") - return response.blob(); - }) - .then(function(data) { - that._data = data; - that.boxcolor = "#AEA"; - }) - .catch(function(error) { - that._data = null; - that.boxcolor = "red"; - console.error("error fetching file:",url); - }); - }; - - ConstantFile.prototype.onDropFile = function(file) - { - var that = this; - this._url = file.name; - this._type = this.properties.type; - this.properties.url = file.name; - var reader = new FileReader(); - reader.onload = function(e) - { - that.boxcolor = "#AEA"; - var v = e.target.result; - if( that.properties.type == "json" ) - v = JSON.parse(v); - that._data = v; - } - if(that.properties.type == "arraybuffer") - reader.readAsArrayBuffer(file); - else if(that.properties.type == "text" || that.properties.type == "json") - reader.readAsText(file); - else if(that.properties.type == "blob") - return reader.readAsBinaryString(file); - } - - LiteGraph.registerNodeType("basic/file", ConstantFile); - - -//to store json objects -function JSONParse() { - this.addInput("parse", LiteGraph.ACTION); - this.addInput("json", "string"); - this.addOutput("done", LiteGraph.EVENT); - this.addOutput("object", "object"); - this.widget = this.addWidget("button","parse","",this.parse.bind(this)); - this._str = null; - this._obj = null; -} - -JSONParse.title = "JSON Parse"; -JSONParse.desc = "Parses JSON String into object"; - -JSONParse.prototype.parse = function() -{ - if(!this._str) - return; - - try { - this._str = this.getInputData(1); - this._obj = JSON.parse(this._str); - this.boxcolor = "#AEA"; - this.triggerSlot(0); - } catch (err) { - this.boxcolor = "red"; - } -} - -JSONParse.prototype.onExecute = function() { - this._str = this.getInputData(1); - this.setOutputData(1, this._obj); -}; - -JSONParse.prototype.onAction = function(name) { - if(name == "parse") - this.parse(); -} - -LiteGraph.registerNodeType("basic/jsonparse", JSONParse); - - //to store json objects - function ConstantData() { - this.addOutput("data", "object"); - this.addProperty("value", ""); - this.widget = this.addWidget("text","json","","value"); - this.widgets_up = true; - this.size = [140, 30]; - this._value = null; - } - - ConstantData.title = "Const Data"; - ConstantData.desc = "Constant Data"; - - ConstantData.prototype.onPropertyChanged = function(name, value) { - this.widget.value = value; - if (value == null || value == "") { - return; - } - - try { - this._value = JSON.parse(value); - this.boxcolor = "#AEA"; - } catch (err) { - this.boxcolor = "red"; - } - }; - - ConstantData.prototype.onExecute = function() { - this.setOutputData(0, this._value); - }; - - ConstantData.prototype.setValue = ConstantNumber.prototype.setValue; - - LiteGraph.registerNodeType("basic/data", ConstantData); - - //to store json objects - function ConstantArray() { - this._value = []; - this.addInput("json", ""); - this.addOutput("arrayOut", "array"); - this.addOutput("length", "number"); - this.addProperty("value", "[]"); - this.widget = this.addWidget("text","array",this.properties.value,"value"); - this.widgets_up = true; - this.size = [140, 50]; - } - - ConstantArray.title = "Const Array"; - ConstantArray.desc = "Constant Array"; - - ConstantArray.prototype.onPropertyChanged = function(name, value) { - this.widget.value = value; - if (value == null || value == "") { - return; - } - - try { - if(value[0] != "[") - this._value = JSON.parse("[" + value + "]"); - else - this._value = JSON.parse(value); - this.boxcolor = "#AEA"; - } catch (err) { - this.boxcolor = "red"; - } - }; - - ConstantArray.prototype.onExecute = function() { - var v = this.getInputData(0); - if(v && v.length) //clone - { - if(!this._value) - this._value = new Array(); - this._value.length = v.length; - for(var i = 0; i < v.length; ++i) - this._value[i] = v[i]; - } - this.setOutputData(0, this._value); - this.setOutputData(1, this._value ? ( this._value.length || 0) : 0 ); - }; - - ConstantArray.prototype.setValue = ConstantNumber.prototype.setValue; - - LiteGraph.registerNodeType("basic/array", ConstantArray); - - function SetArray() - { - this.addInput("arr", "array"); - this.addInput("value", ""); - this.addOutput("arr", "array"); - this.properties = { index: 0 }; - this.widget = this.addWidget("number","i",this.properties.index,"index",{precision: 0, step: 10, min: 0}); - } - - SetArray.title = "Set Array"; - SetArray.desc = "Sets index of array"; - - SetArray.prototype.onExecute = function() { - var arr = this.getInputData(0); - if(!arr) - return; - var v = this.getInputData(1); - if(v === undefined ) - return; - if(this.properties.index) - arr[ Math.floor(this.properties.index) ] = v; - this.setOutputData(0,arr); - }; - - LiteGraph.registerNodeType("basic/set_array", SetArray ); - - function ArrayElement() { - this.addInput("array", "array,table,string"); - this.addInput("index", "number"); - this.addOutput("value", ""); - this.addProperty("index",0); - } - - ArrayElement.title = "Array[i]"; - ArrayElement.desc = "Returns an element from an array"; - - ArrayElement.prototype.onExecute = function() { - var array = this.getInputData(0); - var index = this.getInputData(1); - if(index == null) - index = this.properties.index; - if(array == null || index == null ) - return; - this.setOutputData(0, array[Math.floor(Number(index))] ); - }; - - LiteGraph.registerNodeType("basic/array[]", ArrayElement); - - function TableElement() { - this.addInput("table", "table"); - this.addInput("row", "number"); - this.addInput("col", "number"); - this.addOutput("value", ""); - this.addProperty("row",0); - this.addProperty("column",0); - } - - TableElement.title = "Table[row][col]"; - TableElement.desc = "Returns an element from a table"; - - TableElement.prototype.onExecute = function() { - var table = this.getInputData(0); - var row = this.getInputData(1); - var col = this.getInputData(2); - if(row == null) - row = this.properties.row; - if(col == null) - col = this.properties.column; - if(table == null || row == null || col == null) - return; - var row = table[Math.floor(Number(row))]; - if(row) - this.setOutputData(0, row[Math.floor(Number(col))] ); - else - this.setOutputData(0, null ); - }; - - LiteGraph.registerNodeType("basic/table[][]", TableElement); - - function ObjectProperty() { - this.addInput("obj", "object"); - this.addOutput("property", 0); - this.addProperty("value", 0); - this.widget = this.addWidget("text","prop.","",this.setValue.bind(this) ); - this.widgets_up = true; - this.size = [140, 30]; - this._value = null; - } - - ObjectProperty.title = "Object property"; - ObjectProperty.desc = "Outputs the property of an object"; - - ObjectProperty.prototype.setValue = function(v) { - this.properties.value = v; - this.widget.value = v; - }; - - ObjectProperty.prototype.getTitle = function() { - if (this.flags.collapsed) { - return "in." + this.properties.value; - } - return this.title; - }; - - ObjectProperty.prototype.onPropertyChanged = function(name, value) { - this.widget.value = value; - }; - - ObjectProperty.prototype.onExecute = function() { - var data = this.getInputData(0); - if (data != null) { - this.setOutputData(0, data[this.properties.value]); - } - }; - - LiteGraph.registerNodeType("basic/object_property", ObjectProperty); - - function ObjectKeys() { - this.addInput("obj", ""); - this.addOutput("keys", "array"); - this.size = [140, 30]; - } - - ObjectKeys.title = "Object keys"; - ObjectKeys.desc = "Outputs an array with the keys of an object"; - - ObjectKeys.prototype.onExecute = function() { - var data = this.getInputData(0); - if (data != null) { - this.setOutputData(0, Object.keys(data) ); - } - }; - - LiteGraph.registerNodeType("basic/object_keys", ObjectKeys); - - - function SetObject() - { - this.addInput("obj", ""); - this.addInput("value", ""); - this.addOutput("obj", ""); - this.properties = { property: "" }; - this.name_widget = this.addWidget("text","prop.",this.properties.property,"property"); - } - - SetObject.title = "Set Object"; - SetObject.desc = "Adds propertiesrty to object"; - - SetObject.prototype.onExecute = function() { - var obj = this.getInputData(0); - if(!obj) - return; - var v = this.getInputData(1); - if(v === undefined ) - return; - if(this.properties.property) - obj[ this.properties.property ] = v; - this.setOutputData(0,obj); - }; - - LiteGraph.registerNodeType("basic/set_object", SetObject ); - - - function MergeObjects() { - this.addInput("A", "object"); - this.addInput("B", "object"); - this.addOutput("out", "object"); - this._result = {}; - var that = this; - this.addWidget("button","clear","",function(){ - that._result = {}; - }); - this.size = this.computeSize(); - } - - MergeObjects.title = "Merge Objects"; - MergeObjects.desc = "Creates an object copying properties from others"; - - MergeObjects.prototype.onExecute = function() { - var A = this.getInputData(0); - var B = this.getInputData(1); - var C = this._result; - if(A) - for(var i in A) - C[i] = A[i]; - if(B) - for(var i in B) - C[i] = B[i]; - this.setOutputData(0,C); - }; - - LiteGraph.registerNodeType("basic/merge_objects", MergeObjects ); - - //Store as variable - function Variable() { - this.size = [60, 30]; - this.addInput("in"); - this.addOutput("out"); - this.properties = { varname: "myname", container: Variable.LITEGRAPH }; - this.value = null; - } - - Variable.title = "Variable"; - Variable.desc = "store/read variable value"; - - Variable.LITEGRAPH = 0; //between all graphs - Variable.GRAPH = 1; //only inside this graph - Variable.GLOBALSCOPE = 2; //attached to Window - - Variable["@container"] = { type: "enum", values: {"litegraph":Variable.LITEGRAPH, "graph":Variable.GRAPH,"global": Variable.GLOBALSCOPE} }; - - Variable.prototype.onExecute = function() { - var container = this.getContainer(); - - if(this.isInputConnected(0)) - { - this.value = this.getInputData(0); - container[ this.properties.varname ] = this.value; - this.setOutputData(0, this.value ); - return; - } - - this.setOutputData( 0, container[ this.properties.varname ] ); - }; - - Variable.prototype.getContainer = function() - { - switch(this.properties.container) - { - case Variable.GRAPH: - if(this.graph) - return this.graph.vars; - return {}; - break; - case Variable.GLOBALSCOPE: - return global; - break; - case Variable.LITEGRAPH: - default: - return LiteGraph.Globals; - break; - } - } - - Variable.prototype.getTitle = function() { - return this.properties.varname; - }; - - LiteGraph.registerNodeType("basic/variable", Variable); - - function length(v) { - if(v && v.length != null) - return Number(v.length); - return 0; - } - - LiteGraph.wrapFunctionAsNode( - "basic/length", - length, - [""], - "number" - ); - - function length(v) { - if(v && v.length != null) - return Number(v.length); - return 0; - } - - LiteGraph.wrapFunctionAsNode( - "basic/not", - function(a){ return !a; }, - [""], - "boolean" - ); - - function DownloadData() { - this.size = [60, 30]; - this.addInput("data", 0 ); - this.addInput("download", LiteGraph.ACTION ); - this.properties = { filename: "data.json" }; - this.value = null; - var that = this; - this.addWidget("button","Download","", function(v){ - if(!that.value) - return; - that.downloadAsFile(); - }); - } - - DownloadData.title = "Download"; - DownloadData.desc = "Download some data"; - - DownloadData.prototype.downloadAsFile = function() - { - if(this.value == null) - return; - - var str = null; - if(this.value.constructor === String) - str = this.value; - else - str = JSON.stringify(this.value); - - var file = new Blob([str]); - var url = URL.createObjectURL( file ); - var element = document.createElement("a"); - element.setAttribute('href', url); - element.setAttribute('download', this.properties.filename ); - element.style.display = 'none'; - document.body.appendChild(element); - element.click(); - document.body.removeChild(element); - setTimeout( function(){ URL.revokeObjectURL( url ); }, 1000*60 ); //wait one minute to revoke url - } - - DownloadData.prototype.onAction = function(action, param) { - var that = this; - setTimeout( function(){ that.downloadAsFile(); }, 100); //deferred to avoid blocking the renderer with the popup - } - - DownloadData.prototype.onExecute = function() { - if (this.inputs[0]) { - this.value = this.getInputData(0); - } - }; - - DownloadData.prototype.getTitle = function() { - if (this.flags.collapsed) { - return this.properties.filename; - } - return this.title; - }; - - LiteGraph.registerNodeType("basic/download", DownloadData); - - - - //Watch a value in the editor - function Watch() { - this.size = [60, 30]; - this.addInput("value", 0, { label: "" }); - this.value = 0; - } - - Watch.title = "Watch"; - Watch.desc = "Show value of input"; - - Watch.prototype.onExecute = function() { - if (this.inputs[0]) { - this.value = this.getInputData(0); - } - }; - - Watch.prototype.getTitle = function() { - if (this.flags.collapsed) { - return this.inputs[0].label; - } - return this.title; - }; - - Watch.toString = function(o) { - if (o == null) { - return "null"; - } else if (o.constructor === Number) { - return o.toFixed(3); - } else if (o.constructor === Array) { - var str = "["; - for (var i = 0; i < o.length; ++i) { - str += Watch.toString(o[i]) + (i + 1 != o.length ? "," : ""); - } - str += "]"; - return str; - } else { - return String(o); - } - }; - - Watch.prototype.onDrawBackground = function(ctx) { - //show the current value - this.inputs[0].label = Watch.toString(this.value); - }; - - LiteGraph.registerNodeType("basic/watch", Watch); - - //in case one type doesnt match other type but you want to connect them anyway - function Cast() { - this.addInput("in", 0); - this.addOutput("out", 0); - this.size = [40, 30]; - } - - Cast.title = "Cast"; - Cast.desc = "Allows to connect different types"; - - Cast.prototype.onExecute = function() { - this.setOutputData(0, this.getInputData(0)); - }; - - LiteGraph.registerNodeType("basic/cast", Cast); - - //Show value inside the debug console - function Console() { - this.mode = LiteGraph.ON_EVENT; - this.size = [80, 30]; - this.addProperty("msg", ""); - this.addInput("log", LiteGraph.EVENT); - this.addInput("msg", 0); - } - - Console.title = "Console"; - Console.desc = "Show value inside the console"; - - Console.prototype.onAction = function(action, param) { - // param is the action - var msg = this.getInputData(1); //getInputDataByName("msg"); - //if (msg == null || typeof msg == "undefined") return; - if (!msg) msg = this.properties.msg; - if (!msg) msg = "Event: "+param; // msg is undefined if the slot is lost? - if (action == "log") { - console.log(msg); - } else if (action == "warn") { - console.warn(msg); - } else if (action == "error") { - console.error(msg); - } - }; - - Console.prototype.onExecute = function() { - var msg = this.getInputData(1); //getInputDataByName("msg"); - if (!msg) msg = this.properties.msg; - if (msg != null && typeof msg != "undefined") { - this.properties.msg = msg; - console.log(msg); - } - }; - - Console.prototype.onGetInputs = function() { - return [ - ["log", LiteGraph.ACTION], - ["warn", LiteGraph.ACTION], - ["error", LiteGraph.ACTION] - ]; - }; - - LiteGraph.registerNodeType("basic/console", Console); - - //Show value inside the debug console - function Alert() { - this.mode = LiteGraph.ON_EVENT; - this.addProperty("msg", ""); - this.addInput("", LiteGraph.EVENT); - var that = this; - this.widget = this.addWidget("text", "Text", "", "msg"); - this.widgets_up = true; - this.size = [200, 30]; - } - - Alert.title = "Alert"; - Alert.desc = "Show an alert window"; - Alert.color = "#510"; - - Alert.prototype.onConfigure = function(o) { - this.widget.value = o.properties.msg; - }; - - Alert.prototype.onAction = function(action, param) { - var msg = this.properties.msg; - setTimeout(function() { - alert(msg); - }, 10); - }; - - LiteGraph.registerNodeType("basic/alert", Alert); - - //Execites simple code - function NodeScript() { - this.size = [60, 30]; - this.addProperty("onExecute", "return A;"); - this.addInput("A", 0); - this.addInput("B", 0); - this.addOutput("out", 0); - - this._func = null; - this.data = {}; - } - - NodeScript.prototype.onConfigure = function(o) { - if (o.properties.onExecute && LiteGraph.allow_scripts) - this.compileCode(o.properties.onExecute); - else - console.warn("Script not compiled, LiteGraph.allow_scripts is false"); - }; - - NodeScript.title = "Script"; - NodeScript.desc = "executes a code (max 256 characters)"; - - NodeScript.widgets_info = { - onExecute: { type: "code" } - }; - - NodeScript.prototype.onPropertyChanged = function(name, value) { - if (name == "onExecute" && LiteGraph.allow_scripts) - this.compileCode(value); - else - console.warn("Script not compiled, LiteGraph.allow_scripts is false"); - }; - - NodeScript.prototype.compileCode = function(code) { - this._func = null; - if (code.length > 256) { - console.warn("Script too long, max 256 chars"); - } else { - var code_low = code.toLowerCase(); - var forbidden_words = [ - "script", - "body", - "document", - "eval", - "nodescript", - "function" - ]; //bad security solution - for (var i = 0; i < forbidden_words.length; ++i) { - if (code_low.indexOf(forbidden_words[i]) != -1) { - console.warn("invalid script"); - return; - } - } - try { - this._func = new Function("A", "B", "C", "DATA", "node", code); - } catch (err) { - console.error("Error parsing script"); - console.error(err); - } - } - }; - - NodeScript.prototype.onExecute = function() { - if (!this._func) { - return; - } - - try { - var A = this.getInputData(0); - var B = this.getInputData(1); - var C = this.getInputData(2); - this.setOutputData(0, this._func(A, B, C, this.data, this)); - } catch (err) { - console.error("Error in script"); - console.error(err); - } - }; - - NodeScript.prototype.onGetOutputs = function() { - return [["C", ""]]; - }; - - LiteGraph.registerNodeType("basic/script", NodeScript); - - - function GenericCompare() { - this.addInput("A", 0); - this.addInput("B", 0); - this.addOutput("true", "boolean"); - this.addOutput("false", "boolean"); - this.addProperty("A", 1); - this.addProperty("B", 1); - this.addProperty("OP", "==", "enum", { values: GenericCompare.values }); - this.addWidget("combo","Op.",this.properties.OP,{ property: "OP", values: GenericCompare.values } ); - - this.size = [80, 60]; - } - - GenericCompare.values = ["==", "!="]; //[">", "<", "==", "!=", "<=", ">=", "||", "&&" ]; - GenericCompare["@OP"] = { - type: "enum", - title: "operation", - values: GenericCompare.values - }; - - GenericCompare.title = "Compare *"; - GenericCompare.desc = "evaluates condition between A and B"; - - GenericCompare.prototype.getTitle = function() { - return "*A " + this.properties.OP + " *B"; - }; - - GenericCompare.prototype.onExecute = function() { - var A = this.getInputData(0); - if (A === undefined) { - A = this.properties.A; - } else { - this.properties.A = A; - } - - var B = this.getInputData(1); - if (B === undefined) { - B = this.properties.B; - } else { - this.properties.B = B; - } - - var result = false; - if (typeof A == typeof B){ - switch (this.properties.OP) { - case "==": - case "!=": - // traverse both objects.. consider that this is not a true deep check! consider underscore or other library for thath :: _isEqual() - result = true; - switch(typeof A){ - case "object": - var aProps = Object.getOwnPropertyNames(A); - var bProps = Object.getOwnPropertyNames(B); - if (aProps.length != bProps.length){ - result = false; - break; - } - for (var i = 0; i < aProps.length; i++) { - var propName = aProps[i]; - if (A[propName] !== B[propName]) { - result = false; - break; - } - } - break; - default: - result = A == B; - } - if (this.properties.OP == "!=") result = !result; - break; - /*case ">": - result = A > B; - break; - case "<": - result = A < B; - break; - case "<=": - result = A <= B; - break; - case ">=": - result = A >= B; - break; - case "||": - result = A || B; - break; - case "&&": - result = A && B; - break;*/ - } - } - this.setOutputData(0, result); - this.setOutputData(1, !result); - }; - - LiteGraph.registerNodeType("basic/CompareValues", GenericCompare); - -})(this); diff --git a/src/nodes/events.js b/src/nodes/events.js deleted file mode 100755 index 34d7351b6..000000000 --- a/src/nodes/events.js +++ /dev/null @@ -1,504 +0,0 @@ -//event related nodes -(function(global) { - var LiteGraph = global.LiteGraph; - - //Show value inside the debug console - function LogEvent() { - this.size = [60, 30]; - this.addInput("event", LiteGraph.ACTION); - } - - LogEvent.title = "Log Event"; - LogEvent.desc = "Log event in console"; - - LogEvent.prototype.onAction = function(action, param, options) { - console.log(action, param); - }; - - LiteGraph.registerNodeType("events/log", LogEvent); - - //convert to Event if the value is true - function TriggerEvent() { - this.size = [60, 30]; - this.addInput("if", ""); - this.addOutput("true", LiteGraph.EVENT); - this.addOutput("change", LiteGraph.EVENT); - this.addOutput("false", LiteGraph.EVENT); - this.properties = { only_on_change: true }; - this.prev = 0; - } - - TriggerEvent.title = "TriggerEvent"; - TriggerEvent.desc = "Triggers event if input evaluates to true"; - - TriggerEvent.prototype.onExecute = function( param, options) { - var v = this.getInputData(0); - var changed = (v != this.prev); - if(this.prev === 0) - changed = false; - var must_resend = (changed && this.properties.only_on_change) || (!changed && !this.properties.only_on_change); - if(v && must_resend ) - this.triggerSlot(0, param, null, options); - if(!v && must_resend) - this.triggerSlot(2, param, null, options); - if(changed) - this.triggerSlot(1, param, null, options); - this.prev = v; - }; - - LiteGraph.registerNodeType("events/trigger", TriggerEvent); - - //Sequence of events - function Sequence() { - var that = this; - this.addInput("", LiteGraph.ACTION); - this.addInput("", LiteGraph.ACTION); - this.addInput("", LiteGraph.ACTION); - this.addOutput("", LiteGraph.EVENT); - this.addOutput("", LiteGraph.EVENT); - this.addOutput("", LiteGraph.EVENT); - this.addWidget("button","+",null,function(){ - that.addInput("", LiteGraph.ACTION); - that.addOutput("", LiteGraph.EVENT); - }); - this.size = [90, 70]; - this.flags = { horizontal: true, render_box: false }; - } - - Sequence.title = "Sequence"; - Sequence.desc = "Triggers a sequence of events when an event arrives"; - - Sequence.prototype.getTitle = function() { - return ""; - }; - - Sequence.prototype.onAction = function(action, param, options) { - if (this.outputs) { - options = options || {}; - for (var i = 0; i < this.outputs.length; ++i) { - var output = this.outputs[i]; - //needs more info about this... - if( options.action_call ) // CREATE A NEW ID FOR THE ACTION - options.action_call = options.action_call + "_seq_" + i; - else - options.action_call = this.id + "_" + (action ? action : "action")+"_seq_"+i+"_"+Math.floor(Math.random()*9999); - this.triggerSlot(i, param, null, options); - } - } - }; - - LiteGraph.registerNodeType("events/sequence", Sequence); - - - //Sequencer for events - function Stepper() { - var that = this; - this.properties = { index: 0 }; - this.addInput("index", "number"); - this.addInput("step", LiteGraph.ACTION); - this.addInput("reset", LiteGraph.ACTION); - this.addOutput("index", "number"); - this.addOutput("", LiteGraph.EVENT); - this.addOutput("", LiteGraph.EVENT); - this.addOutput("", LiteGraph.EVENT,{removable:true}); - this.addWidget("button","+",null,function(){ - that.addOutput("", LiteGraph.EVENT, {removable:true}); - }); - this.size = [120, 120]; - this.flags = { render_box: false }; - } - - Stepper.title = "Stepper"; - Stepper.desc = "Trigger events sequentially when an tick arrives"; - - Stepper.prototype.onDrawBackground = function(ctx) - { - if (this.flags.collapsed) { - return; - } - var index = this.properties.index || 0; - ctx.fillStyle = "#AFB"; - var w = this.size[0]; - var y = (index + 1)* LiteGraph.NODE_SLOT_HEIGHT + 4; - ctx.beginPath(); - ctx.moveTo(w - 30, y); - ctx.lineTo(w - 30, y + LiteGraph.NODE_SLOT_HEIGHT); - ctx.lineTo(w - 15, y + LiteGraph.NODE_SLOT_HEIGHT * 0.5); - ctx.fill(); - } - - Stepper.prototype.onExecute = function() - { - var index = this.getInputData(0); - if(index != null) - { - index = Math.floor(index); - index = clamp( index, 0, this.outputs ? (this.outputs.length - 2) : 0 ); - if( index != this.properties.index ) - { - this.properties.index = index; - this.triggerSlot( index+1 ); - } - } - - this.setOutputData(0, this.properties.index ); - } - - Stepper.prototype.onAction = function(action, param) { - if(action == "reset") - this.properties.index = 0; - else if(action == "step") - { - this.triggerSlot(this.properties.index+1, param); - var n = this.outputs ? this.outputs.length - 1 : 0; - this.properties.index = (this.properties.index + 1) % n; - } - }; - - LiteGraph.registerNodeType("events/stepper", Stepper); - - //Filter events - function FilterEvent() { - this.size = [60, 30]; - this.addInput("event", LiteGraph.ACTION); - this.addOutput("event", LiteGraph.EVENT); - this.properties = { - equal_to: "", - has_property: "", - property_equal_to: "" - }; - } - - FilterEvent.title = "Filter Event"; - FilterEvent.desc = "Blocks events that do not match the filter"; - - FilterEvent.prototype.onAction = function(action, param, options) { - if (param == null) { - return; - } - - if (this.properties.equal_to && this.properties.equal_to != param) { - return; - } - - if (this.properties.has_property) { - var prop = param[this.properties.has_property]; - if (prop == null) { - return; - } - - if ( - this.properties.property_equal_to && - this.properties.property_equal_to != prop - ) { - return; - } - } - - this.triggerSlot(0, param, null, options); - }; - - LiteGraph.registerNodeType("events/filter", FilterEvent); - - - function EventBranch() { - this.addInput("in", LiteGraph.ACTION); - this.addInput("cond", "boolean"); - this.addOutput("true", LiteGraph.EVENT); - this.addOutput("false", LiteGraph.EVENT); - this.size = [120, 60]; - this._value = false; - } - - EventBranch.title = "Branch"; - EventBranch.desc = "If condition is true, outputs triggers true, otherwise false"; - - EventBranch.prototype.onExecute = function() { - this._value = this.getInputData(1); - } - - EventBranch.prototype.onAction = function(action, param, options) { - this._value = this.getInputData(1); - this.triggerSlot(this._value ? 0 : 1, param, null, options); - } - - LiteGraph.registerNodeType("events/branch", EventBranch); - - //Show value inside the debug console - function EventCounter() { - this.addInput("inc", LiteGraph.ACTION); - this.addInput("dec", LiteGraph.ACTION); - this.addInput("reset", LiteGraph.ACTION); - this.addOutput("change", LiteGraph.EVENT); - this.addOutput("num", "number"); - this.addProperty("doCountExecution", false, "boolean", {name: "Count Executions"}); - this.addWidget("toggle","Count Exec.",this.properties.doCountExecution,"doCountExecution"); - this.num = 0; - } - - EventCounter.title = "Counter"; - EventCounter.desc = "Counts events"; - - EventCounter.prototype.getTitle = function() { - if (this.flags.collapsed) { - return String(this.num); - } - return this.title; - }; - - EventCounter.prototype.onAction = function(action, param, options) { - var v = this.num; - if (action == "inc") { - this.num += 1; - } else if (action == "dec") { - this.num -= 1; - } else if (action == "reset") { - this.num = 0; - } - if (this.num != v) { - this.trigger("change", this.num); - } - }; - - EventCounter.prototype.onDrawBackground = function(ctx) { - if (this.flags.collapsed) { - return; - } - ctx.fillStyle = "#AAA"; - ctx.font = "20px Arial"; - ctx.textAlign = "center"; - ctx.fillText(this.num, this.size[0] * 0.5, this.size[1] * 0.5); - }; - - EventCounter.prototype.onExecute = function() { - if(this.properties.doCountExecution){ - this.num += 1; - } - this.setOutputData(1, this.num); - }; - - LiteGraph.registerNodeType("events/counter", EventCounter); - - //Show value inside the debug console - function DelayEvent() { - this.size = [60, 30]; - this.addProperty("time_in_ms", 1000); - this.addInput("event", LiteGraph.ACTION); - this.addOutput("on_time", LiteGraph.EVENT); - - this._pending = []; - } - - DelayEvent.title = "Delay"; - DelayEvent.desc = "Delays one event"; - - DelayEvent.prototype.onAction = function(action, param, options) { - var time = this.properties.time_in_ms; - if (time <= 0) { - this.trigger(null, param, options); - } else { - this._pending.push([time, param]); - } - }; - - DelayEvent.prototype.onExecute = function(param, options) { - var dt = this.graph.elapsed_time * 1000; //in ms - - if (this.isInputConnected(1)) { - this.properties.time_in_ms = this.getInputData(1); - } - - for (var i = 0; i < this._pending.length; ++i) { - var actionPass = this._pending[i]; - actionPass[0] -= dt; - if (actionPass[0] > 0) { - continue; - } - - //remove - this._pending.splice(i, 1); - --i; - - //trigger - this.trigger(null, actionPass[1], options); - } - }; - - DelayEvent.prototype.onGetInputs = function() { - return [["event", LiteGraph.ACTION], ["time_in_ms", "number"]]; - }; - - LiteGraph.registerNodeType("events/delay", DelayEvent); - - //Show value inside the debug console - function TimerEvent() { - this.addProperty("interval", 1000); - this.addProperty("event", "tick"); - this.addOutput("on_tick", LiteGraph.EVENT); - this.time = 0; - this.last_interval = 1000; - this.triggered = false; - } - - TimerEvent.title = "Timer"; - TimerEvent.desc = "Sends an event every N milliseconds"; - - TimerEvent.prototype.onStart = function() { - this.time = 0; - }; - - TimerEvent.prototype.getTitle = function() { - return "Timer: " + this.last_interval.toString() + "ms"; - }; - - TimerEvent.on_color = "#AAA"; - TimerEvent.off_color = "#222"; - - TimerEvent.prototype.onDrawBackground = function() { - this.boxcolor = this.triggered - ? TimerEvent.on_color - : TimerEvent.off_color; - this.triggered = false; - }; - - TimerEvent.prototype.onExecute = function() { - var dt = this.graph.elapsed_time * 1000; //in ms - - var trigger = this.time == 0; - - this.time += dt; - this.last_interval = Math.max( - 1, - this.getInputOrProperty("interval") | 0 - ); - - if ( - !trigger && - (this.time < this.last_interval || isNaN(this.last_interval)) - ) { - if (this.inputs && this.inputs.length > 1 && this.inputs[1]) { - this.setOutputData(1, false); - } - return; - } - - this.triggered = true; - this.time = this.time % this.last_interval; - this.trigger("on_tick", this.properties.event); - if (this.inputs && this.inputs.length > 1 && this.inputs[1]) { - this.setOutputData(1, true); - } - }; - - TimerEvent.prototype.onGetInputs = function() { - return [["interval", "number"]]; - }; - - TimerEvent.prototype.onGetOutputs = function() { - return [["tick", "boolean"]]; - }; - - LiteGraph.registerNodeType("events/timer", TimerEvent); - - - - function SemaphoreEvent() { - this.addInput("go", LiteGraph.ACTION ); - this.addInput("green", LiteGraph.ACTION ); - this.addInput("red", LiteGraph.ACTION ); - this.addOutput("continue", LiteGraph.EVENT ); - this.addOutput("blocked", LiteGraph.EVENT ); - this.addOutput("is_green", "boolean" ); - this._ready = false; - this.properties = {}; - var that = this; - this.addWidget("button","reset","",function(){ - that._ready = false; - }); - } - - SemaphoreEvent.title = "Semaphore Event"; - SemaphoreEvent.desc = "Until both events are not triggered, it doesnt continue."; - - SemaphoreEvent.prototype.onExecute = function() - { - this.setOutputData(1,this._ready); - this.boxcolor = this._ready ? "#9F9" : "#FA5"; - } - - SemaphoreEvent.prototype.onAction = function(action, param) { - if( action == "go" ) - this.triggerSlot( this._ready ? 0 : 1 ); - else if( action == "green" ) - this._ready = true; - else if( action == "red" ) - this._ready = false; - }; - - LiteGraph.registerNodeType("events/semaphore", SemaphoreEvent); - - function OnceEvent() { - this.addInput("in", LiteGraph.ACTION ); - this.addInput("reset", LiteGraph.ACTION ); - this.addOutput("out", LiteGraph.EVENT ); - this._once = false; - this.properties = {}; - var that = this; - this.addWidget("button","reset","",function(){ - that._once = false; - }); - } - - OnceEvent.title = "Once"; - OnceEvent.desc = "Only passes an event once, then gets locked"; - - OnceEvent.prototype.onAction = function(action, param) { - if( action == "in" && !this._once ) - { - this._once = true; - this.triggerSlot( 0, param ); - } - else if( action == "reset" ) - this._once = false; - }; - - LiteGraph.registerNodeType("events/once", OnceEvent); - - function DataStore() { - this.addInput("data", 0); - this.addInput("assign", LiteGraph.ACTION); - this.addOutput("data", 0); - this._last_value = null; - this.properties = { data: null, serialize: true }; - var that = this; - this.addWidget("button","store","",function(){ - that.properties.data = that._last_value; - }); - } - - DataStore.title = "Data Store"; - DataStore.desc = "Stores data and only changes when event is received"; - - DataStore.prototype.onExecute = function() - { - this._last_value = this.getInputData(0); - this.setOutputData(0, this.properties.data ); - } - - DataStore.prototype.onAction = function(action, param, options) { - this.properties.data = this._last_value; - }; - - DataStore.prototype.onSerialize = function(o) - { - if(o.data == null) - return; - if(this.properties.serialize == false || (o.data.constructor !== String && o.data.constructor !== Number && o.data.constructor !== Boolean && o.data.constructor !== Array && o.data.constructor !== Object )) - o.data = null; - } - - LiteGraph.registerNodeType("basic/data_store", DataStore); - - - -})(this); diff --git a/src/nodes/geometry.js b/src/nodes/geometry.js deleted file mode 100755 index 313b14510..000000000 --- a/src/nodes/geometry.js +++ /dev/null @@ -1,1910 +0,0 @@ -(function(global) { - var LiteGraph = global.LiteGraph; - - var view_matrix = new Float32Array(16); - var projection_matrix = new Float32Array(16); - var viewprojection_matrix = new Float32Array(16); - var model_matrix = new Float32Array(16); - var global_uniforms = { - u_view: view_matrix, - u_projection: projection_matrix, - u_viewprojection: viewprojection_matrix, - u_model: model_matrix - }; - - LiteGraph.LGraphRender = { - onRequestCameraMatrices: null //overwrite with your 3D engine specifics, it will receive (view_matrix, projection_matrix,viewprojection_matrix) and must be filled - }; - - function generateGeometryId() { - return (Math.random() * 100000)|0; - } - - function LGraphPoints3D() { - - this.addInput("obj", ""); - this.addInput("radius", "number"); - - this.addOutput("out", "geometry"); - this.addOutput("points", "[vec3]"); - this.properties = { - radius: 1, - num_points: 4096, - generate_normals: true, - regular: false, - mode: LGraphPoints3D.SPHERE, - force_update: false - }; - - this.points = new Float32Array( this.properties.num_points * 3 ); - this.normals = new Float32Array( this.properties.num_points * 3 ); - this.must_update = true; - this.version = 0; - - var that = this; - this.addWidget("button","update",null, function(){ that.must_update = true; }); - - this.geometry = { - vertices: null, - _id: generateGeometryId() - } - - this._old_obj = null; - this._last_radius = null; - } - - global.LGraphPoints3D = LGraphPoints3D; - - LGraphPoints3D.RECTANGLE = 1; - LGraphPoints3D.CIRCLE = 2; - - LGraphPoints3D.CUBE = 10; - LGraphPoints3D.SPHERE = 11; - LGraphPoints3D.HEMISPHERE = 12; - LGraphPoints3D.INSIDE_SPHERE = 13; - - LGraphPoints3D.OBJECT = 20; - LGraphPoints3D.OBJECT_UNIFORMLY = 21; - LGraphPoints3D.OBJECT_INSIDE = 22; - - LGraphPoints3D.MODE_VALUES = { "rectangle":LGraphPoints3D.RECTANGLE, "circle":LGraphPoints3D.CIRCLE, "cube":LGraphPoints3D.CUBE, "sphere":LGraphPoints3D.SPHERE, "hemisphere":LGraphPoints3D.HEMISPHERE, "inside_sphere":LGraphPoints3D.INSIDE_SPHERE, "object":LGraphPoints3D.OBJECT, "object_uniformly":LGraphPoints3D.OBJECT_UNIFORMLY, "object_inside":LGraphPoints3D.OBJECT_INSIDE }; - - LGraphPoints3D.widgets_info = { - mode: { widget: "combo", values: LGraphPoints3D.MODE_VALUES } - }; - - LGraphPoints3D.title = "list of points"; - LGraphPoints3D.desc = "returns an array of points"; - - LGraphPoints3D.prototype.onPropertyChanged = function(name,value) - { - this.must_update = true; - } - - LGraphPoints3D.prototype.onExecute = function() { - - var obj = this.getInputData(0); - if( obj != this._old_obj || (obj && obj._version != this._old_obj_version) ) - { - this._old_obj = obj; - this.must_update = true; - } - - var radius = this.getInputData(1); - if(radius == null) - radius = this.properties.radius; - if( this._last_radius != radius ) - { - this._last_radius = radius; - this.must_update = true; - } - - if(this.must_update || this.properties.force_update ) - { - this.must_update = false; - this.updatePoints(); - } - - this.geometry.vertices = this.points; - this.geometry.normals = this.normals; - this.geometry._version = this.version; - - this.setOutputData( 0, this.geometry ); - } - - LGraphPoints3D.prototype.updatePoints = function() { - var num_points = this.properties.num_points|0; - if(num_points < 1) - num_points = 1; - - if(!this.points || this.points.length != num_points * 3) - this.points = new Float32Array( num_points * 3 ); - - if(this.properties.generate_normals) - { - if (!this.normals || this.normals.length != this.points.length) - this.normals = new Float32Array( this.points.length ); - } - else - this.normals = null; - - var radius = this._last_radius || this.properties.radius; - var mode = this.properties.mode; - - var obj = this.getInputData(0); - this._old_obj_version = obj ? obj._version : null; - - this.points = LGraphPoints3D.generatePoints( radius, num_points, mode, this.points, this.normals, this.properties.regular, obj ); - - this.version++; - } - - //global - LGraphPoints3D.generatePoints = function( radius, num_points, mode, points, normals, regular, obj ) - { - var size = num_points * 3; - if(!points || points.length != size) - points = new Float32Array( size ); - var temp = new Float32Array(3); - var UP = new Float32Array([0,1,0]); - - if(regular) - { - if( mode == LGraphPoints3D.RECTANGLE) - { - var side = Math.floor(Math.sqrt(num_points)); - for(var i = 0; i < side; ++i) - for(var j = 0; j < side; ++j) - { - var pos = i*3 + j*3*side; - points[pos] = ((i/side) - 0.5) * radius * 2; - points[pos+1] = 0; - points[pos+2] = ((j/side) - 0.5) * radius * 2; - } - points = new Float32Array( points.subarray(0,side*side*3) ); - if(normals) - { - for(var i = 0; i < normals.length; i+=3) - normals.set(UP, i); - } - } - else if( mode == LGraphPoints3D.SPHERE) - { - var side = Math.floor(Math.sqrt(num_points)); - for(var i = 0; i < side; ++i) - for(var j = 0; j < side; ++j) - { - var pos = i*3 + j*3*side; - polarToCartesian( temp, (i/side) * 2 * Math.PI, ((j/side) - 0.5) * 2 * Math.PI, radius ); - points[pos] = temp[0]; - points[pos+1] = temp[1]; - points[pos+2] = temp[2]; - } - points = new Float32Array( points.subarray(0,side*side*3) ); - if(normals) - LGraphPoints3D.generateSphericalNormals( points, normals ); - } - else if( mode == LGraphPoints3D.CIRCLE) - { - for(var i = 0; i < size; i+=3) - { - var angle = 2 * Math.PI * (i/size); - points[i] = Math.cos( angle ) * radius; - points[i+1] = 0; - points[i+2] = Math.sin( angle ) * radius; - } - if(normals) - { - for(var i = 0; i < normals.length; i+=3) - normals.set(UP, i); - } - } - } - else //non regular - { - if( mode == LGraphPoints3D.RECTANGLE) - { - for(var i = 0; i < size; i+=3) - { - points[i] = (Math.random() - 0.5) * radius * 2; - points[i+1] = 0; - points[i+2] = (Math.random() - 0.5) * radius * 2; - } - if(normals) - { - for(var i = 0; i < normals.length; i+=3) - normals.set(UP, i); - } - } - else if( mode == LGraphPoints3D.CUBE) - { - for(var i = 0; i < size; i+=3) - { - points[i] = (Math.random() - 0.5) * radius * 2; - points[i+1] = (Math.random() - 0.5) * radius * 2; - points[i+2] = (Math.random() - 0.5) * radius * 2; - } - if(normals) - { - for(var i = 0; i < normals.length; i+=3) - normals.set(UP, i); - } - } - else if( mode == LGraphPoints3D.SPHERE) - { - LGraphPoints3D.generateSphere( points, size, radius ); - if(normals) - LGraphPoints3D.generateSphericalNormals( points, normals ); - } - else if( mode == LGraphPoints3D.HEMISPHERE) - { - LGraphPoints3D.generateHemisphere( points, size, radius ); - if(normals) - LGraphPoints3D.generateSphericalNormals( points, normals ); - } - else if( mode == LGraphPoints3D.CIRCLE) - { - LGraphPoints3D.generateInsideCircle( points, size, radius ); - if(normals) - LGraphPoints3D.generateSphericalNormals( points, normals ); - } - else if( mode == LGraphPoints3D.INSIDE_SPHERE) - { - LGraphPoints3D.generateInsideSphere( points, size, radius ); - if(normals) - LGraphPoints3D.generateSphericalNormals( points, normals ); - } - else if( mode == LGraphPoints3D.OBJECT) - { - LGraphPoints3D.generateFromObject( points, normals, size, obj, false ); - } - else if( mode == LGraphPoints3D.OBJECT_UNIFORMLY) - { - LGraphPoints3D.generateFromObject( points, normals, size, obj, true ); - } - else if( mode == LGraphPoints3D.OBJECT_INSIDE) - { - LGraphPoints3D.generateFromInsideObject( points, size, obj ); - //if(normals) - // LGraphPoints3D.generateSphericalNormals( points, normals ); - } - else - console.warn("wrong mode in LGraphPoints3D"); - } - - return points; - } - - LGraphPoints3D.generateSphericalNormals = function(points, normals) - { - var temp = new Float32Array(3); - for(var i = 0; i < normals.length; i+=3) - { - temp[0] = points[i]; - temp[1] = points[i+1]; - temp[2] = points[i+2]; - vec3.normalize(temp,temp); - normals.set(temp,i); - } - } - - LGraphPoints3D.generateSphere = function (points, size, radius) - { - for(var i = 0; i < size; i+=3) - { - var r1 = Math.random(); - var r2 = Math.random(); - var x = 2 * Math.cos( 2 * Math.PI * r1 ) * Math.sqrt( r2 * (1-r2) ); - var y = 1 - 2 * r2; - var z = 2 * Math.sin( 2 * Math.PI * r1 ) * Math.sqrt( r2 * (1-r2) ); - points[i] = x * radius; - points[i+1] = y * radius; - points[i+2] = z * radius; - } - } - - LGraphPoints3D.generateHemisphere = function (points, size, radius) - { - for(var i = 0; i < size; i+=3) - { - var r1 = Math.random(); - var r2 = Math.random(); - var x = Math.cos( 2 * Math.PI * r1 ) * Math.sqrt(1 - r2*r2 ); - var y = r2; - var z = Math.sin( 2 * Math.PI * r1 ) * Math.sqrt(1 - r2*r2 ); - points[i] = x * radius; - points[i+1] = y * radius; - points[i+2] = z * radius; - } - } - - LGraphPoints3D.generateInsideCircle = function (points, size, radius) - { - for(var i = 0; i < size; i+=3) - { - var r1 = Math.random(); - var r2 = Math.random(); - var x = Math.cos( 2 * Math.PI * r1 ) * Math.sqrt(1 - r2*r2 ); - var y = r2; - var z = Math.sin( 2 * Math.PI * r1 ) * Math.sqrt(1 - r2*r2 ); - points[i] = x * radius; - points[i+1] = 0; - points[i+2] = z * radius; - } - } - - LGraphPoints3D.generateInsideSphere = function (points, size, radius) - { - for(var i = 0; i < size; i+=3) - { - var u = Math.random(); - var v = Math.random(); - var theta = u * 2.0 * Math.PI; - var phi = Math.acos(2.0 * v - 1.0); - var r = Math.cbrt(Math.random()) * radius; - var sinTheta = Math.sin(theta); - var cosTheta = Math.cos(theta); - var sinPhi = Math.sin(phi); - var cosPhi = Math.cos(phi); - points[i] = r * sinPhi * cosTheta; - points[i+1] = r * sinPhi * sinTheta; - points[i+2] = r * cosPhi; - } - } - - function findRandomTriangle( areas, f ) - { - var l = areas.length; - var imin = 0; - var imid = 0; - var imax = l; - - if(l == 0) - return -1; - if(l == 1) - return 0; - //dichotomic search - while (imax >= imin) - { - imid = ((imax + imin)*0.5)|0; - var t = areas[ imid ]; - if( t == f ) - return imid; - if( imin == (imax - 1) ) - return imin; - if (t < f) - imin = imid; - else - imax = imid; - } - return imid; - } - - LGraphPoints3D.generateFromObject = function( points, normals, size, obj, evenly ) - { - if(!obj) - return; - - var vertices = null; - var mesh_normals = null; - var indices = null; - var areas = null; - if( obj.constructor === GL.Mesh ) - { - vertices = obj.vertexBuffers.vertices.data; - mesh_normals = obj.vertexBuffers.normals ? obj.vertexBuffers.normals.data : null; - indices = obj.indexBuffers.indices ? obj.indexBuffers.indices.data : null; - if(!indices) - indices = obj.indexBuffers.triangles ? obj.indexBuffers.triangles.data : null; - } - if(!vertices) - return null; - var num_triangles = indices ? indices.length / 3 : vertices.length / (3*3); - var total_area = 0; //sum of areas of all triangles - - if(evenly) - { - areas = new Float32Array(num_triangles); //accum - for(var i = 0; i < num_triangles; ++i) - { - if(indices) - { - a = indices[i*3]*3; - b = indices[i*3+1]*3; - c = indices[i*3+2]*3; - } - else - { - a = i*9; - b = i*9+3; - c = i*9+6; - } - var P1 = vertices.subarray(a,a+3); - var P2 = vertices.subarray(b,b+3); - var P3 = vertices.subarray(c,c+3); - var aL = vec3.distance( P1, P2 ); - var bL = vec3.distance( P2, P3 ); - var cL = vec3.distance( P3, P1 ); - var s = (aL + bL+ cL) / 2; - total_area += Math.sqrt(s * (s - aL) * (s - bL) * (s - cL)); - areas[i] = total_area; - } - for(var i = 0; i < num_triangles; ++i) //normalize - areas[i] /= total_area; - } - - for(var i = 0; i < size; i+=3) - { - var r = Math.random(); - var index = evenly ? findRandomTriangle( areas, r ) : Math.floor(r * num_triangles ); - //get random triangle - var a = 0; - var b = 0; - var c = 0; - if(indices) - { - a = indices[index*3]*3; - b = indices[index*3+1]*3; - c = indices[index*3+2]*3; - } - else - { - a = index*9; - b = index*9+3; - c = index*9+6; - } - var s = Math.random(); - var t = Math.random(); - var sqrt_s = Math.sqrt(s); - var af = 1 - sqrt_s; - var bf = sqrt_s * ( 1 - t); - var cf = t * sqrt_s; - points[i] = af * vertices[a] + bf*vertices[b] + cf*vertices[c]; - points[i+1] = af * vertices[a+1] + bf*vertices[b+1] + cf*vertices[c+1]; - points[i+2] = af * vertices[a+2] + bf*vertices[b+2] + cf*vertices[c+2]; - if(normals && mesh_normals) - { - normals[i] = af * mesh_normals[a] + bf*mesh_normals[b] + cf*mesh_normals[c]; - normals[i+1] = af * mesh_normals[a+1] + bf*mesh_normals[b+1] + cf*mesh_normals[c+1]; - normals[i+2] = af * mesh_normals[a+2] + bf*mesh_normals[b+2] + cf*mesh_normals[c+2]; - var N = normals.subarray(i,i+3); - vec3.normalize(N,N); - } - } - } - - LGraphPoints3D.generateFromInsideObject = function( points, size, mesh ) - { - if(!mesh || mesh.constructor !== GL.Mesh) - return; - - var aabb = mesh.getBoundingBox(); - if(!mesh.octree) - mesh.octree = new GL.Octree( mesh ); - var octree = mesh.octree; - var origin = vec3.create(); - var direction = vec3.fromValues(1,0,0); - var temp = vec3.create(); - var i = 0; - var tries = 0; - while(i < size && tries < points.length * 10) //limit to avoid problems - { - tries += 1 - var r = vec3.random(temp); //random point inside the aabb - r[0] = (r[0] * 2 - 1) * aabb[3] + aabb[0]; - r[1] = (r[1] * 2 - 1) * aabb[4] + aabb[1]; - r[2] = (r[2] * 2 - 1) * aabb[5] + aabb[2]; - origin.set(r); - var hit = octree.testRay( origin, direction, 0, 10000, true, GL.Octree.ALL ); - if(!hit || hit.length % 2 == 0) //not inside - continue; - points.set( r, i ); - i+=3; - } - } - - LiteGraph.registerNodeType( "geometry/points3D", LGraphPoints3D ); - - - - function LGraphPointsToInstances() { - this.addInput("points", "geometry"); - this.addOutput("instances", "[mat4]"); - this.properties = { - mode: 1, - autoupdate: true - }; - - this.must_update = true; - this.matrices = []; - this.first_time = true; - } - - LGraphPointsToInstances.NORMAL = 0; - LGraphPointsToInstances.VERTICAL = 1; - LGraphPointsToInstances.SPHERICAL = 2; - LGraphPointsToInstances.RANDOM = 3; - LGraphPointsToInstances.RANDOM_VERTICAL = 4; - - LGraphPointsToInstances.modes = {"normal":0,"vertical":1,"spherical":2,"random":3,"random_vertical":4}; - LGraphPointsToInstances.widgets_info = { - mode: { widget: "combo", values: LGraphPointsToInstances.modes } - }; - - LGraphPointsToInstances.title = "points to inst"; - - LGraphPointsToInstances.prototype.onExecute = function() - { - var geo = this.getInputData(0); - if( !geo ) - { - this.setOutputData(0,null); - return; - } - - if( !this.isOutputConnected(0) ) - return; - - var has_changed = (geo._version != this._version || geo._id != this._geometry_id); - - if( has_changed && this.properties.autoupdate || this.first_time ) - { - this.first_time = false; - this.updateInstances( geo ); - } - - this.setOutputData( 0, this.matrices ); - } - - LGraphPointsToInstances.prototype.updateInstances = function( geometry ) - { - var vertices = geometry.vertices; - if(!vertices) - return null; - var normals = geometry.normals; - - var matrices = this.matrices; - var num_points = vertices.length / 3; - if( matrices.length != num_points) - matrices.length = num_points; - var identity = mat4.create(); - var temp = vec3.create(); - var zero = vec3.create(); - var UP = vec3.fromValues(0,1,0); - var FRONT = vec3.fromValues(0,0,-1); - var RIGHT = vec3.fromValues(1,0,0); - var R = quat.create(); - - var front = vec3.create(); - var right = vec3.create(); - var top = vec3.create(); - - for(var i = 0; i < vertices.length; i += 3) - { - var index = i/3; - var m = matrices[index]; - if(!m) - m = matrices[index] = mat4.create(); - m.set( identity ); - var point = vertices.subarray(i,i+3); - - switch(this.properties.mode) - { - case LGraphPointsToInstances.NORMAL: - mat4.setTranslation( m, point ); - if(normals) - { - var normal = normals.subarray(i,i+3); - top.set( normal ); - vec3.normalize( top, top ); - vec3.cross( right, FRONT, top ); - vec3.normalize( right, right ); - vec3.cross( front, right, top ); - vec3.normalize( front, front ); - m.set(right,0); - m.set(top,4); - m.set(front,8); - mat4.setTranslation( m, point ); - } - break; - case LGraphPointsToInstances.VERTICAL: - mat4.setTranslation( m, point ); - break; - case LGraphPointsToInstances.SPHERICAL: - front.set( point ); - vec3.normalize( front, front ); - vec3.cross( right, UP, front ); - vec3.normalize( right, right ); - vec3.cross( top, front, right ); - vec3.normalize( top, top ); - m.set(right,0); - m.set(top,4); - m.set(front,8); - mat4.setTranslation( m, point ); - break; - case LGraphPointsToInstances.RANDOM: - temp[0] = Math.random()*2 - 1; - temp[1] = Math.random()*2 - 1; - temp[2] = Math.random()*2 - 1; - vec3.normalize( temp, temp ); - quat.setAxisAngle( R, temp, Math.random() * 2 * Math.PI ); - mat4.fromQuat(m, R); - mat4.setTranslation( m, point ); - break; - case LGraphPointsToInstances.RANDOM_VERTICAL: - quat.setAxisAngle( R, UP, Math.random() * 2 * Math.PI ); - mat4.fromQuat(m, R); - mat4.setTranslation( m, point ); - break; - } - } - - this._version = geometry._version; - this._geometry_id = geometry._id; - } - - LiteGraph.registerNodeType( "geometry/points_to_instances", LGraphPointsToInstances ); - - - function LGraphGeometryTransform() { - this.addInput("in", "geometry,[mat4]"); - this.addInput("mat4", "mat4"); - this.addOutput("out", "geometry"); - this.properties = {}; - - this.geometry = { - type: "triangles", - vertices: null, - _id: generateGeometryId(), - _version: 0 - }; - - this._last_geometry_id = -1; - this._last_version = -1; - this._last_key = ""; - - this.must_update = true; - } - - LGraphGeometryTransform.title = "Transform"; - - LGraphGeometryTransform.prototype.onExecute = function() { - - var input = this.getInputData(0); - var model = this.getInputData(1); - - if(!input) - return; - - //array of matrices - if(input.constructor === Array) - { - if(input.length == 0) - return; - this.outputs[0].type = "[mat4]"; - if( !this.isOutputConnected(0) ) - return; - - if(!model) - { - this.setOutputData(0,input); - return; - } - - if(!this._output) - this._output = new Array(); - if(this._output.length != input.length) - this._output.length = input.length; - for(var i = 0; i < input.length; ++i) - { - var m = this._output[i]; - if(!m) - m = this._output[i] = mat4.create(); - mat4.multiply(m,input[i],model); - } - this.setOutputData(0,this._output); - return; - } - - //geometry - if(!input.vertices || !input.vertices.length) - return; - var geo = input; - this.outputs[0].type = "geometry"; - if( !this.isOutputConnected(0) ) - return; - if(!model) - { - this.setOutputData(0,geo); - return; - } - - var key = typedArrayToArray(model).join(","); - - if( this.must_update || geo._id != this._last_geometry_id || geo._version != this._last_version || key != this._last_key ) - { - this.updateGeometry(geo, model); - this._last_key = key; - this._last_version = geo._version; - this._last_geometry_id = geo._id; - this.must_update = false; - } - - this.setOutputData(0,this.geometry); - } - - LGraphGeometryTransform.prototype.updateGeometry = function(geometry, model) { - var old_vertices = geometry.vertices; - var vertices = this.geometry.vertices; - if( !vertices || vertices.length != old_vertices.length ) - vertices = this.geometry.vertices = new Float32Array( old_vertices.length ); - var temp = vec3.create(); - - for(var i = 0, l = vertices.length; i < l; i+=3) - { - temp[0] = old_vertices[i]; temp[1] = old_vertices[i+1]; temp[2] = old_vertices[i+2]; - mat4.multiplyVec3( temp, model, temp ); - vertices[i] = temp[0]; vertices[i+1] = temp[1]; vertices[i+2] = temp[2]; - } - - if(geometry.normals) - { - if( !this.geometry.normals || this.geometry.normals.length != geometry.normals.length ) - this.geometry.normals = new Float32Array( geometry.normals.length ); - var normals = this.geometry.normals; - var normal_model = mat4.invert(mat4.create(), model); - if(normal_model) - mat4.transpose(normal_model, normal_model); - var old_normals = geometry.normals; - for(var i = 0, l = normals.length; i < l; i+=3) - { - temp[0] = old_normals[i]; temp[1] = old_normals[i+1]; temp[2] = old_normals[i+2]; - mat4.multiplyVec3( temp, normal_model, temp ); - normals[i] = temp[0]; normals[i+1] = temp[1]; normals[i+2] = temp[2]; - } - } - - this.geometry.type = geometry.type; - this.geometry._version++; - } - - LiteGraph.registerNodeType( "geometry/transform", LGraphGeometryTransform ); - - - function LGraphGeometryPolygon() { - this.addInput("sides", "number"); - this.addInput("radius", "number"); - this.addOutput("out", "geometry"); - this.properties = { sides: 6, radius: 1, uvs: false } - - this.geometry = { - type: "line_loop", - vertices: null, - _id: generateGeometryId() - }; - this.geometry_id = -1; - this.version = -1; - this.must_update = true; - - this.last_info = { sides: -1, radius: -1 }; - } - - LGraphGeometryPolygon.title = "Polygon"; - - LGraphGeometryPolygon.prototype.onExecute = function() { - - if( !this.isOutputConnected(0) ) - return; - - var sides = this.getInputOrProperty("sides"); - var radius = this.getInputOrProperty("radius"); - sides = Math.max(3,sides)|0; - - //update - if( this.last_info.sides != sides || this.last_info.radius != radius ) - this.updateGeometry(sides, radius); - - this.setOutputData(0,this.geometry); - } - - LGraphGeometryPolygon.prototype.updateGeometry = function(sides, radius) { - var num = 3*sides; - var vertices = this.geometry.vertices; - if( !vertices || vertices.length != num ) - vertices = this.geometry.vertices = new Float32Array( 3*sides ); - var delta = (Math.PI * 2) / sides; - var gen_uvs = this.properties.uvs; - if(gen_uvs) - { - uvs = this.geometry.coords = new Float32Array( 3*sides ); - } - - - for(var i = 0; i < sides; ++i) - { - var angle = delta * -i; - var x = Math.cos( angle ) * radius; - var y = 0; - var z = Math.sin( angle ) * radius; - vertices[i*3] = x; - vertices[i*3+1] = y; - vertices[i*3+2] = z; - - if(gen_uvs) - { - - - } - } - this.geometry._id = ++this.geometry_id; - this.geometry._version = ++this.version; - this.last_info.sides = sides; - this.last_info.radius = radius; - } - - LiteGraph.registerNodeType( "geometry/polygon", LGraphGeometryPolygon ); - - - function LGraphGeometryExtrude() { - - this.addInput("", "geometry"); - this.addOutput("", "geometry"); - this.properties = { top_cap: true, bottom_cap: true, offset: [0,100,0] }; - this.version = -1; - - this._last_geo_version = -1; - this._must_update = true; - } - - LGraphGeometryExtrude.title = "extrude"; - - LGraphGeometryExtrude.prototype.onPropertyChanged = function(name, value) - { - this._must_update = true; - } - - LGraphGeometryExtrude.prototype.onExecute = function() - { - var geo = this.getInputData(0); - if( !geo || !this.isOutputConnected(0) ) - return; - - if(geo.version != this._last_geo_version || this._must_update) - { - this._geo = this.extrudeGeometry( geo, this._geo ); - if(this._geo) - this._geo.version = this.version++; - this._must_update = false; - } - - this.setOutputData(0, this._geo); - } - - LGraphGeometryExtrude.prototype.extrudeGeometry = function( geo ) - { - //for every pair of vertices - var vertices = geo.vertices; - var num_points = vertices.length / 3; - - var tempA = vec3.create(); - var tempB = vec3.create(); - var tempC = vec3.create(); - var tempD = vec3.create(); - var offset = new Float32Array( this.properties.offset ); - - if(geo.type == "line_loop") - { - var new_vertices = new Float32Array( num_points * 6 * 3 ); //every points become 6 ( caps not included ) - var npos = 0; - for(var i = 0, l = vertices.length; i < l; i += 3) - { - tempA[0] = vertices[i]; tempA[1] = vertices[i+1]; tempA[2] = vertices[i+2]; - - if( i+3 < l ) //loop - { - tempB[0] = vertices[i+3]; tempB[1] = vertices[i+4]; tempB[2] = vertices[i+5]; - } - else - { - tempB[0] = vertices[0]; tempB[1] = vertices[1]; tempB[2] = vertices[2]; - } - - vec3.add( tempC, tempA, offset ); - vec3.add( tempD, tempB, offset ); - - new_vertices.set( tempA, npos ); npos += 3; - new_vertices.set( tempB, npos ); npos += 3; - new_vertices.set( tempC, npos ); npos += 3; - - new_vertices.set( tempB, npos ); npos += 3; - new_vertices.set( tempD, npos ); npos += 3; - new_vertices.set( tempC, npos ); npos += 3; - } - } - - var out_geo = { - _id: generateGeometryId(), - type: "triangles", - vertices: new_vertices - }; - - return out_geo; - } - - LiteGraph.registerNodeType( "geometry/extrude", LGraphGeometryExtrude ); - - - function LGraphGeometryEval() { - this.addInput("in", "geometry"); - this.addOutput("out", "geometry"); - - this.properties = { - code: "V[1] += 0.01 * Math.sin(I + T*0.001);", - execute_every_frame: false - }; - - this.geometry = null; - this.geometry_id = -1; - this.version = -1; - this.must_update = true; - - this.vertices = null; - this.func = null; - } - - LGraphGeometryEval.title = "geoeval"; - LGraphGeometryEval.desc = "eval code"; - - LGraphGeometryEval.widgets_info = { - code: { widget: "code" } - }; - - LGraphGeometryEval.prototype.onConfigure = function(o) - { - this.compileCode(); - } - - LGraphGeometryEval.prototype.compileCode = function() - { - if(!this.properties.code) - return; - - try - { - this.func = new Function("V","I","T", this.properties.code); - this.boxcolor = "#AFA"; - this.must_update = true; - } - catch (err) - { - this.boxcolor = "red"; - } - } - - LGraphGeometryEval.prototype.onPropertyChanged = function(name, value) - { - if(name == "code") - { - this.properties.code = value; - this.compileCode(); - } - } - - LGraphGeometryEval.prototype.onExecute = function() { - var geometry = this.getInputData(0); - if(!geometry) - return; - - if(!this.func) - { - this.setOutputData(0,geometry); - return; - } - - if( this.geometry_id != geometry._id || this.version != geometry._version || this.must_update || this.properties.execute_every_frame ) - { - this.must_update = false; - this.geometry_id = geometry._id; - if(this.properties.execute_every_frame) - this.version++; - else - this.version = geometry._version; - var func = this.func; - var T = getTime(); - - //clone - if(!this.geometry) - this.geometry = {}; - for(var i in geometry) - { - if(geometry[i] == null) - continue; - if( geometry[i].constructor == Float32Array ) - this.geometry[i] = new Float32Array( geometry[i] ); - else - this.geometry[i] = geometry[i]; - } - this.geometry._id = geometry._id; - if(this.properties.execute_every_frame) - this.geometry._version = this.version; - else - this.geometry._version = geometry._version + 1; - - var V = vec3.create(); - var vertices = this.vertices; - if(!vertices || this.vertices.length != geometry.vertices.length) - vertices = this.vertices = new Float32Array( geometry.vertices ); - else - vertices.set( geometry.vertices ); - for(var i = 0; i < vertices.length; i+=3) - { - V[0] = vertices[i]; - V[1] = vertices[i+1]; - V[2] = vertices[i+2]; - func(V,i/3,T); - vertices[i] = V[0]; - vertices[i+1] = V[1]; - vertices[i+2] = V[2]; - } - this.geometry.vertices = vertices; - } - - this.setOutputData(0,this.geometry); - } - - LiteGraph.registerNodeType( "geometry/eval", LGraphGeometryEval ); - -/* -function LGraphGeometryDisplace() { - this.addInput("in", "geometry"); - this.addInput("img", "image"); - this.addOutput("out", "geometry"); - - this.properties = { - grid_size: 1 - }; - - this.geometry = null; - this.geometry_id = -1; - this.version = -1; - this.must_update = true; - - this.vertices = null; - } - - LGraphGeometryDisplace.title = "displace"; - LGraphGeometryDisplace.desc = "displace points"; - - LGraphGeometryDisplace.prototype.onExecute = function() { - var geometry = this.getInputData(0); - var image = this.getInputData(1); - if(!geometry) - return; - - if(!image) - { - this.setOutputData(0,geometry); - return; - } - - if( this.geometry_id != geometry._id || this.version != geometry._version || this.must_update ) - { - this.must_update = false; - this.geometry_id = geometry._id; - this.version = geometry._version; - - //copy - this.geometry = {}; - for(var i in geometry) - this.geometry[i] = geometry[i]; - this.geometry._id = geometry._id; - this.geometry._version = geometry._version + 1; - - var grid_size = this.properties.grid_size; - if(grid_size != 0) - { - var vertices = this.vertices; - if(!vertices || this.vertices.length != this.geometry.vertices.length) - vertices = this.vertices = new Float32Array( this.geometry.vertices ); - for(var i = 0; i < vertices.length; i+=3) - { - vertices[i] = Math.round(vertices[i]/grid_size) * grid_size; - vertices[i+1] = Math.round(vertices[i+1]/grid_size) * grid_size; - vertices[i+2] = Math.round(vertices[i+2]/grid_size) * grid_size; - } - this.geometry.vertices = vertices; - } - } - - this.setOutputData(0,this.geometry); - } - - LiteGraph.registerNodeType( "geometry/displace", LGraphGeometryDisplace ); -*/ - - function LGraphConnectPoints() { - this.addInput("in", "geometry"); - this.addOutput("out", "geometry"); - - this.properties = { - min_dist: 0.4, - max_dist: 0.5, - max_connections: 0, - probability: 1 - }; - - this.geometry_id = -1; - this.version = -1; - this.my_version = 1; - this.must_update = true; - } - - LGraphConnectPoints.title = "connect points"; - LGraphConnectPoints.desc = "adds indices between near points"; - - LGraphConnectPoints.prototype.onPropertyChanged = function(name,value) - { - this.must_update = true; - } - - LGraphConnectPoints.prototype.onExecute = function() { - var geometry = this.getInputData(0); - if(!geometry) - return; - - if( this.geometry_id != geometry._id || this.version != geometry._version || this.must_update ) - { - this.must_update = false; - this.geometry_id = geometry._id; - this.version = geometry._version; - - //copy - this.geometry = {}; - for(var i in geometry) - this.geometry[i] = geometry[i]; - this.geometry._id = generateGeometryId(); - this.geometry._version = this.my_version++; - - var vertices = geometry.vertices; - var l = vertices.length; - var min_dist = this.properties.min_dist; - var max_dist = this.properties.max_dist; - var probability = this.properties.probability; - var max_connections = this.properties.max_connections; - var indices = []; - - for(var i = 0; i < l; i+=3) - { - var x = vertices[i]; - var y = vertices[i+1]; - var z = vertices[i+2]; - var connections = 0; - for(var j = i+3; j < l; j+=3) - { - var x2 = vertices[j]; - var y2 = vertices[j+1]; - var z2 = vertices[j+2]; - var dist = Math.sqrt( (x-x2)*(x-x2) + (y-y2)*(y-y2) + (z-z2)*(z-z2)); - if(dist > max_dist || dist < min_dist || (probability < 1 && probability < Math.random()) ) - continue; - indices.push(i/3,j/3); - connections += 1; - if(max_connections && connections > max_connections) - break; - } - } - this.geometry.indices = this.indices = new Uint32Array(indices); - } - - if(this.indices && this.indices.length) - { - this.geometry.indices = this.indices; - this.setOutputData( 0, this.geometry ); - } - else - this.setOutputData( 0, null ); - } - - LiteGraph.registerNodeType( "geometry/connectPoints", LGraphConnectPoints ); - - //Works with Litegl.js to create WebGL nodes - if (typeof GL == "undefined") //LiteGL RELATED ********************************************** - return; - - function LGraphToGeometry() { - this.addInput("mesh", "mesh"); - this.addOutput("out", "geometry"); - - this.geometry = {}; - this.last_mesh = null; - } - - LGraphToGeometry.title = "to geometry"; - LGraphToGeometry.desc = "converts a mesh to geometry"; - - LGraphToGeometry.prototype.onExecute = function() { - var mesh = this.getInputData(0); - if(!mesh) - return; - - if(mesh != this.last_mesh) - { - this.last_mesh = mesh; - for(i in mesh.vertexBuffers) - { - var buffer = mesh.vertexBuffers[i]; - this.geometry[i] = buffer.data - } - if(mesh.indexBuffers["triangles"]) - this.geometry.indices = mesh.indexBuffers["triangles"].data; - - this.geometry._id = generateGeometryId(); - this.geometry._version = 0; - } - - this.setOutputData(0,this.geometry); - if(this.geometry) - this.setOutputData(1,this.geometry.vertices); - } - - LiteGraph.registerNodeType( "geometry/toGeometry", LGraphToGeometry ); - - function LGraphGeometryToMesh() { - this.addInput("in", "geometry"); - this.addOutput("mesh", "mesh"); - this.properties = {}; - this.version = -1; - this.mesh = null; - } - - LGraphGeometryToMesh.title = "Geo to Mesh"; - - LGraphGeometryToMesh.prototype.updateMesh = function(geometry) - { - if(!this.mesh) - this.mesh = new GL.Mesh(); - - for(var i in geometry) - { - if(i[0] == "_") - continue; - - var buffer_data = geometry[i]; - - var info = GL.Mesh.common_buffers[i]; - if(!info && i != "indices") //unknown buffer - continue; - var spacing = info ? info.spacing : 3; - var mesh_buffer = this.mesh.vertexBuffers[i]; - - if(!mesh_buffer || mesh_buffer.data.length != buffer_data.length) - { - mesh_buffer = new GL.Buffer( i == "indices" ? GL.ELEMENT_ARRAY_BUFFER : GL.ARRAY_BUFFER, buffer_data, spacing, GL.DYNAMIC_DRAW ); - } - else - { - mesh_buffer.data.set( buffer_data ); - mesh_buffer.upload(GL.DYNAMIC_DRAW); - } - - this.mesh.addBuffer( i, mesh_buffer ); - } - - if(this.mesh.vertexBuffers.normals &&this.mesh.vertexBuffers.normals.data.length != this.mesh.vertexBuffers.vertices.data.length ) - { - var n = new Float32Array([0,1,0]); - var normals = new Float32Array( this.mesh.vertexBuffers.vertices.data.length ); - for(var i = 0; i < normals.length; i+= 3) - normals.set( n, i ); - mesh_buffer = new GL.Buffer( GL.ARRAY_BUFFER, normals, 3 ); - this.mesh.addBuffer( "normals", mesh_buffer ); - } - - this.mesh.updateBoundingBox(); - this.geometry_id = this.mesh.id = geometry._id; - this.version = this.mesh.version = geometry._version; - return this.mesh; - } - - LGraphGeometryToMesh.prototype.onExecute = function() { - - var geometry = this.getInputData(0); - if(!geometry) - return; - if( this.version != geometry._version || this.geometry_id != geometry._id ) - this.updateMesh( geometry ); - this.setOutputData(0, this.mesh); - } - - LiteGraph.registerNodeType( "geometry/toMesh", LGraphGeometryToMesh ); - - function LGraphRenderMesh() { - this.addInput("mesh", "mesh"); - this.addInput("mat4", "mat4"); - this.addInput("tex", "texture"); - - this.properties = { - enabled: true, - primitive: GL.TRIANGLES, - additive: false, - color: [1,1,1], - opacity: 1 - }; - - this.color = vec4.create([1,1,1,1]); - this.model_matrix = mat4.create(); - this.uniforms = { - u_color: this.color, - u_model: this.model_matrix - }; - } - - LGraphRenderMesh.title = "Render Mesh"; - LGraphRenderMesh.desc = "renders a mesh flat"; - - LGraphRenderMesh.PRIMITIVE_VALUES = { "points":GL.POINTS, "lines":GL.LINES, "line_loop":GL.LINE_LOOP,"line_strip":GL.LINE_STRIP, "triangles":GL.TRIANGLES, "triangle_fan":GL.TRIANGLE_FAN, "triangle_strip":GL.TRIANGLE_STRIP }; - - LGraphRenderMesh.widgets_info = { - primitive: { widget: "combo", values: LGraphRenderMesh.PRIMITIVE_VALUES }, - color: { widget: "color" } - }; - - LGraphRenderMesh.prototype.onExecute = function() { - - if(!this.properties.enabled) - return; - - var mesh = this.getInputData(0); - if(!mesh) - return; - - if(!LiteGraph.LGraphRender.onRequestCameraMatrices) - { - console.warn("cannot render geometry, LiteGraph.onRequestCameraMatrices is null, remember to fill this with a callback(view_matrix, projection_matrix,viewprojection_matrix) to use 3D rendering from the graph"); - return; - } - - LiteGraph.LGraphRender.onRequestCameraMatrices( view_matrix, projection_matrix,viewprojection_matrix ); - var shader = null; - var texture = this.getInputData(2); - if(texture) - { - shader = gl.shaders["textured"]; - if(!shader) - shader = gl.shaders["textured"] = new GL.Shader( LGraphRenderPoints.vertex_shader_code, LGraphRenderPoints.fragment_shader_code, { USE_TEXTURE:"" }); - } - else - { - shader = gl.shaders["flat"]; - if(!shader) - shader = gl.shaders["flat"] = new GL.Shader( LGraphRenderPoints.vertex_shader_code, LGraphRenderPoints.fragment_shader_code ); - } - - this.color.set( this.properties.color ); - this.color[3] = this.properties.opacity; - - var model_matrix = this.model_matrix; - var m = this.getInputData(1); - if(m) - model_matrix.set(m); - else - mat4.identity( model_matrix ); - - this.uniforms.u_point_size = 1; - var primitive = this.properties.primitive; - - shader.uniforms( global_uniforms ); - shader.uniforms( this.uniforms ); - - if(this.properties.opacity >= 1) - gl.disable( gl.BLEND ); - else - gl.enable( gl.BLEND ); - gl.enable( gl.DEPTH_TEST ); - if( this.properties.additive ) - { - gl.blendFunc( gl.SRC_ALPHA, gl.ONE ); - gl.depthMask( false ); - } - else - gl.blendFunc( gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA ); - - var indices = "indices"; - if( mesh.indexBuffers.triangles ) - indices = "triangles"; - shader.draw( mesh, primitive, indices ); - gl.disable( gl.BLEND ); - gl.depthMask( true ); - } - - LiteGraph.registerNodeType( "geometry/render_mesh", LGraphRenderMesh ); - - //************************** - - - function LGraphGeometryPrimitive() { - this.addInput("size", "number"); - this.addOutput("out", "mesh"); - this.properties = { type: 1, size: 1, subdivisions: 32 }; - - this.version = (Math.random() * 100000)|0; - this.last_info = { type: -1, size: -1, subdivisions: -1 }; - } - - LGraphGeometryPrimitive.title = "Primitive"; - - LGraphGeometryPrimitive.VALID = { "CUBE":1, "PLANE":2, "CYLINDER":3, "SPHERE":4, "CIRCLE":5, "HEMISPHERE":6, "ICOSAHEDRON":7, "CONE":8, "QUAD":9 }; - LGraphGeometryPrimitive.widgets_info = { - type: { widget: "combo", values: LGraphGeometryPrimitive.VALID } - }; - - LGraphGeometryPrimitive.prototype.onExecute = function() { - - if( !this.isOutputConnected(0) ) - return; - - var size = this.getInputOrProperty("size"); - - //update - if( this.last_info.type != this.properties.type || this.last_info.size != size || this.last_info.subdivisions != this.properties.subdivisions ) - this.updateMesh( this.properties.type, size, this.properties.subdivisions ); - - this.setOutputData(0,this._mesh); - } - - LGraphGeometryPrimitive.prototype.updateMesh = function(type, size, subdivisions) - { - subdivisions = Math.max(0,subdivisions)|0; - - switch (type) - { - case 1: //CUBE: - this._mesh = GL.Mesh.cube({size: size, normals:true,coords:true}); - break; - case 2: //PLANE: - this._mesh = GL.Mesh.plane({size: size, xz: true, detail: subdivisions, normals:true,coords:true}); - break; - case 3: //CYLINDER: - this._mesh = GL.Mesh.cylinder({size: size, subdivisions: subdivisions, normals:true,coords:true}); - break; - case 4: //SPHERE: - this._mesh = GL.Mesh.sphere({size: size, "long": subdivisions, lat: subdivisions, normals:true,coords:true}); - break; - case 5: //CIRCLE: - this._mesh = GL.Mesh.circle({size: size, slices: subdivisions, normals:true, coords:true}); - break; - case 6: //HEMISPHERE: - this._mesh = GL.Mesh.sphere({size: size, "long": subdivisions, lat: subdivisions, normals:true, coords:true, hemi: true}); - break; - case 7: //ICOSAHEDRON: - this._mesh = GL.Mesh.icosahedron({size: size, subdivisions:subdivisions }); - break; - case 8: //CONE: - this._mesh = GL.Mesh.cone({radius: size, height: size, subdivisions:subdivisions }); - break; - case 9: //QUAD: - this._mesh = GL.Mesh.plane({size: size, xz: false, detail: subdivisions, normals:true, coords:true }); - break; - } - - this.last_info.type = type; - this.last_info.size = size; - this.last_info.subdivisions = subdivisions; - this._mesh.version = this.version++; - } - - LiteGraph.registerNodeType( "geometry/mesh_primitive", LGraphGeometryPrimitive ); - - - function LGraphRenderPoints() { - this.addInput("in", "geometry"); - this.addInput("mat4", "mat4"); - this.addInput("tex", "texture"); - this.properties = { - enabled: true, - point_size: 0.1, - fixed_size: false, - additive: true, - color: [1,1,1], - opacity: 1 - }; - - this.color = vec4.create([1,1,1,1]); - - this.uniforms = { - u_point_size: 1, - u_perspective: 1, - u_point_perspective: 1, - u_color: this.color - }; - - this.geometry_id = -1; - this.version = -1; - this.mesh = null; - } - - LGraphRenderPoints.title = "renderPoints"; - LGraphRenderPoints.desc = "render points with a texture"; - - LGraphRenderPoints.widgets_info = { - color: { widget: "color" } - }; - - LGraphRenderPoints.prototype.updateMesh = function(geometry) - { - var buffer = this.buffer; - if(!this.buffer || !this.buffer.data || this.buffer.data.length != geometry.vertices.length) - this.buffer = new GL.Buffer( GL.ARRAY_BUFFER, geometry.vertices,3,GL.DYNAMIC_DRAW); - else - { - this.buffer.data.set( geometry.vertices ); - this.buffer.upload(GL.DYNAMIC_DRAW); - } - - if(!this.mesh) - this.mesh = new GL.Mesh(); - - this.mesh.addBuffer("vertices",this.buffer); - this.geometry_id = this.mesh.id = geometry._id; - this.version = this.mesh.version = geometry._version; - } - - LGraphRenderPoints.prototype.onExecute = function() { - - if(!this.properties.enabled) - return; - - var geometry = this.getInputData(0); - if(!geometry) - return; - if(this.version != geometry._version || this.geometry_id != geometry._id ) - this.updateMesh( geometry ); - - if(!LiteGraph.LGraphRender.onRequestCameraMatrices) - { - console.warn("cannot render geometry, LiteGraph.onRequestCameraMatrices is null, remember to fill this with a callback(view_matrix, projection_matrix,viewprojection_matrix) to use 3D rendering from the graph"); - return; - } - - LiteGraph.LGraphRender.onRequestCameraMatrices( view_matrix, projection_matrix,viewprojection_matrix ); - var shader = null; - - var texture = this.getInputData(2); - - if(texture) - { - shader = gl.shaders["textured_points"]; - if(!shader) - shader = gl.shaders["textured_points"] = new GL.Shader( LGraphRenderPoints.vertex_shader_code, LGraphRenderPoints.fragment_shader_code, { USE_TEXTURED_POINTS:"" }); - } - else - { - shader = gl.shaders["points"]; - if(!shader) - shader = gl.shaders["points"] = new GL.Shader( LGraphRenderPoints.vertex_shader_code, LGraphRenderPoints.fragment_shader_code, { USE_POINTS: "" }); - } - - this.color.set( this.properties.color ); - this.color[3] = this.properties.opacity; - - var m = this.getInputData(1); - if(m) - model_matrix.set(m); - else - mat4.identity( model_matrix ); - - this.uniforms.u_point_size = this.properties.point_size; - this.uniforms.u_point_perspective = this.properties.fixed_size ? 0 : 1; - this.uniforms.u_perspective = gl.viewport_data[3] * projection_matrix[5]; - - shader.uniforms( global_uniforms ); - shader.uniforms( this.uniforms ); - - if(this.properties.opacity >= 1) - gl.disable( gl.BLEND ); - else - gl.enable( gl.BLEND ); - - gl.enable( gl.DEPTH_TEST ); - if( this.properties.additive ) - { - gl.blendFunc( gl.SRC_ALPHA, gl.ONE ); - gl.depthMask( false ); - } - else - gl.blendFunc( gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA ); - - shader.draw( this.mesh, GL.POINTS ); - - gl.disable( gl.BLEND ); - gl.depthMask( true ); - } - - LiteGraph.registerNodeType( "geometry/render_points", LGraphRenderPoints ); - - LGraphRenderPoints.vertex_shader_code = '\ - precision mediump float;\n\ - attribute vec3 a_vertex;\n\ - varying vec3 v_vertex;\n\ - attribute vec3 a_normal;\n\ - varying vec3 v_normal;\n\ - #ifdef USE_COLOR\n\ - attribute vec4 a_color;\n\ - varying vec4 v_color;\n\ - #endif\n\ - attribute vec2 a_coord;\n\ - varying vec2 v_coord;\n\ - #ifdef USE_SIZE\n\ - attribute float a_extra;\n\ - #endif\n\ - #ifdef USE_INSTANCING\n\ - attribute mat4 u_model;\n\ - #else\n\ - uniform mat4 u_model;\n\ - #endif\n\ - uniform mat4 u_viewprojection;\n\ - uniform float u_point_size;\n\ - uniform float u_perspective;\n\ - uniform float u_point_perspective;\n\ - float computePointSize(float radius, float w)\n\ - {\n\ - if(radius < 0.0)\n\ - return -radius;\n\ - return u_perspective * radius / w;\n\ - }\n\ - void main() {\n\ - v_coord = a_coord;\n\ - #ifdef USE_COLOR\n\ - v_color = a_color;\n\ - #endif\n\ - v_vertex = ( u_model * vec4( a_vertex, 1.0 )).xyz;\n\ - v_normal = ( u_model * vec4( a_normal, 0.0 )).xyz;\n\ - gl_Position = u_viewprojection * vec4(v_vertex,1.0);\n\ - gl_PointSize = u_point_size;\n\ - #ifdef USE_SIZE\n\ - gl_PointSize = a_extra;\n\ - #endif\n\ - if(u_point_perspective != 0.0)\n\ - gl_PointSize = computePointSize( gl_PointSize, gl_Position.w );\n\ - }\ - '; - - LGraphRenderPoints.fragment_shader_code = '\ - precision mediump float;\n\ - uniform vec4 u_color;\n\ - #ifdef USE_COLOR\n\ - varying vec4 v_color;\n\ - #endif\n\ - varying vec2 v_coord;\n\ - uniform sampler2D u_texture;\n\ - void main() {\n\ - vec4 color = u_color;\n\ - #ifdef USE_TEXTURED_POINTS\n\ - color *= texture2D(u_texture, gl_PointCoord.xy);\n\ - #else\n\ - #ifdef USE_TEXTURE\n\ - color *= texture2D(u_texture, v_coord);\n\ - if(color.a < 0.1)\n\ - discard;\n\ - #endif\n\ - #ifdef USE_POINTS\n\ - float dist = length( gl_PointCoord.xy - vec2(0.5) );\n\ - if( dist > 0.45 )\n\ - discard;\n\ - #endif\n\ - #endif\n\ - #ifdef USE_COLOR\n\ - color *= v_color;\n\ - #endif\n\ - gl_FragColor = color;\n\ - }\ - '; - - //based on https://inconvergent.net/2019/depth-of-field/ - /* - function LGraphRenderGeometryDOF() { - this.addInput("in", "geometry"); - this.addInput("mat4", "mat4"); - this.addInput("tex", "texture"); - this.properties = { - enabled: true, - lines: true, - point_size: 0.1, - fixed_size: false, - additive: true, - color: [1,1,1], - opacity: 1 - }; - - this.color = vec4.create([1,1,1,1]); - - this.uniforms = { - u_point_size: 1, - u_perspective: 1, - u_point_perspective: 1, - u_color: this.color - }; - - this.geometry_id = -1; - this.version = -1; - this.mesh = null; - } - - LGraphRenderGeometryDOF.widgets_info = { - color: { widget: "color" } - }; - - LGraphRenderGeometryDOF.prototype.updateMesh = function(geometry) - { - var buffer = this.buffer; - if(!this.buffer || this.buffer.data.length != geometry.vertices.length) - this.buffer = new GL.Buffer( GL.ARRAY_BUFFER, geometry.vertices,3,GL.DYNAMIC_DRAW); - else - { - this.buffer.data.set( geometry.vertices ); - this.buffer.upload(GL.DYNAMIC_DRAW); - } - - if(!this.mesh) - this.mesh = new GL.Mesh(); - - this.mesh.addBuffer("vertices",this.buffer); - this.geometry_id = this.mesh.id = geometry._id; - this.version = this.mesh.version = geometry._version; - } - - LGraphRenderGeometryDOF.prototype.onExecute = function() { - - if(!this.properties.enabled) - return; - - var geometry = this.getInputData(0); - if(!geometry) - return; - if(this.version != geometry._version || this.geometry_id != geometry._id ) - this.updateMesh( geometry ); - - if(!LiteGraph.LGraphRender.onRequestCameraMatrices) - { - console.warn("cannot render geometry, LiteGraph.onRequestCameraMatrices is null, remember to fill this with a callback(view_matrix, projection_matrix,viewprojection_matrix) to use 3D rendering from the graph"); - return; - } - - LiteGraph.LGraphRender.onRequestCameraMatrices( view_matrix, projection_matrix,viewprojection_matrix ); - var shader = null; - - var texture = this.getInputData(2); - - if(texture) - { - shader = gl.shaders["textured_points"]; - if(!shader) - shader = gl.shaders["textured_points"] = new GL.Shader( LGraphRenderGeometryDOF.vertex_shader_code, LGraphRenderGeometryDOF.fragment_shader_code, { USE_TEXTURED_POINTS:"" }); - } - else - { - shader = gl.shaders["points"]; - if(!shader) - shader = gl.shaders["points"] = new GL.Shader( LGraphRenderGeometryDOF.vertex_shader_code, LGraphRenderGeometryDOF.fragment_shader_code, { USE_POINTS: "" }); - } - - this.color.set( this.properties.color ); - this.color[3] = this.properties.opacity; - - var m = this.getInputData(1); - if(m) - model_matrix.set(m); - else - mat4.identity( model_matrix ); - - this.uniforms.u_point_size = this.properties.point_size; - this.uniforms.u_point_perspective = this.properties.fixed_size ? 0 : 1; - this.uniforms.u_perspective = gl.viewport_data[3] * projection_matrix[5]; - - shader.uniforms( global_uniforms ); - shader.uniforms( this.uniforms ); - - if(this.properties.opacity >= 1) - gl.disable( gl.BLEND ); - else - gl.enable( gl.BLEND ); - - gl.enable( gl.DEPTH_TEST ); - if( this.properties.additive ) - { - gl.blendFunc( gl.SRC_ALPHA, gl.ONE ); - gl.depthMask( false ); - } - else - gl.blendFunc( gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA ); - - shader.draw( this.mesh, GL.POINTS ); - - gl.disable( gl.BLEND ); - gl.depthMask( true ); - } - - LiteGraph.registerNodeType( "geometry/render_dof", LGraphRenderGeometryDOF ); - - LGraphRenderGeometryDOF.vertex_shader_code = '\ - precision mediump float;\n\ - attribute vec3 a_vertex;\n\ - varying vec3 v_vertex;\n\ - attribute vec3 a_normal;\n\ - varying vec3 v_normal;\n\ - #ifdef USE_COLOR\n\ - attribute vec4 a_color;\n\ - varying vec4 v_color;\n\ - #endif\n\ - attribute vec2 a_coord;\n\ - varying vec2 v_coord;\n\ - #ifdef USE_SIZE\n\ - attribute float a_extra;\n\ - #endif\n\ - #ifdef USE_INSTANCING\n\ - attribute mat4 u_model;\n\ - #else\n\ - uniform mat4 u_model;\n\ - #endif\n\ - uniform mat4 u_viewprojection;\n\ - uniform float u_point_size;\n\ - uniform float u_perspective;\n\ - uniform float u_point_perspective;\n\ - float computePointSize(float radius, float w)\n\ - {\n\ - if(radius < 0.0)\n\ - return -radius;\n\ - return u_perspective * radius / w;\n\ - }\n\ - void main() {\n\ - v_coord = a_coord;\n\ - #ifdef USE_COLOR\n\ - v_color = a_color;\n\ - #endif\n\ - v_vertex = ( u_model * vec4( a_vertex, 1.0 )).xyz;\n\ - v_normal = ( u_model * vec4( a_normal, 0.0 )).xyz;\n\ - gl_Position = u_viewprojection * vec4(v_vertex,1.0);\n\ - gl_PointSize = u_point_size;\n\ - #ifdef USE_SIZE\n\ - gl_PointSize = a_extra;\n\ - #endif\n\ - if(u_point_perspective != 0.0)\n\ - gl_PointSize = computePointSize( gl_PointSize, gl_Position.w );\n\ - }\ - '; - - LGraphRenderGeometryDOF.fragment_shader_code = '\ - precision mediump float;\n\ - uniform vec4 u_color;\n\ - #ifdef USE_COLOR\n\ - varying vec4 v_color;\n\ - #endif\n\ - varying vec2 v_coord;\n\ - uniform sampler2D u_texture;\n\ - void main() {\n\ - vec4 color = u_color;\n\ - #ifdef USE_TEXTURED_POINTS\n\ - color *= texture2D(u_texture, gl_PointCoord.xy);\n\ - #else\n\ - #ifdef USE_TEXTURE\n\ - color *= texture2D(u_texture, v_coord);\n\ - if(color.a < 0.1)\n\ - discard;\n\ - #endif\n\ - #ifdef USE_POINTS\n\ - float dist = length( gl_PointCoord.xy - vec2(0.5) );\n\ - if( dist > 0.45 )\n\ - discard;\n\ - #endif\n\ - #endif\n\ - #ifdef USE_COLOR\n\ - color *= v_color;\n\ - #endif\n\ - gl_FragColor = color;\n\ - }\ - '; - */ - - - -})(this); \ No newline at end of file diff --git a/src/nodes/glfx.js b/src/nodes/glfx.js deleted file mode 100755 index 69b9ec3e5..000000000 --- a/src/nodes/glfx.js +++ /dev/null @@ -1,788 +0,0 @@ -(function(global) { - var LiteGraph = global.LiteGraph; - var LGraphTexture = global.LGraphTexture; - - //Works with Litegl.js to create WebGL nodes - if (typeof GL != "undefined") { - // Texture Lens ***************************************** - function LGraphFXLens() { - this.addInput("Texture", "Texture"); - this.addInput("Aberration", "number"); - this.addInput("Distortion", "number"); - this.addInput("Blur", "number"); - this.addOutput("Texture", "Texture"); - this.properties = { - aberration: 1.0, - distortion: 1.0, - blur: 1.0, - precision: LGraphTexture.DEFAULT - }; - - if (!LGraphFXLens._shader) { - LGraphFXLens._shader = new GL.Shader( - GL.Shader.SCREEN_VERTEX_SHADER, - LGraphFXLens.pixel_shader - ); - LGraphFXLens._texture = new GL.Texture(3, 1, { - format: gl.RGB, - wrap: gl.CLAMP_TO_EDGE, - magFilter: gl.LINEAR, - minFilter: gl.LINEAR, - pixel_data: [255, 0, 0, 0, 255, 0, 0, 0, 255] - }); - } - } - - LGraphFXLens.title = "Lens"; - LGraphFXLens.desc = "Camera Lens distortion"; - LGraphFXLens.widgets_info = { - precision: { widget: "combo", values: LGraphTexture.MODE_VALUES } - }; - - LGraphFXLens.prototype.onExecute = function() { - var tex = this.getInputData(0); - if (this.properties.precision === LGraphTexture.PASS_THROUGH) { - this.setOutputData(0, tex); - return; - } - - if (!tex) { - return; - } - - this._tex = LGraphTexture.getTargetTexture( - tex, - this._tex, - this.properties.precision - ); - - var aberration = this.properties.aberration; - if (this.isInputConnected(1)) { - aberration = this.getInputData(1); - this.properties.aberration = aberration; - } - - var distortion = this.properties.distortion; - if (this.isInputConnected(2)) { - distortion = this.getInputData(2); - this.properties.distortion = distortion; - } - - var blur = this.properties.blur; - if (this.isInputConnected(3)) { - blur = this.getInputData(3); - this.properties.blur = blur; - } - - gl.disable(gl.BLEND); - gl.disable(gl.DEPTH_TEST); - var mesh = Mesh.getScreenQuad(); - var shader = LGraphFXLens._shader; - //var camera = LS.Renderer._current_camera; - - this._tex.drawTo(function() { - tex.bind(0); - shader - .uniforms({ - u_texture: 0, - u_aberration: aberration, - u_distortion: distortion, - u_blur: blur - }) - .draw(mesh); - }); - - this.setOutputData(0, this._tex); - }; - - LGraphFXLens.pixel_shader = - "precision highp float;\n\ - precision highp float;\n\ - varying vec2 v_coord;\n\ - uniform sampler2D u_texture;\n\ - uniform vec2 u_camera_planes;\n\ - uniform float u_aberration;\n\ - uniform float u_distortion;\n\ - uniform float u_blur;\n\ - \n\ - void main() {\n\ - vec2 coord = v_coord;\n\ - float dist = distance(vec2(0.5), coord);\n\ - vec2 dist_coord = coord - vec2(0.5);\n\ - float percent = 1.0 + ((0.5 - dist) / 0.5) * u_distortion;\n\ - dist_coord *= percent;\n\ - coord = dist_coord + vec2(0.5);\n\ - vec4 color = texture2D(u_texture,coord, u_blur * dist);\n\ - color.r = texture2D(u_texture,vec2(0.5) + dist_coord * (1.0+0.01*u_aberration), u_blur * dist ).r;\n\ - color.b = texture2D(u_texture,vec2(0.5) + dist_coord * (1.0-0.01*u_aberration), u_blur * dist ).b;\n\ - gl_FragColor = color;\n\ - }\n\ - "; - /* - float normalized_tunable_sigmoid(float xs, float k)\n\ - {\n\ - xs = xs * 2.0 - 1.0;\n\ - float signx = sign(xs);\n\ - float absx = abs(xs);\n\ - return signx * ((-k - 1.0)*absx)/(2.0*(-2.0*k*absx+k-1.0)) + 0.5;\n\ - }\n\ - */ - - LiteGraph.registerNodeType("fx/lens", LGraphFXLens); - global.LGraphFXLens = LGraphFXLens; - - /* not working yet - function LGraphDepthOfField() - { - this.addInput("Color","Texture"); - this.addInput("Linear Depth","Texture"); - this.addInput("Camera","camera"); - this.addOutput("Texture","Texture"); - this.properties = { high_precision: false }; - } - - LGraphDepthOfField.title = "Depth Of Field"; - LGraphDepthOfField.desc = "Applies a depth of field effect"; - - LGraphDepthOfField.prototype.onExecute = function() - { - var tex = this.getInputData(0); - var depth = this.getInputData(1); - var camera = this.getInputData(2); - - if(!tex || !depth || !camera) - { - this.setOutputData(0, tex); - return; - } - - var precision = gl.UNSIGNED_BYTE; - if(this.properties.high_precision) - precision = gl.half_float_ext ? gl.HALF_FLOAT_OES : gl.FLOAT; - if(!this._temp_texture || this._temp_texture.type != precision || - this._temp_texture.width != tex.width || this._temp_texture.height != tex.height) - this._temp_texture = new GL.Texture( tex.width, tex.height, { type: precision, format: gl.RGBA, filter: gl.LINEAR }); - - var shader = LGraphDepthOfField._shader = new GL.Shader( GL.Shader.SCREEN_VERTEX_SHADER, LGraphDepthOfField._pixel_shader ); - - var screen_mesh = Mesh.getScreenQuad(); - - gl.disable( gl.DEPTH_TEST ); - gl.disable( gl.BLEND ); - - var camera_position = camera.getEye(); - var focus_point = camera.getCenter(); - var distance = vec3.distance( camera_position, focus_point ); - var far = camera.far; - var focus_range = distance * 0.5; - - this._temp_texture.drawTo( function() { - tex.bind(0); - depth.bind(1); - shader.uniforms({u_texture:0, u_depth_texture:1, u_resolution: [1/tex.width, 1/tex.height], u_far: far, u_focus_point: distance, u_focus_scale: focus_range }).draw(screen_mesh); - }); - - this.setOutputData(0, this._temp_texture); - } - - //from http://tuxedolabs.blogspot.com.es/2018/05/bokeh-depth-of-field-in-single-pass.html - LGraphDepthOfField._pixel_shader = "\n\ - precision highp float;\n\ - varying vec2 v_coord;\n\ - uniform sampler2D u_texture; //Image to be processed\n\ - uniform sampler2D u_depth_texture; //Linear depth, where 1.0 == far plane\n\ - uniform vec2 u_iresolution; //The size of a pixel: vec2(1.0/width, 1.0/height)\n\ - uniform float u_far; // Far plane\n\ - uniform float u_focus_point;\n\ - uniform float u_focus_scale;\n\ - \n\ - const float GOLDEN_ANGLE = 2.39996323;\n\ - const float MAX_BLUR_SIZE = 20.0;\n\ - const float RAD_SCALE = 0.5; // Smaller = nicer blur, larger = faster\n\ - \n\ - float getBlurSize(float depth, float focusPoint, float focusScale)\n\ - {\n\ - float coc = clamp((1.0 / focusPoint - 1.0 / depth)*focusScale, -1.0, 1.0);\n\ - return abs(coc) * MAX_BLUR_SIZE;\n\ - }\n\ - \n\ - vec3 depthOfField(vec2 texCoord, float focusPoint, float focusScale)\n\ - {\n\ - float centerDepth = texture2D(u_depth_texture, texCoord).r * u_far;\n\ - float centerSize = getBlurSize(centerDepth, focusPoint, focusScale);\n\ - vec3 color = texture2D(u_texture, v_coord).rgb;\n\ - float tot = 1.0;\n\ - \n\ - float radius = RAD_SCALE;\n\ - for (float ang = 0.0; ang < 100.0; ang += GOLDEN_ANGLE)\n\ - {\n\ - vec2 tc = texCoord + vec2(cos(ang), sin(ang)) * u_iresolution * radius;\n\ - \n\ - vec3 sampleColor = texture2D(u_texture, tc).rgb;\n\ - float sampleDepth = texture2D(u_depth_texture, tc).r * u_far;\n\ - float sampleSize = getBlurSize( sampleDepth, focusPoint, focusScale );\n\ - if (sampleDepth > centerDepth)\n\ - sampleSize = clamp(sampleSize, 0.0, centerSize*2.0);\n\ - \n\ - float m = smoothstep(radius-0.5, radius+0.5, sampleSize);\n\ - color += mix(color/tot, sampleColor, m);\n\ - tot += 1.0;\n\ - radius += RAD_SCALE/radius;\n\ - if(radius>=MAX_BLUR_SIZE)\n\ - return color / tot;\n\ - }\n\ - return color / tot;\n\ - }\n\ - void main()\n\ - {\n\ - gl_FragColor = vec4( depthOfField( v_coord, u_focus_point, u_focus_scale ), 1.0 );\n\ - //gl_FragColor = vec4( texture2D(u_depth_texture, v_coord).r );\n\ - }\n\ - "; - - LiteGraph.registerNodeType("fx/DOF", LGraphDepthOfField ); - global.LGraphDepthOfField = LGraphDepthOfField; - */ - - //******************************************************* - - function LGraphFXBokeh() { - this.addInput("Texture", "Texture"); - this.addInput("Blurred", "Texture"); - this.addInput("Mask", "Texture"); - this.addInput("Threshold", "number"); - this.addOutput("Texture", "Texture"); - this.properties = { - shape: "", - size: 10, - alpha: 1.0, - threshold: 1.0, - high_precision: false - }; - } - - LGraphFXBokeh.title = "Bokeh"; - LGraphFXBokeh.desc = "applies an Bokeh effect"; - - LGraphFXBokeh.widgets_info = { shape: { widget: "texture" } }; - - LGraphFXBokeh.prototype.onExecute = function() { - var tex = this.getInputData(0); - var blurred_tex = this.getInputData(1); - var mask_tex = this.getInputData(2); - if (!tex || !mask_tex || !this.properties.shape) { - this.setOutputData(0, tex); - return; - } - - if (!blurred_tex) { - blurred_tex = tex; - } - - var shape_tex = LGraphTexture.getTexture(this.properties.shape); - if (!shape_tex) { - return; - } - - var threshold = this.properties.threshold; - if (this.isInputConnected(3)) { - threshold = this.getInputData(3); - this.properties.threshold = threshold; - } - - var precision = gl.UNSIGNED_BYTE; - if (this.properties.high_precision) { - precision = gl.half_float_ext ? gl.HALF_FLOAT_OES : gl.FLOAT; - } - if ( - !this._temp_texture || - this._temp_texture.type != precision || - this._temp_texture.width != tex.width || - this._temp_texture.height != tex.height - ) { - this._temp_texture = new GL.Texture(tex.width, tex.height, { - type: precision, - format: gl.RGBA, - filter: gl.LINEAR - }); - } - - //iterations - var size = this.properties.size; - - var first_shader = LGraphFXBokeh._first_shader; - if (!first_shader) { - first_shader = LGraphFXBokeh._first_shader = new GL.Shader( - Shader.SCREEN_VERTEX_SHADER, - LGraphFXBokeh._first_pixel_shader - ); - } - - var second_shader = LGraphFXBokeh._second_shader; - if (!second_shader) { - second_shader = LGraphFXBokeh._second_shader = new GL.Shader( - LGraphFXBokeh._second_vertex_shader, - LGraphFXBokeh._second_pixel_shader - ); - } - - var points_mesh = this._points_mesh; - if ( - !points_mesh || - points_mesh._width != tex.width || - points_mesh._height != tex.height || - points_mesh._spacing != 2 - ) { - points_mesh = this.createPointsMesh(tex.width, tex.height, 2); - } - - var screen_mesh = Mesh.getScreenQuad(); - - var point_size = this.properties.size; - var min_light = this.properties.min_light; - var alpha = this.properties.alpha; - - gl.disable(gl.DEPTH_TEST); - gl.disable(gl.BLEND); - - this._temp_texture.drawTo(function() { - tex.bind(0); - blurred_tex.bind(1); - mask_tex.bind(2); - first_shader - .uniforms({ - u_texture: 0, - u_texture_blur: 1, - u_mask: 2, - u_texsize: [tex.width, tex.height] - }) - .draw(screen_mesh); - }); - - this._temp_texture.drawTo(function() { - //clear because we use blending - //gl.clearColor(0.0,0.0,0.0,1.0); - //gl.clear( gl.COLOR_BUFFER_BIT ); - gl.enable(gl.BLEND); - gl.blendFunc(gl.ONE, gl.ONE); - - tex.bind(0); - shape_tex.bind(3); - second_shader - .uniforms({ - u_texture: 0, - u_mask: 2, - u_shape: 3, - u_alpha: alpha, - u_threshold: threshold, - u_pointSize: point_size, - u_itexsize: [1.0 / tex.width, 1.0 / tex.height] - }) - .draw(points_mesh, gl.POINTS); - }); - - this.setOutputData(0, this._temp_texture); - }; - - LGraphFXBokeh.prototype.createPointsMesh = function( - width, - height, - spacing - ) { - var nwidth = Math.round(width / spacing); - var nheight = Math.round(height / spacing); - - var vertices = new Float32Array(nwidth * nheight * 2); - - var ny = -1; - var dx = (2 / width) * spacing; - var dy = (2 / height) * spacing; - for (var y = 0; y < nheight; ++y) { - var nx = -1; - for (var x = 0; x < nwidth; ++x) { - var pos = y * nwidth * 2 + x * 2; - vertices[pos] = nx; - vertices[pos + 1] = ny; - nx += dx; - } - ny += dy; - } - - this._points_mesh = GL.Mesh.load({ vertices2D: vertices }); - this._points_mesh._width = width; - this._points_mesh._height = height; - this._points_mesh._spacing = spacing; - - return this._points_mesh; - }; - - /* - LGraphTextureBokeh._pixel_shader = "precision highp float;\n\ - varying vec2 a_coord;\n\ - uniform sampler2D u_texture;\n\ - uniform sampler2D u_shape;\n\ - \n\ - void main() {\n\ - vec4 color = texture2D( u_texture, gl_PointCoord );\n\ - color *= v_color * u_alpha;\n\ - gl_FragColor = color;\n\ - }\n"; - */ - - LGraphFXBokeh._first_pixel_shader = - "precision highp float;\n\ - precision highp float;\n\ - varying vec2 v_coord;\n\ - uniform sampler2D u_texture;\n\ - uniform sampler2D u_texture_blur;\n\ - uniform sampler2D u_mask;\n\ - \n\ - void main() {\n\ - vec4 color = texture2D(u_texture, v_coord);\n\ - vec4 blurred_color = texture2D(u_texture_blur, v_coord);\n\ - float mask = texture2D(u_mask, v_coord).x;\n\ - gl_FragColor = mix(color, blurred_color, mask);\n\ - }\n\ - "; - - LGraphFXBokeh._second_vertex_shader = - "precision highp float;\n\ - attribute vec2 a_vertex2D;\n\ - varying vec4 v_color;\n\ - uniform sampler2D u_texture;\n\ - uniform sampler2D u_mask;\n\ - uniform vec2 u_itexsize;\n\ - uniform float u_pointSize;\n\ - uniform float u_threshold;\n\ - void main() {\n\ - vec2 coord = a_vertex2D * 0.5 + 0.5;\n\ - v_color = texture2D( u_texture, coord );\n\ - v_color += texture2D( u_texture, coord + vec2(u_itexsize.x, 0.0) );\n\ - v_color += texture2D( u_texture, coord + vec2(0.0, u_itexsize.y));\n\ - v_color += texture2D( u_texture, coord + u_itexsize);\n\ - v_color *= 0.25;\n\ - float mask = texture2D(u_mask, coord).x;\n\ - float luminance = length(v_color) * mask;\n\ - /*luminance /= (u_pointSize*u_pointSize)*0.01 */;\n\ - luminance -= u_threshold;\n\ - if(luminance < 0.0)\n\ - {\n\ - gl_Position.x = -100.0;\n\ - return;\n\ - }\n\ - gl_PointSize = u_pointSize;\n\ - gl_Position = vec4(a_vertex2D,0.0,1.0);\n\ - }\n\ - "; - - LGraphFXBokeh._second_pixel_shader = - "precision highp float;\n\ - varying vec4 v_color;\n\ - uniform sampler2D u_shape;\n\ - uniform float u_alpha;\n\ - \n\ - void main() {\n\ - vec4 color = texture2D( u_shape, gl_PointCoord );\n\ - color *= v_color * u_alpha;\n\ - gl_FragColor = color;\n\ - }\n"; - - LiteGraph.registerNodeType("fx/bokeh", LGraphFXBokeh); - global.LGraphFXBokeh = LGraphFXBokeh; - - //************************************************ - - function LGraphFXGeneric() { - this.addInput("Texture", "Texture"); - this.addInput("value1", "number"); - this.addInput("value2", "number"); - this.addOutput("Texture", "Texture"); - this.properties = { - fx: "halftone", - value1: 1, - value2: 1, - precision: LGraphTexture.DEFAULT - }; - } - - LGraphFXGeneric.title = "FX"; - LGraphFXGeneric.desc = "applies an FX from a list"; - - LGraphFXGeneric.widgets_info = { - fx: { - widget: "combo", - values: ["halftone", "pixelate", "lowpalette", "noise", "gamma"] - }, - precision: { widget: "combo", values: LGraphTexture.MODE_VALUES } - }; - LGraphFXGeneric.shaders = {}; - - LGraphFXGeneric.prototype.onExecute = function() { - if (!this.isOutputConnected(0)) { - return; - } //saves work - - var tex = this.getInputData(0); - if (this.properties.precision === LGraphTexture.PASS_THROUGH) { - this.setOutputData(0, tex); - return; - } - - if (!tex) { - return; - } - - this._tex = LGraphTexture.getTargetTexture( - tex, - this._tex, - this.properties.precision - ); - - //iterations - var value1 = this.properties.value1; - if (this.isInputConnected(1)) { - value1 = this.getInputData(1); - this.properties.value1 = value1; - } - - var value2 = this.properties.value2; - if (this.isInputConnected(2)) { - value2 = this.getInputData(2); - this.properties.value2 = value2; - } - - var fx = this.properties.fx; - var shader = LGraphFXGeneric.shaders[fx]; - if (!shader) { - var pixel_shader_code = LGraphFXGeneric["pixel_shader_" + fx]; - if (!pixel_shader_code) { - return; - } - - shader = LGraphFXGeneric.shaders[fx] = new GL.Shader( - Shader.SCREEN_VERTEX_SHADER, - pixel_shader_code - ); - } - - gl.disable(gl.BLEND); - gl.disable(gl.DEPTH_TEST); - var mesh = Mesh.getScreenQuad(); - var camera = global.LS ? LS.Renderer._current_camera : null; - var camera_planes; - if (camera) { - camera_planes = [ - LS.Renderer._current_camera.near, - LS.Renderer._current_camera.far - ]; - } else { - camera_planes = [1, 100]; - } - - var noise = null; - if (fx == "noise") { - noise = LGraphTexture.getNoiseTexture(); - } - - this._tex.drawTo(function() { - tex.bind(0); - if (fx == "noise") { - noise.bind(1); - } - - shader - .uniforms({ - u_texture: 0, - u_noise: 1, - u_size: [tex.width, tex.height], - u_rand: [Math.random(), Math.random()], - u_value1: value1, - u_value2: value2, - u_camera_planes: camera_planes - }) - .draw(mesh); - }); - - this.setOutputData(0, this._tex); - }; - - LGraphFXGeneric.pixel_shader_halftone = - "precision highp float;\n\ - varying vec2 v_coord;\n\ - uniform sampler2D u_texture;\n\ - uniform vec2 u_camera_planes;\n\ - uniform vec2 u_size;\n\ - uniform float u_value1;\n\ - uniform float u_value2;\n\ - \n\ - float pattern() {\n\ - float s = sin(u_value1 * 3.1415), c = cos(u_value1 * 3.1415);\n\ - vec2 tex = v_coord * u_size.xy;\n\ - vec2 point = vec2(\n\ - c * tex.x - s * tex.y ,\n\ - s * tex.x + c * tex.y \n\ - ) * u_value2;\n\ - return (sin(point.x) * sin(point.y)) * 4.0;\n\ - }\n\ - void main() {\n\ - vec4 color = texture2D(u_texture, v_coord);\n\ - float average = (color.r + color.g + color.b) / 3.0;\n\ - gl_FragColor = vec4(vec3(average * 10.0 - 5.0 + pattern()), color.a);\n\ - }\n"; - - LGraphFXGeneric.pixel_shader_pixelate = - "precision highp float;\n\ - varying vec2 v_coord;\n\ - uniform sampler2D u_texture;\n\ - uniform vec2 u_camera_planes;\n\ - uniform vec2 u_size;\n\ - uniform float u_value1;\n\ - uniform float u_value2;\n\ - \n\ - void main() {\n\ - vec2 coord = vec2( floor(v_coord.x * u_value1) / u_value1, floor(v_coord.y * u_value2) / u_value2 );\n\ - vec4 color = texture2D(u_texture, coord);\n\ - gl_FragColor = color;\n\ - }\n"; - - LGraphFXGeneric.pixel_shader_lowpalette = - "precision highp float;\n\ - varying vec2 v_coord;\n\ - uniform sampler2D u_texture;\n\ - uniform vec2 u_camera_planes;\n\ - uniform vec2 u_size;\n\ - uniform float u_value1;\n\ - uniform float u_value2;\n\ - \n\ - void main() {\n\ - vec4 color = texture2D(u_texture, v_coord);\n\ - gl_FragColor = floor(color * u_value1) / u_value1;\n\ - }\n"; - - LGraphFXGeneric.pixel_shader_noise = - "precision highp float;\n\ - varying vec2 v_coord;\n\ - uniform sampler2D u_texture;\n\ - uniform sampler2D u_noise;\n\ - uniform vec2 u_size;\n\ - uniform float u_value1;\n\ - uniform float u_value2;\n\ - uniform vec2 u_rand;\n\ - \n\ - void main() {\n\ - vec4 color = texture2D(u_texture, v_coord);\n\ - vec3 noise = texture2D(u_noise, v_coord * vec2(u_size.x / 512.0, u_size.y / 512.0) + u_rand).xyz - vec3(0.5);\n\ - gl_FragColor = vec4( color.xyz + noise * u_value1, color.a );\n\ - }\n"; - - LGraphFXGeneric.pixel_shader_gamma = - "precision highp float;\n\ - varying vec2 v_coord;\n\ - uniform sampler2D u_texture;\n\ - uniform float u_value1;\n\ - \n\ - void main() {\n\ - vec4 color = texture2D(u_texture, v_coord);\n\ - float gamma = 1.0 / u_value1;\n\ - gl_FragColor = vec4( pow( color.xyz, vec3(gamma) ), color.a );\n\ - }\n"; - - LiteGraph.registerNodeType("fx/generic", LGraphFXGeneric); - global.LGraphFXGeneric = LGraphFXGeneric; - - // Vigneting ************************************ - - function LGraphFXVigneting() { - this.addInput("Tex.", "Texture"); - this.addInput("intensity", "number"); - - this.addOutput("Texture", "Texture"); - this.properties = { - intensity: 1, - invert: false, - precision: LGraphTexture.DEFAULT - }; - - if (!LGraphFXVigneting._shader) { - LGraphFXVigneting._shader = new GL.Shader( - Shader.SCREEN_VERTEX_SHADER, - LGraphFXVigneting.pixel_shader - ); - } - } - - LGraphFXVigneting.title = "Vigneting"; - LGraphFXVigneting.desc = "Vigneting"; - - LGraphFXVigneting.widgets_info = { - precision: { widget: "combo", values: LGraphTexture.MODE_VALUES } - }; - - LGraphFXVigneting.prototype.onExecute = function() { - var tex = this.getInputData(0); - - if (this.properties.precision === LGraphTexture.PASS_THROUGH) { - this.setOutputData(0, tex); - return; - } - - if (!tex) { - return; - } - - this._tex = LGraphTexture.getTargetTexture( - tex, - this._tex, - this.properties.precision - ); - - var intensity = this.properties.intensity; - if (this.isInputConnected(1)) { - intensity = this.getInputData(1); - this.properties.intensity = intensity; - } - - gl.disable(gl.BLEND); - gl.disable(gl.DEPTH_TEST); - - var mesh = Mesh.getScreenQuad(); - var shader = LGraphFXVigneting._shader; - var invert = this.properties.invert; - - this._tex.drawTo(function() { - tex.bind(0); - shader - .uniforms({ - u_texture: 0, - u_intensity: intensity, - u_isize: [1 / tex.width, 1 / tex.height], - u_invert: invert ? 1 : 0 - }) - .draw(mesh); - }); - - this.setOutputData(0, this._tex); - }; - - LGraphFXVigneting.pixel_shader = - "precision highp float;\n\ - precision highp float;\n\ - varying vec2 v_coord;\n\ - uniform sampler2D u_texture;\n\ - uniform float u_intensity;\n\ - uniform int u_invert;\n\ - \n\ - void main() {\n\ - float luminance = 1.0 - length( v_coord - vec2(0.5) ) * 1.414;\n\ - vec4 color = texture2D(u_texture, v_coord);\n\ - if(u_invert == 1)\n\ - luminance = 1.0 - luminance;\n\ - luminance = mix(1.0, luminance, u_intensity);\n\ - gl_FragColor = vec4( luminance * color.xyz, color.a);\n\ - }\n\ - "; - - LiteGraph.registerNodeType("fx/vigneting", LGraphFXVigneting); - global.LGraphFXVigneting = LGraphFXVigneting; - } -})(this); diff --git a/src/nodes/glshaders.js b/src/nodes/glshaders.js deleted file mode 100755 index 11f129da6..000000000 --- a/src/nodes/glshaders.js +++ /dev/null @@ -1,1839 +0,0 @@ -(function(global) { - - if (typeof GL == "undefined") - return; - - var LiteGraph = global.LiteGraph; - var LGraphCanvas = global.LGraphCanvas; - - var SHADERNODES_COLOR = "#345"; - - var LGShaders = LiteGraph.Shaders = {}; - - var GLSL_types = LGShaders.GLSL_types = ["float","vec2","vec3","vec4","mat3","mat4","sampler2D","samplerCube"]; - var GLSL_types_const = LGShaders.GLSL_types_const = ["float","vec2","vec3","vec4"]; - - var GLSL_functions_desc = { - "radians": "T radians(T degrees)", - "degrees": "T degrees(T radians)", - "sin": "T sin(T angle)", - "cos": "T cos(T angle)", - "tan": "T tan(T angle)", - "asin": "T asin(T x)", - "acos": "T acos(T x)", - "atan": "T atan(T x)", - "atan2": "T atan(T x,T y)", - "pow": "T pow(T x,T y)", - "exp": "T exp(T x)", - "log": "T log(T x)", - "exp2": "T exp2(T x)", - "log2": "T log2(T x)", - "sqrt": "T sqrt(T x)", - "inversesqrt": "T inversesqrt(T x)", - "abs": "T abs(T x)", - "sign": "T sign(T x)", - "floor": "T floor(T x)", - "round": "T round(T x)", - "ceil": "T ceil(T x)", - "fract": "T fract(T x)", - "mod": "T mod(T x,T y)", //"T mod(T x,float y)" - "min": "T min(T x,T y)", - "max": "T max(T x,T y)", - "clamp": "T clamp(T x,T minVal = 0.0,T maxVal = 1.0)", - "mix": "T mix(T x,T y,T a)", //"T mix(T x,T y,float a)" - "step": "T step(T edge, T edge2, T x)", //"T step(float edge, T x)" - "smoothstep": "T smoothstep(T edge, T edge2, T x)", //"T smoothstep(float edge, T x)" - "length":"float length(T x)", - "distance":"float distance(T p0, T p1)", - "normalize":"T normalize(T x)", - "dot": "float dot(T x,T y)", - "cross": "vec3 cross(vec3 x,vec3 y)", - "reflect": "vec3 reflect(vec3 V,vec3 N)", - "refract": "vec3 refract(vec3 V,vec3 N, float IOR)" - }; - - //parse them - var GLSL_functions = {}; - var GLSL_functions_name = []; - parseGLSLDescriptions(); - - LGShaders.ALL_TYPES = "float,vec2,vec3,vec4"; - - function parseGLSLDescriptions() - { - GLSL_functions_name.length = 0; - - for(var i in GLSL_functions_desc) - { - var op = GLSL_functions_desc[i]; - var index = op.indexOf(" "); - var return_type = op.substr(0,index); - var index2 = op.indexOf("(",index); - var func_name = op.substr(index,index2-index).trim(); - var params = op.substr(index2 + 1, op.length - index2 - 2).split(","); - for(var j in params) - { - var p = params[j].split(" ").filter(function(a){ return a; }); - params[j] = { type: p[0].trim(), name: p[1].trim() }; - if(p[2] == "=") - params[j].value = p[3].trim(); - } - GLSL_functions[i] = { return_type: return_type, func: func_name, params: params }; - GLSL_functions_name.push( func_name ); - //console.log( GLSL_functions[i] ); - } - } - - //common actions to all shader node classes - function registerShaderNode( type, node_ctor ) - { - //static attributes - node_ctor.color = SHADERNODES_COLOR; - node_ctor.filter = "shader"; - - //common methods - node_ctor.prototype.clearDestination = function(){ this.shader_destination = {}; } - node_ctor.prototype.propagateDestination = function propagateDestination( dest_name ) - { - this.shader_destination[ dest_name ] = true; - if(this.inputs) - for(var i = 0; i < this.inputs.length; ++i) - { - var origin_node = this.getInputNode(i); - if(origin_node) - origin_node.propagateDestination( dest_name ); - } - } - if(!node_ctor.prototype.onPropertyChanged) - node_ctor.prototype.onPropertyChanged = function() - { - if(this.graph) - this.graph._version++; - } - - /* - if(!node_ctor.prototype.onGetCode) - node_ctor.prototype.onGetCode = function() - { - //check destination to avoid lonely nodes - if(!this.shader_destination) - return; - //grab inputs with types - var inputs = []; - if(this.inputs) - for(var i = 0; i < this.inputs.length; ++i) - inputs.push({ type: this.getInputData(i), name: getInputLinkID(this,i) }); - var outputs = []; - if(this.outputs) - for(var i = 0; i < this.outputs.length; ++i) - outputs.push({ name: getOutputLinkID(this,i) }); - //pass to code func - var results = this.extractCode(inputs); - //grab output, pass to next - if(results) - for(var i = 0; i < results.length; ++i) - { - var r = results[i]; - if(!r) - continue; - this.setOutputData(i,r.value); - } - } - */ - - LiteGraph.registerNodeType( "shader::" + type, node_ctor ); - } - - function getShaderNodeVarName( node, name ) - { - return "VAR_" + (name || "TEMP") + "_" + node.id; - } - - function getInputLinkID( node, slot ) - { - if(!node.inputs) - return null; - var link = node.getInputLink( slot ); - if( !link ) - return null; - var origin_node = node.graph.getNodeById( link.origin_id ); - if( !origin_node ) - return null; - if(origin_node.getOutputVarName) - return origin_node.getOutputVarName(link.origin_slot); - //generate - return "link_" + origin_node.id + "_" + link.origin_slot; - } - - function getOutputLinkID( node, slot ) - { - if (!node.isOutputConnected(slot)) - return null; - return "link_" + node.id + "_" + slot; - } - - LGShaders.registerShaderNode = registerShaderNode; - LGShaders.getInputLinkID = getInputLinkID; - LGShaders.getOutputLinkID = getOutputLinkID; - LGShaders.getShaderNodeVarName = getShaderNodeVarName; - LGShaders.parseGLSLDescriptions = parseGLSLDescriptions; - - //given a const number, it transform it to a string that matches a type - var valueToGLSL = LiteGraph.valueToGLSL = function valueToGLSL( v, type, precision ) - { - var n = 5; //num decimals - if(precision != null) - n = precision; - if(!type) - { - if(v.constructor === Number) - type = "float"; - else if(v.length) - { - switch(v.length) - { - case 2: type = "vec2"; break; - case 3: type = "vec3"; break; - case 4: type = "vec4"; break; - case 9: type = "mat3"; break; - case 16: type = "mat4"; break; - default: - throw("unknown type for glsl value size"); - } - } - else - throw("unknown type for glsl value: " + v.constructor); - } - switch(type) - { - case 'float': return v.toFixed(n); break; - case 'vec2': return "vec2(" + v[0].toFixed(n) + "," + v[1].toFixed(n) + ")"; break; - case 'color3': - case 'vec3': return "vec3(" + v[0].toFixed(n) + "," + v[1].toFixed(n) + "," + v[2].toFixed(n) + ")"; break; - case 'color4': - case 'vec4': return "vec4(" + v[0].toFixed(n) + "," + v[1].toFixed(n) + "," + v[2].toFixed(n) + "," + v[3].toFixed(n) + ")"; break; - case 'mat3': return "mat3(1.0,0.0,0.0,0.0,1.0,0.0,0.0,0.0,1.0)"; break; //not fully supported yet - case 'mat4': return "mat4(1.0,0.0,0.0,0.0,0.0,1.0,0.0,0.0,0.0,0.0,0.0,1.0,0.0,0.0,0.0,0.0,1.0)"; break;//not fully supported yet - default: - throw("unknown glsl type in valueToGLSL:", type); - } - - return ""; - } - - //makes sure that a var is of a type, and if not, it converts it - var varToTypeGLSL = LiteGraph.varToTypeGLSL = function varToTypeGLSL( v, input_type, output_type ) - { - if(input_type == output_type) - return v; - if(v == null) - switch(output_type) - { - case "float": return "0.0"; - case "vec2": return "vec2(0.0)"; - case "vec3": return "vec3(0.0)"; - case "vec4": return "vec4(0.0,0.0,0.0,1.0)"; - default: //null - return null; - } - - if(!output_type) - throw("error: no output type specified"); - if(output_type == "float") - { - switch(input_type) - { - //case "float": - case "vec2": - case "vec3": - case "vec4": - return v + ".x"; - break; - default: //null - return "0.0"; - break; - } - } - else if(output_type == "vec2") - { - switch(input_type) - { - case "float": - return "vec2("+v+")"; - //case "vec2": - case "vec3": - case "vec4": - return v + ".xy"; - default: //null - return "vec2(0.0)"; - } - } - else if(output_type == "vec3") - { - switch(input_type) - { - case "float": - return "vec3("+v+")"; - case "vec2": - return "vec3(" + v + ",0.0)"; - //case "vec3": - case "vec4": - return v + ".xyz"; - default: //null - return "vec3(0.0)"; - } - } - else if(output_type == "vec4") - { - switch(input_type) - { - case "float": - return "vec4("+v+")"; - case "vec2": - return "vec4(" + v + ",0.0,1.0)"; - case "vec3": - return "vec4(" + v + ",1.0)"; - default: //null - return "vec4(0.0,0.0,0.0,1.0)"; - } - } - throw("type cannot be converted"); - } - - - //used to plug incompatible stuff - var convertVarToGLSLType = LiteGraph.convertVarToGLSLType = function convertVarToGLSLType( varname, type, target_type ) - { - if(type == target_type) - return varname; - if(type == "float") - return target_type + "(" + varname + ")"; - if(target_type == "vec2") //works for vec2,vec3 and vec4 - return "vec2(" + varname + ".xy)"; - if(target_type == "vec3") //works for vec2,vec3 and vec4 - { - if(type == "vec2") - return "vec3(" + varname + ",0.0)"; - if(type == "vec4") - return "vec4(" + varname + ".xyz)"; - } - if(target_type == "vec4") - { - if(type == "vec2") - return "vec4(" + varname + ",0.0,0.0)"; - if(target_type == "vec3") - return "vec4(" + varname + ",1.0)"; - } - return null; - } - - //used to host a shader body ************************************** - function LGShaderContext() - { - //to store the code template - this.vs_template = ""; - this.fs_template = ""; - - //required so nodes now where to fetch the input data - this.buffer_names = { - uvs: "v_coord" - }; - - this.extra = {}; //to store custom info from the nodes (like if this shader supports a feature, etc) - - this._functions = {}; - this._uniforms = {}; - this._codeparts = {}; - this._uniform_value = null; - } - - LGShaderContext.prototype.clear = function() - { - this._uniforms = {}; - this._functions = {}; - this._codeparts = {}; - this._uniform_value = null; - - this.extra = {}; - } - - LGShaderContext.prototype.addUniform = function( name, type, value ) - { - this._uniforms[ name ] = type; - if(value != null) - { - if(!this._uniform_value) - this._uniform_value = {}; - this._uniform_value[name] = value; - } - } - - LGShaderContext.prototype.addFunction = function( name, code ) - { - this._functions[name] = code; - } - - LGShaderContext.prototype.addCode = function( hook, code, destinations ) - { - destinations = destinations || {"":""}; - for(var i in destinations) - { - var h = i ? i + "_" + hook : hook; - if(!this._codeparts[ h ]) - this._codeparts[ h ] = code + "\n"; - else - this._codeparts[ h ] += code + "\n"; - } - } - - //the system works by grabbing code fragments from every node and concatenating them in blocks depending on where must they be attached - LGShaderContext.prototype.computeCodeBlocks = function( graph, extra_uniforms ) - { - //prepare context - this.clear(); - - //grab output nodes - var vertexout = graph.findNodesByType("shader::output/vertex"); - vertexout = vertexout && vertexout.length ? vertexout[0] : null; - var fragmentout = graph.findNodesByType("shader::output/fragcolor"); - fragmentout = fragmentout && fragmentout.length ? fragmentout[0] : null; - if(!fragmentout) //?? - return null; - - //propagate back destinations - graph.sendEventToAllNodes( "clearDestination" ); - if(vertexout) - vertexout.propagateDestination("vs"); - if(fragmentout) - fragmentout.propagateDestination("fs"); - - //gets code from graph - graph.sendEventToAllNodes("onGetCode", this ); - - var uniforms = ""; - for(var i in this._uniforms) - uniforms += "uniform " + this._uniforms[i] + " " + i + ";\n"; - if(extra_uniforms) - for(var i in extra_uniforms) - uniforms += "uniform " + extra_uniforms[i] + " " + i + ";\n"; - - var functions = ""; - for(var i in this._functions) - functions += "//" + i + "\n" + this._functions[i] + "\n"; - - var blocks = this._codeparts; - blocks.uniforms = uniforms; - blocks.functions = functions; - return blocks; - } - - //replaces blocks using the vs and fs template and returns the final codes - LGShaderContext.prototype.computeShaderCode = function( graph ) - { - var blocks = this.computeCodeBlocks( graph ); - var vs_code = GL.Shader.replaceCodeUsingContext( this.vs_template, blocks ); - var fs_code = GL.Shader.replaceCodeUsingContext( this.fs_template, blocks ); - return { - vs_code: vs_code, - fs_code: fs_code - }; - } - - //generates the shader code from the template and the - LGShaderContext.prototype.computeShader = function( graph, shader ) - { - var finalcode = this.computeShaderCode( graph ); - console.log( finalcode.vs_code, finalcode.fs_code ); - - if(!LiteGraph.catch_exceptions) - { - this._shader_error = true; - if(shader) - shader.updateShader( finalcode.vs_code, finalcode.fs_code ); - else - shader = new GL.Shader( finalcode.vs_code, finalcode.fs_code ); - this._shader_error = false; - return shader; - } - - try - { - if(shader) - shader.updateShader( finalcode.vs_code, finalcode.fs_code ); - else - shader = new GL.Shader( finalcode.vs_code, finalcode.fs_code ); - this._shader_error = false; - return shader; - } - catch (err) - { - if(!this._shader_error) - { - console.error(err); - if(err.indexOf("Fragment shader") != -1) - console.log( finalcode.fs_code.split("\n").map(function(v,i){ return i + ".- " + v; }).join("\n") ); - else - console.log( finalcode.vs_code ); - } - this._shader_error = true; - return null; - } - - return null;//never here - } - - LGShaderContext.prototype.getShader = function( graph ) - { - //if graph not changed? - if(this._shader && this._shader._version == graph._version) - return this._shader; - - //compile shader - var shader = this.computeShader( graph, this._shader ); - if(!shader) - return null; - - this._shader = shader; - shader._version = graph._version; - return shader; - } - - //some shader nodes could require to fill the box with some uniforms - LGShaderContext.prototype.fillUniforms = function( uniforms, param ) - { - if(!this._uniform_value) - return; - - for(var i in this._uniform_value) - { - var v = this._uniform_value[i]; - if(v == null) - continue; - if(v.constructor === Function) - uniforms[i] = v.call( this, param ); - else if(v.constructor === GL.Texture) - { - //todo... - } - else - uniforms[i] = v; - } - } - - LiteGraph.ShaderContext = LiteGraph.Shaders.Context = LGShaderContext; - - // LGraphShaderGraph ***************************** - // applies a shader graph to texture, it can be uses as an example - - function LGraphShaderGraph() { - - //before inputs - this.subgraph = new LiteGraph.LGraph(); - this.subgraph._subgraph_node = this; - this.subgraph._is_subgraph = true; - this.subgraph.filter = "shader"; - - this.addInput("in", "texture"); - this.addOutput("out", "texture"); - this.properties = { width: 0, height: 0, alpha: false, precision: typeof(LGraphTexture) != "undefined" ? LGraphTexture.DEFAULT : 2 }; - - var inputNode = this.subgraph.findNodesByType("shader::input/uniform")[0]; - inputNode.pos = [200,300]; - - var sampler = LiteGraph.createNode("shader::texture/sampler2D"); - sampler.pos = [400,300]; - this.subgraph.add( sampler ); - - var outnode = LiteGraph.createNode("shader::output/fragcolor"); - outnode.pos = [600,300]; - this.subgraph.add( outnode ); - - inputNode.connect( 0, sampler ); - sampler.connect( 0, outnode ); - - this.size = [180,60]; - this.redraw_on_mouse = true; //force redraw - - this._uniforms = {}; - this._shader = null; - this._context = new LGShaderContext(); - this._context.vs_template = "#define VERTEX\n" + GL.Shader.SCREEN_VERTEX_SHADER; - this._context.fs_template = LGraphShaderGraph.template; - } - - LGraphShaderGraph.template = "\n\ -#define FRAGMENT\n\ -precision highp float;\n\ -varying vec2 v_coord;\n\ -{{varying}}\n\ -{{uniforms}}\n\ -{{functions}}\n\ -{{fs_functions}}\n\ -void main() {\n\n\ -vec2 uv = v_coord;\n\ -vec4 fragcolor = vec4(0.0);\n\ -vec4 fragcolor1 = vec4(0.0);\n\ -{{fs_code}}\n\ -gl_FragColor = fragcolor;\n\ -}\n\ - "; - - LGraphShaderGraph.widgets_info = { - precision: { widget: "combo", values: LGraphTexture.MODE_VALUES } - }; - - LGraphShaderGraph.title = "ShaderGraph"; - LGraphShaderGraph.desc = "Builds a shader using a graph"; - LGraphShaderGraph.input_node_type = "input/uniform"; - LGraphShaderGraph.output_node_type = "output/fragcolor"; - LGraphShaderGraph.title_color = SHADERNODES_COLOR; - - LGraphShaderGraph.prototype.onSerialize = function(o) - { - o.subgraph = this.subgraph.serialize(); - } - - LGraphShaderGraph.prototype.onConfigure = function(o) - { - this.subgraph.configure(o.subgraph); - } - - LGraphShaderGraph.prototype.onExecute = function() { - if (!this.isOutputConnected(0)) - return; - - //read input texture - var intex = this.getInputData(0); - if(intex && intex.constructor != GL.Texture) - intex = null; - - var w = this.properties.width | 0; - var h = this.properties.height | 0; - if (w == 0) { - w = intex ? intex.width : gl.viewport_data[2]; - } //0 means default - if (h == 0) { - h = intex ? intex.height : gl.viewport_data[3]; - } //0 means default - - var type = LGraphTexture.getTextureType( this.properties.precision, intex ); - - var texture = this._texture; - if ( !texture || texture.width != w || texture.height != h || texture.type != type ) { - texture = this._texture = new GL.Texture(w, h, { - type: type, - format: this.alpha ? gl.RGBA : gl.RGB, - filter: gl.LINEAR - }); - } - - var shader = this.getShader( this.subgraph ); - if(!shader) - return; - - var uniforms = this._uniforms; - this._context.fillUniforms( uniforms ); - - var tex_slot = 0; - if(this.inputs) - for(var i = 0; i < this.inputs.length; ++i) - { - var input = this.inputs[i]; - var data = this.getInputData(i); - if(input.type == "texture") - { - if(!data) - data = GL.Texture.getWhiteTexture(); - data = data.bind(tex_slot++); - } - - if(data != null) - uniforms[ "u_" + input.name ] = data; - } - - var mesh = GL.Mesh.getScreenQuad(); - - gl.disable( gl.DEPTH_TEST ); - gl.disable( gl.BLEND ); - - texture.drawTo(function(){ - shader.uniforms( uniforms ); - shader.draw( mesh ); - }); - - //use subgraph output - this.setOutputData(0, texture ); - }; - - //add input node inside subgraph - LGraphShaderGraph.prototype.onInputAdded = function( slot_info ) - { - var subnode = LiteGraph.createNode("shader::input/uniform"); - subnode.setProperty("name",slot_info.name); - subnode.setProperty("type",slot_info.type); - this.subgraph.add( subnode ); - } - - //remove all - LGraphShaderGraph.prototype.onInputRemoved = function( slot, slot_info ) - { - var nodes = this.subgraph.findNodesByType("shader::input/uniform"); - for(var i = 0; i < nodes.length; ++i) - { - var node = nodes[i]; - if(node.properties.name == slot_info.name ) - this.subgraph.remove( node ); - } - } - - LGraphShaderGraph.prototype.computeSize = function() - { - var num_inputs = this.inputs ? this.inputs.length : 0; - var num_outputs = this.outputs ? this.outputs.length : 0; - return [ 200, Math.max(num_inputs,num_outputs) * LiteGraph.NODE_SLOT_HEIGHT + LiteGraph.NODE_TITLE_HEIGHT + 10]; - } - - LGraphShaderGraph.prototype.getShader = function() - { - var shader = this._context.getShader( this.subgraph ); - if(!shader) - this.boxcolor = "red"; - else - this.boxcolor = null; - return shader; - } - - LGraphShaderGraph.prototype.onDrawBackground = function(ctx, graphcanvas, canvas, pos) - { - if(this.flags.collapsed) - return; - - //allows to preview the node if the canvas is a webgl canvas - var tex = this.getOutputData(0); - var inputs_y = this.inputs ? this.inputs.length * LiteGraph.NODE_SLOT_HEIGHT : 0; - if (tex && ctx == tex.gl && this.size[1] > inputs_y + LiteGraph.NODE_TITLE_HEIGHT ) { - ctx.drawImage( tex, 10,y, this.size[0] - 20, this.size[1] - inputs_y - LiteGraph.NODE_TITLE_HEIGHT ); - } - - var y = this.size[1] - LiteGraph.NODE_TITLE_HEIGHT + 0.5; - - //button - var over = LiteGraph.isInsideRectangle(pos[0],pos[1],this.pos[0],this.pos[1] + y,this.size[0],LiteGraph.NODE_TITLE_HEIGHT); - ctx.fillStyle = over ? "#555" : "#222"; - ctx.beginPath(); - if (this._shape == LiteGraph.BOX_SHAPE) - ctx.rect(0, y, this.size[0]+1, LiteGraph.NODE_TITLE_HEIGHT); - else - ctx.roundRect( 0, y, this.size[0]+1, LiteGraph.NODE_TITLE_HEIGHT, 0, 8); - ctx.fill(); - - //button - ctx.textAlign = "center"; - ctx.font = "24px Arial"; - ctx.fillStyle = over ? "#DDD" : "#999"; - ctx.fillText( "+", this.size[0] * 0.5, y + 24 ); - } - - LGraphShaderGraph.prototype.onMouseDown = function(e, localpos, graphcanvas) - { - var y = this.size[1] - LiteGraph.NODE_TITLE_HEIGHT + 0.5; - if(localpos[1] > y) - { - graphcanvas.showSubgraphPropertiesDialog(this); - } - } - - LGraphShaderGraph.prototype.onDrawSubgraphBackground = function(graphcanvas) - { - //TODO - } - - LGraphShaderGraph.prototype.getExtraMenuOptions = function(graphcanvas) - { - var that = this; - var options = [{ content: "Print Code", callback: function(){ - var code = that._context.computeShaderCode(); - console.log( code.vs_code, code.fs_code ); - }}]; - - return options; - } - - LiteGraph.registerNodeType( "texture/shaderGraph", LGraphShaderGraph ); - - function shaderNodeFromFunction( classname, params, return_type, code ) - { - //TODO - } - - //Shader Nodes *********************************************************** - - //applies a shader graph to a code - function LGraphShaderUniform() { - this.addOutput("out", ""); - this.properties = { name: "", type: "" }; - } - - LGraphShaderUniform.title = "Uniform"; - LGraphShaderUniform.desc = "Input data for the shader"; - - LGraphShaderUniform.prototype.getTitle = function() - { - if( this.properties.name && this.flags.collapsed) - return this.properties.type + " " + this.properties.name; - return "Uniform"; - } - - LGraphShaderUniform.prototype.onPropertyChanged = function(name,value) - { - this.outputs[0].name = this.properties.type + " " + this.properties.name; - } - - LGraphShaderUniform.prototype.onGetCode = function( context ) - { - if(!this.shader_destination) - return; - - var type = this.properties.type; - if( !type ) - { - if( !context.onGetPropertyInfo ) - return; - var info = context.onGetPropertyInfo( this.property.name ); - if(!info) - return; - type = info.type; - } - if(type == "number") - type = "float"; - else if(type == "texture") - type = "sampler2D"; - if ( LGShaders.GLSL_types.indexOf(type) == -1 ) - return; - - context.addUniform( "u_" + this.properties.name, type ); - this.setOutputData( 0, type ); - } - - LGraphShaderUniform.prototype.getOutputVarName = function(slot) - { - return "u_" + this.properties.name; - } - - registerShaderNode( "input/uniform", LGraphShaderUniform ); - - - function LGraphShaderAttribute() { - this.addOutput("out", "vec2"); - this.properties = { name: "coord", type: "vec2" }; - } - - LGraphShaderAttribute.title = "Attribute"; - LGraphShaderAttribute.desc = "Input data from mesh attribute"; - - LGraphShaderAttribute.prototype.getTitle = function() - { - return "att. " + this.properties.name; - } - - LGraphShaderAttribute.prototype.onGetCode = function( context ) - { - if(!this.shader_destination) - return; - - var type = this.properties.type; - if( !type || LGShaders.GLSL_types.indexOf(type) == -1 ) - return; - if(type == "number") - type = "float"; - if( this.properties.name != "coord") - { - context.addCode( "varying", " varying " + type +" v_" + this.properties.name + ";" ); - //if( !context.varyings[ this.properties.name ] ) - //context.addCode( "vs_code", "v_" + this.properties.name + " = " + input_name + ";" ); - } - this.setOutputData( 0, type ); - } - - LGraphShaderAttribute.prototype.getOutputVarName = function(slot) - { - return "v_" + this.properties.name; - } - - registerShaderNode( "input/attribute", LGraphShaderAttribute ); - - function LGraphShaderSampler2D() { - this.addInput("tex", "sampler2D"); - this.addInput("uv", "vec2"); - this.addOutput("rgba", "vec4"); - this.addOutput("rgb", "vec3"); - } - - LGraphShaderSampler2D.title = "Sampler2D"; - LGraphShaderSampler2D.desc = "Reads a pixel from a texture"; - - LGraphShaderSampler2D.prototype.onGetCode = function( context ) - { - if(!this.shader_destination) - return; - - var texname = getInputLinkID( this, 0 ); - var varname = getShaderNodeVarName(this); - var code = "vec4 " + varname + " = vec4(0.0);\n"; - if(texname) - { - var uvname = getInputLinkID( this, 1 ) || context.buffer_names.uvs; - code += varname + " = texture2D("+texname+","+uvname+");\n"; - } - - var link0 = getOutputLinkID( this, 0 ); - if(link0) - code += "vec4 " + getOutputLinkID( this, 0 ) + " = "+varname+";\n"; - - var link1 = getOutputLinkID( this, 1 ); - if(link1) - code += "vec3 " + getOutputLinkID( this, 1 ) + " = "+varname+".xyz;\n"; - - context.addCode( "code", code, this.shader_destination ); - this.setOutputData( 0, "vec4" ); - this.setOutputData( 1, "vec3" ); - } - - registerShaderNode( "texture/sampler2D", LGraphShaderSampler2D ); - - //********************************* - - function LGraphShaderConstant() - { - this.addOutput("","float"); - - this.properties = { - type: "float", - value: 0 - }; - - this.addWidget("combo","type","float",null, { values: GLSL_types_const, property: "type" } ); - this.updateWidgets(); - } - - LGraphShaderConstant.title = "const"; - - LGraphShaderConstant.prototype.getTitle = function() - { - if(this.flags.collapsed) - return valueToGLSL( this.properties.value, this.properties.type, 2 ); - return "Const"; - } - - LGraphShaderConstant.prototype.onPropertyChanged = function(name,value) - { - var that = this; - if(name == "type") - { - if(this.outputs[0].type != value) - { - this.disconnectOutput(0); - this.outputs[0].type = value; - } - this.widgets.length = 1; //remove extra widgets - this.updateWidgets(); - } - if(name == "value") - { - if(!value.length) - this.widgets[1].value = value; - else - { - this.widgets[1].value = value[1]; - if(value.length > 2) - this.widgets[2].value = value[2]; - if(value.length > 3) - this.widgets[3].value = value[3]; - } - } - } - - LGraphShaderConstant.prototype.updateWidgets = function( old_value ) - { - var that = this; - var old_value = this.properties.value; - var options = { step: 0.01 }; - switch(this.properties.type) - { - case 'float': - this.properties.value = 0; - this.addWidget("number","v",0,{ step:0.01, property: "value" }); - break; - case 'vec2': - this.properties.value = old_value && old_value.length == 2 ? [old_value[0],old_value[1]] : [0,0,0]; - this.addWidget("number","x",this.properties.value[0], function(v){ that.properties.value[0] = v; },options); - this.addWidget("number","y",this.properties.value[1], function(v){ that.properties.value[1] = v; },options); - break; - case 'vec3': - this.properties.value = old_value && old_value.length == 3 ? [old_value[0],old_value[1],old_value[2]] : [0,0,0]; - this.addWidget("number","x",this.properties.value[0], function(v){ that.properties.value[0] = v; },options); - this.addWidget("number","y",this.properties.value[1], function(v){ that.properties.value[1] = v; },options); - this.addWidget("number","z",this.properties.value[2], function(v){ that.properties.value[2] = v; },options); - break; - case 'vec4': - this.properties.value = old_value && old_value.length == 4 ? [old_value[0],old_value[1],old_value[2],old_value[3]] : [0,0,0,0]; - this.addWidget("number","x",this.properties.value[0], function(v){ that.properties.value[0] = v; },options); - this.addWidget("number","y",this.properties.value[1], function(v){ that.properties.value[1] = v; },options); - this.addWidget("number","z",this.properties.value[2], function(v){ that.properties.value[2] = v; },options); - this.addWidget("number","w",this.properties.value[3], function(v){ that.properties.value[3] = v; },options); - break; - default: - console.error("unknown type for constant"); - } - } - - LGraphShaderConstant.prototype.onGetCode = function( context ) - { - if(!this.shader_destination) - return; - - var value = valueToGLSL( this.properties.value, this.properties.type ); - var link_name = getOutputLinkID(this,0); - if(!link_name) //not connected - return; - - var code = " " + this.properties.type + " " + link_name + " = " + value + ";"; - context.addCode( "code", code, this.shader_destination ); - - this.setOutputData( 0, this.properties.type ); - } - - registerShaderNode( "const/const", LGraphShaderConstant ); - - function LGraphShaderVec2() - { - this.addInput("xy","vec2"); - this.addInput("x","float"); - this.addInput("y","float"); - this.addOutput("xy","vec2"); - this.addOutput("x","float"); - this.addOutput("y","float"); - - this.properties = { x: 0, y: 0 }; - } - - LGraphShaderVec2.title = "vec2"; - LGraphShaderVec2.varmodes = ["xy","x","y"]; - - LGraphShaderVec2.prototype.onPropertyChanged = function() - { - if(this.graph) - this.graph._version++; - } - - LGraphShaderVec2.prototype.onGetCode = function( context ) - { - if(!this.shader_destination) - return; - - var props = this.properties; - - var varname = getShaderNodeVarName(this); - var code = " vec2 " + varname + " = " + valueToGLSL([props.x,props.y]) + ";\n"; - - for(var i = 0;i < LGraphShaderVec2.varmodes.length; ++i) - { - var varmode = LGraphShaderVec2.varmodes[i]; - var inlink = getInputLinkID(this,i); - if(!inlink) - continue; - code += " " + varname + "."+varmode+" = " + inlink + ";\n"; - } - - for(var i = 0;i < LGraphShaderVec2.varmodes.length; ++i) - { - var varmode = LGraphShaderVec2.varmodes[i]; - var outlink = getOutputLinkID(this,i); - if(!outlink) - continue; - var type = GLSL_types_const[varmode.length - 1]; - code += " "+type+" " + outlink + " = " + varname + "." + varmode + ";\n"; - this.setOutputData( i, type ); - } - - context.addCode( "code", code, this.shader_destination ); - } - - registerShaderNode( "const/vec2", LGraphShaderVec2 ); - - function LGraphShaderVec3() - { - this.addInput("xyz","vec3"); - this.addInput("x","float"); - this.addInput("y","float"); - this.addInput("z","float"); - this.addInput("xy","vec2"); - this.addInput("xz","vec2"); - this.addInput("yz","vec2"); - this.addOutput("xyz","vec3"); - this.addOutput("x","float"); - this.addOutput("y","float"); - this.addOutput("z","float"); - this.addOutput("xy","vec2"); - this.addOutput("xz","vec2"); - this.addOutput("yz","vec2"); - - this.properties = { x:0, y: 0, z: 0 }; - } - - LGraphShaderVec3.title = "vec3"; - LGraphShaderVec3.varmodes = ["xyz","x","y","z","xy","xz","yz"]; - - LGraphShaderVec3.prototype.onPropertyChanged = function() - { - if(this.graph) - this.graph._version++; - } - - LGraphShaderVec3.prototype.onGetCode = function( context ) - { - if(!this.shader_destination) - return; - - var props = this.properties; - - var varname = getShaderNodeVarName(this); - var code = "vec3 " + varname + " = " + valueToGLSL([props.x,props.y,props.z]) + ";\n"; - - for(var i = 0;i < LGraphShaderVec3.varmodes.length; ++i) - { - var varmode = LGraphShaderVec3.varmodes[i]; - var inlink = getInputLinkID(this,i); - if(!inlink) - continue; - code += " " + varname + "."+varmode+" = " + inlink + ";\n"; - } - - for(var i = 0; i < LGraphShaderVec3.varmodes.length; ++i) - { - var varmode = LGraphShaderVec3.varmodes[i]; - var outlink = getOutputLinkID(this,i); - if(!outlink) - continue; - var type = GLSL_types_const[varmode.length - 1]; - code += " "+type+" " + outlink + " = " + varname + "." + varmode + ";\n"; - this.setOutputData( i, type ); - } - - context.addCode( "code", code, this.shader_destination ); - } - - registerShaderNode( "const/vec3", LGraphShaderVec3 ); - - - function LGraphShaderVec4() - { - this.addInput("xyzw","vec4"); - this.addInput("xyz","vec3"); - this.addInput("x","float"); - this.addInput("y","float"); - this.addInput("z","float"); - this.addInput("w","float"); - this.addInput("xy","vec2"); - this.addInput("yz","vec2"); - this.addInput("zw","vec2"); - this.addOutput("xyzw","vec4"); - this.addOutput("xyz","vec3"); - this.addOutput("x","float"); - this.addOutput("y","float"); - this.addOutput("z","float"); - this.addOutput("xy","vec2"); - this.addOutput("yz","vec2"); - this.addOutput("zw","vec2"); - - this.properties = { x:0, y: 0, z: 0, w: 0 }; - } - - LGraphShaderVec4.title = "vec4"; - LGraphShaderVec4.varmodes = ["xyzw","xyz","x","y","z","w","xy","yz","zw"]; - - LGraphShaderVec4.prototype.onPropertyChanged = function() - { - if(this.graph) - this.graph._version++; - } - - LGraphShaderVec4.prototype.onGetCode = function( context ) - { - if(!this.shader_destination) - return; - - var props = this.properties; - - var varname = getShaderNodeVarName(this); - var code = "vec4 " + varname + " = " + valueToGLSL([props.x,props.y,props.z,props.w]) + ";\n"; - - for(var i = 0;i < LGraphShaderVec4.varmodes.length; ++i) - { - var varmode = LGraphShaderVec4.varmodes[i]; - var inlink = getInputLinkID(this,i); - if(!inlink) - continue; - code += " " + varname + "."+varmode+" = " + inlink + ";\n"; - } - - for(var i = 0;i < LGraphShaderVec4.varmodes.length; ++i) - { - var varmode = LGraphShaderVec4.varmodes[i]; - var outlink = getOutputLinkID(this,i); - if(!outlink) - continue; - var type = GLSL_types_const[varmode.length - 1]; - code += " "+type+" " + outlink + " = " + varname + "." + varmode + ";\n"; - this.setOutputData( i, type ); - } - - context.addCode( "code", code, this.shader_destination ); - - } - - registerShaderNode( "const/vec4", LGraphShaderVec4 ); - - //********************************* - - function LGraphShaderFragColor() { - this.addInput("color", LGShaders.ALL_TYPES ); - this.block_delete = true; - } - - LGraphShaderFragColor.title = "FragColor"; - LGraphShaderFragColor.desc = "Pixel final color"; - - LGraphShaderFragColor.prototype.onGetCode = function( context ) - { - var link_name = getInputLinkID( this, 0 ); - if(!link_name) - return; - var type = this.getInputData(0); - var code = varToTypeGLSL( link_name, type, "vec4" ); - context.addCode("fs_code", "fragcolor = " + code + ";"); - } - - registerShaderNode( "output/fragcolor", LGraphShaderFragColor ); - - - /* - function LGraphShaderDiscard() - { - this.addInput("v","T"); - this.addInput("min","T"); - this.properties = { min_value: 0.0 }; - this.addWidget("number","min",0,{ step: 0.01, property: "min_value" }); - } - - LGraphShaderDiscard.title = "Discard"; - - LGraphShaderDiscard.prototype.onGetCode = function( context ) - { - if(!this.isOutputConnected(0)) - return; - - var inlink = getInputLinkID(this,0); - var inlink1 = getInputLinkID(this,1); - - if(!inlink && !inlink1) //not connected - return; - context.addCode("code", return_type + " " + outlink + " = ( (" + inlink + " - "+minv+") / ("+ maxv+" - "+minv+") ) * ("+ maxv2+" - "+minv2+") + " + minv2 + ";", this.shader_destination ); - this.setOutputData( 0, return_type ); - } - - registerShaderNode( "output/discard", LGraphShaderDiscard ); - */ - - - // ************************************************* - - function LGraphShaderOperation() - { - this.addInput("A", LGShaders.ALL_TYPES ); - this.addInput("B", LGShaders.ALL_TYPES ); - this.addOutput("out",""); - this.properties = { - operation: "*" - }; - this.addWidget("combo","op.",this.properties.operation,{ property: "operation", values: LGraphShaderOperation.operations }); - } - - LGraphShaderOperation.title = "Operation"; - LGraphShaderOperation.operations = ["+","-","*","/"]; - - LGraphShaderOperation.prototype.getTitle = function() - { - if(this.flags.collapsed) - return "A" + this.properties.operation + "B"; - else - return "Operation"; - } - - LGraphShaderOperation.prototype.onGetCode = function( context ) - { - if(!this.shader_destination) - return; - - if(!this.isOutputConnected(0)) - return; - - var inlinks = []; - for(var i = 0; i < 3; ++i) - inlinks.push( { name: getInputLinkID(this,i), type: this.getInputData(i) || "float" } ); - - var outlink = getOutputLinkID(this,0); - if(!outlink) //not connected - return; - - //func_desc - var base_type = inlinks[0].type; - var return_type = base_type; - var op = this.properties.operation; - - var params = []; - for(var i = 0; i < 2; ++i) - { - var param_code = inlinks[i].name; - if(param_code == null) //not plugged - { - param_code = p.value != null ? p.value : "(1.0)"; - inlinks[i].type = "float"; - } - - //convert - if( inlinks[i].type != base_type ) - { - if( inlinks[i].type == "float" && (op == "*" || op == "/") ) - { - //I find hard to create the opposite condition now, so I prefeer an else - } - else - param_code = convertVarToGLSLType( param_code, inlinks[i].type, base_type ); - } - params.push( param_code ); - } - - context.addCode("code", return_type + " " + outlink + " = "+ params[0] + op + params[1] + ";", this.shader_destination ); - this.setOutputData( 0, return_type ); - } - - registerShaderNode( "math/operation", LGraphShaderOperation ); - - - function LGraphShaderFunc() - { - this.addInput("A", LGShaders.ALL_TYPES ); - this.addInput("B", LGShaders.ALL_TYPES ); - this.addOutput("out",""); - this.properties = { - func: "floor" - }; - this._current = "floor"; - this.addWidget("combo","func",this.properties.func,{ property: "func", values: GLSL_functions_name }); - } - - LGraphShaderFunc.title = "Func"; - - LGraphShaderFunc.prototype.onPropertyChanged = function(name,value) - { - if(this.graph) - this.graph._version++; - - if(name == "func") - { - var func_desc = GLSL_functions[ value ]; - if(!func_desc) - return; - - //remove extra inputs - for(var i = func_desc.params.length; i < this.inputs.length; ++i) - this.removeInput(i); - - //add and update inputs - for(var i = 0; i < func_desc.params.length; ++i) - { - var p = func_desc.params[i]; - if( this.inputs[i] ) - this.inputs[i].name = p.name + (p.value ? " (" + p.value + ")" : ""); - else - this.addInput( p.name, LGShaders.ALL_TYPES ); - } - } - } - - LGraphShaderFunc.prototype.getTitle = function() - { - if(this.flags.collapsed) - return this.properties.func; - else - return "Func"; - } - - LGraphShaderFunc.prototype.onGetCode = function( context ) - { - if(!this.shader_destination) - return; - - if(!this.isOutputConnected(0)) - return; - - var inlinks = []; - for(var i = 0; i < 3; ++i) - inlinks.push( { name: getInputLinkID(this,i), type: this.getInputData(i) || "float" } ); - - var outlink = getOutputLinkID(this,0); - if(!outlink) //not connected - return; - - var func_desc = GLSL_functions[ this.properties.func ]; - if(!func_desc) - return; - - //func_desc - var base_type = inlinks[0].type; - var return_type = func_desc.return_type; - if( return_type == "T" ) - return_type = base_type; - - var params = []; - for(var i = 0; i < func_desc.params.length; ++i) - { - var p = func_desc.params[i]; - var param_code = inlinks[i].name; - if(param_code == null) //not plugged - { - param_code = p.value != null ? p.value : "(1.0)"; - inlinks[i].type = "float"; - } - if( (p.type == "T" && inlinks[i].type != base_type) || - (p.type != "T" && inlinks[i].type != base_type) ) - param_code = convertVarToGLSLType( param_code, inlinks[i].type, base_type ); - params.push( param_code ); - } - - context.addFunction("round","float round(float v){ return floor(v+0.5); }\nvec2 round(vec2 v){ return floor(v+vec2(0.5));}\nvec3 round(vec3 v){ return floor(v+vec3(0.5));}\nvec4 round(vec4 v){ return floor(v+vec4(0.5)); }\n"); - context.addCode("code", return_type + " " + outlink + " = "+func_desc.func+"("+params.join(",")+");", this.shader_destination ); - - this.setOutputData( 0, return_type ); - } - - registerShaderNode( "math/func", LGraphShaderFunc ); - - - - function LGraphShaderSnippet() - { - this.addInput("A", LGShaders.ALL_TYPES ); - this.addInput("B", LGShaders.ALL_TYPES ); - this.addOutput("C","vec4"); - this.properties = { - code:"C = A+B", - type: "vec4" - } - this.addWidget("text","code",this.properties.code,{ property: "code" }); - this.addWidget("combo","type",this.properties.type,{ values:["float","vec2","vec3","vec4"], property: "type" }); - } - - LGraphShaderSnippet.title = "Snippet"; - - LGraphShaderSnippet.prototype.onPropertyChanged = function(name,value) - { - if(this.graph) - this.graph._version++; - - if(name == "type"&& this.outputs[0].type != value) - { - this.disconnectOutput(0); - this.outputs[0].type = value; - } - } - - LGraphShaderSnippet.prototype.getTitle = function() - { - if(this.flags.collapsed) - return this.properties.code; - else - return "Snippet"; - } - - LGraphShaderSnippet.prototype.onGetCode = function( context ) - { - if(!this.shader_destination || !this.isOutputConnected(0)) - return; - - var inlinkA = getInputLinkID(this,0); - if(!inlinkA) - inlinkA = "1.0"; - var inlinkB = getInputLinkID(this,1); - if(!inlinkB) - inlinkB = "1.0"; - var outlink = getOutputLinkID(this,0); - if(!outlink) //not connected - return; - - var inA_type = this.getInputData(0) || "float"; - var inB_type = this.getInputData(1) || "float"; - var return_type = this.properties.type; - - //cannot resolve input - if(inA_type == "T" || inB_type == "T") - { - return null; - } - - var funcname = "funcSnippet" + this.id; - - var func_code = "\n" + return_type + " " + funcname + "( " + inA_type + " A, " + inB_type + " B) {\n"; - func_code += " " + return_type + " C = " + return_type + "(0.0);\n"; - func_code += " " + this.properties.code + ";\n"; - func_code += " return C;\n}\n"; - - context.addCode("functions", func_code, this.shader_destination ); - context.addCode("code", return_type + " " + outlink + " = "+funcname+"("+inlinkA+","+inlinkB+");", this.shader_destination ); - - this.setOutputData( 0, return_type ); - } - - registerShaderNode( "utils/snippet", LGraphShaderSnippet ); - - //************************************ - - function LGraphShaderRand() - { - this.addOutput("out","float"); - } - - LGraphShaderRand.title = "Rand"; - - LGraphShaderRand.prototype.onGetCode = function( context ) - { - if(!this.shader_destination || !this.isOutputConnected(0)) - return; - - var outlink = getOutputLinkID(this,0); - - context.addUniform( "u_rand" + this.id, "float", function(){ return Math.random(); }); - context.addCode("code", "float " + outlink + " = u_rand" + this.id +";", this.shader_destination ); - this.setOutputData( 0, "float" ); - } - - registerShaderNode( "input/rand", LGraphShaderRand ); - - //noise - //https://gist.github.com/patriciogonzalezvivo/670c22f3966e662d2f83 - function LGraphShaderNoise() - { - this.addInput("out", LGShaders.ALL_TYPES ); - this.addInput("scale", "float" ); - this.addOutput("out","float"); - this.properties = { - type: "noise", - scale: 1 - }; - this.addWidget("combo","type", this.properties.type, { property: "type", values: LGraphShaderNoise.NOISE_TYPES }); - this.addWidget("number","scale", this.properties.scale, { property: "scale" }); - } - - LGraphShaderNoise.NOISE_TYPES = ["noise","rand"]; - - LGraphShaderNoise.title = "noise"; - - LGraphShaderNoise.prototype.onGetCode = function( context ) - { - if(!this.shader_destination || !this.isOutputConnected(0)) - return; - - var inlink = getInputLinkID(this,0); - var outlink = getOutputLinkID(this,0); - - var intype = this.getInputData(0); - if(!inlink) - { - intype = "vec2"; - inlink = context.buffer_names.uvs; - } - - context.addFunction("noise",LGraphShaderNoise.shader_functions); - context.addUniform( "u_noise_scale" + this.id, "float", this.properties.scale ); - if( intype == "float" ) - context.addCode("code", "float " + outlink + " = snoise( vec2(" + inlink +") * u_noise_scale" + this.id +");", this.shader_destination ); - else if( intype == "vec2" || intype == "vec3" ) - context.addCode("code", "float " + outlink + " = snoise(" + inlink +" * u_noise_scale" + this.id +");", this.shader_destination ); - else if( intype == "vec4" ) - context.addCode("code", "float " + outlink + " = snoise(" + inlink +".xyz * u_noise_scale" + this.id +");", this.shader_destination ); - this.setOutputData( 0, "float" ); - } - - registerShaderNode( "math/noise", LGraphShaderNoise ); - -LGraphShaderNoise.shader_functions = "\n\ -vec3 permute(vec3 x) { return mod(((x*34.0)+1.0)*x, 289.0); }\n\ -\n\ -float snoise(vec2 v){\n\ - const vec4 C = vec4(0.211324865405187, 0.366025403784439,-0.577350269189626, 0.024390243902439);\n\ - vec2 i = floor(v + dot(v, C.yy) );\n\ - vec2 x0 = v - i + dot(i, C.xx);\n\ - vec2 i1;\n\ - i1 = (x0.x > x0.y) ? vec2(1.0, 0.0) : vec2(0.0, 1.0);\n\ - vec4 x12 = x0.xyxy + C.xxzz;\n\ - x12.xy -= i1;\n\ - i = mod(i, 289.0);\n\ - vec3 p = permute( permute( i.y + vec3(0.0, i1.y, 1.0 ))\n\ - + i.x + vec3(0.0, i1.x, 1.0 ));\n\ - vec3 m = max(0.5 - vec3(dot(x0,x0), dot(x12.xy,x12.xy),dot(x12.zw,x12.zw)), 0.0);\n\ - m = m*m ;\n\ - m = m*m ;\n\ - vec3 x = 2.0 * fract(p * C.www) - 1.0;\n\ - vec3 h = abs(x) - 0.5;\n\ - vec3 ox = floor(x + 0.5);\n\ - vec3 a0 = x - ox;\n\ - m *= 1.79284291400159 - 0.85373472095314 * ( a0*a0 + h*h );\n\ - vec3 g;\n\ - g.x = a0.x * x0.x + h.x * x0.y;\n\ - g.yz = a0.yz * x12.xz + h.yz * x12.yw;\n\ - return 130.0 * dot(m, g);\n\ -}\n\ -vec4 permute(vec4 x){return mod(((x*34.0)+1.0)*x, 289.0);}\n\ -vec4 taylorInvSqrt(vec4 r){return 1.79284291400159 - 0.85373472095314 * r;}\n\ -\n\ -float snoise(vec3 v){ \n\ - const vec2 C = vec2(1.0/6.0, 1.0/3.0) ;\n\ - const vec4 D = vec4(0.0, 0.5, 1.0, 2.0);\n\ -\n\ -// First corner\n\ - vec3 i = floor(v + dot(v, C.yyy) );\n\ - vec3 x0 = v - i + dot(i, C.xxx) ;\n\ -\n\ -// Other corners\n\ - vec3 g = step(x0.yzx, x0.xyz);\n\ - vec3 l = 1.0 - g;\n\ - vec3 i1 = min( g.xyz, l.zxy );\n\ - vec3 i2 = max( g.xyz, l.zxy );\n\ -\n\ - // x0 = x0 - 0. + 0.0 * C \n\ - vec3 x1 = x0 - i1 + 1.0 * C.xxx;\n\ - vec3 x2 = x0 - i2 + 2.0 * C.xxx;\n\ - vec3 x3 = x0 - 1. + 3.0 * C.xxx;\n\ -\n\ -// Permutations\n\ - i = mod(i, 289.0 ); \n\ - vec4 p = permute( permute( permute( \n\ - i.z + vec4(0.0, i1.z, i2.z, 1.0 ))\n\ - + i.y + vec4(0.0, i1.y, i2.y, 1.0 )) \n\ - + i.x + vec4(0.0, i1.x, i2.x, 1.0 ));\n\ -\n\ -// Gradients\n\ -// ( N*N points uniformly over a square, mapped onto an octahedron.)\n\ - float n_ = 1.0/7.0; // N=7\n\ - vec3 ns = n_ * D.wyz - D.xzx;\n\ -\n\ - vec4 j = p - 49.0 * floor(p * ns.z *ns.z); // mod(p,N*N)\n\ -\n\ - vec4 x_ = floor(j * ns.z);\n\ - vec4 y_ = floor(j - 7.0 * x_ ); // mod(j,N)\n\ -\n\ - vec4 x = x_ *ns.x + ns.yyyy;\n\ - vec4 y = y_ *ns.x + ns.yyyy;\n\ - vec4 h = 1.0 - abs(x) - abs(y);\n\ -\n\ - vec4 b0 = vec4( x.xy, y.xy );\n\ - vec4 b1 = vec4( x.zw, y.zw );\n\ -\n\ - vec4 s0 = floor(b0)*2.0 + 1.0;\n\ - vec4 s1 = floor(b1)*2.0 + 1.0;\n\ - vec4 sh = -step(h, vec4(0.0));\n\ -\n\ - vec4 a0 = b0.xzyw + s0.xzyw*sh.xxyy ;\n\ - vec4 a1 = b1.xzyw + s1.xzyw*sh.zzww ;\n\ -\n\ - vec3 p0 = vec3(a0.xy,h.x);\n\ - vec3 p1 = vec3(a0.zw,h.y);\n\ - vec3 p2 = vec3(a1.xy,h.z);\n\ - vec3 p3 = vec3(a1.zw,h.w);\n\ -\n\ -//Normalise gradients\n\ - vec4 norm = taylorInvSqrt(vec4(dot(p0,p0), dot(p1,p1), dot(p2, p2), dot(p3,p3)));\n\ - p0 *= norm.x;\n\ - p1 *= norm.y;\n\ - p2 *= norm.z;\n\ - p3 *= norm.w;\n\ -\n\ -// Mix final noise value\n\ - vec4 m = max(0.6 - vec4(dot(x0,x0), dot(x1,x1), dot(x2,x2), dot(x3,x3)), 0.0);\n\ - m = m * m;\n\ - return 42.0 * dot( m*m, vec4( dot(p0,x0), dot(p1,x1),dot(p2,x2), dot(p3,x3) ) );\n\ -}\n\ -\n\ -vec3 hash3( vec2 p ){\n\ - vec3 q = vec3( dot(p,vec2(127.1,311.7)), \n\ - dot(p,vec2(269.5,183.3)), \n\ - dot(p,vec2(419.2,371.9)) );\n\ - return fract(sin(q)*43758.5453);\n\ -}\n\ -vec4 hash4( vec3 p ){\n\ - vec4 q = vec4( dot(p,vec3(127.1,311.7,257.3)), \n\ - dot(p,vec3(269.5,183.3,335.1)), \n\ - dot(p,vec3(314.5,235.1,467.3)), \n\ - dot(p,vec3(419.2,371.9,114.9)) );\n\ - return fract(sin(q)*43758.5453);\n\ -}\n\ -\n\ -float iqnoise( in vec2 x, float u, float v ){\n\ - vec2 p = floor(x);\n\ - vec2 f = fract(x);\n\ - \n\ - float k = 1.0+63.0*pow(1.0-v,4.0);\n\ - \n\ - float va = 0.0;\n\ - float wt = 0.0;\n\ - for( int j=-2; j<=2; j++ )\n\ - for( int i=-2; i<=2; i++ )\n\ - {\n\ - vec2 g = vec2( float(i),float(j) );\n\ - vec3 o = hash3( p + g )*vec3(u,u,1.0);\n\ - vec2 r = g - f + o.xy;\n\ - float d = dot(r,r);\n\ - float ww = pow( 1.0-smoothstep(0.0,1.414,sqrt(d)), k );\n\ - va += o.z*ww;\n\ - wt += ww;\n\ - }\n\ - \n\ - return va/wt;\n\ -}\n\ -" - - function LGraphShaderTime() - { - this.addOutput("out","float"); - } - - LGraphShaderTime.title = "Time"; - - LGraphShaderTime.prototype.onGetCode = function( context ) - { - if(!this.shader_destination || !this.isOutputConnected(0)) - return; - - var outlink = getOutputLinkID(this,0); - - context.addUniform( "u_time" + this.id, "float", function(){ return getTime() * 0.001; }); - context.addCode("code", "float " + outlink + " = u_time" + this.id +";", this.shader_destination ); - this.setOutputData( 0, "float" ); - } - - registerShaderNode( "input/time", LGraphShaderTime ); - - - function LGraphShaderDither() - { - this.addInput("in","T"); - this.addOutput("out","float"); - } - - LGraphShaderDither.title = "Dither"; - - LGraphShaderDither.prototype.onGetCode = function( context ) - { - if(!this.shader_destination || !this.isOutputConnected(0)) - return; - - var inlink = getInputLinkID(this,0); - var return_type = "float"; - var outlink = getOutputLinkID(this,0); - var intype = this.getInputData(0); - inlink = varToTypeGLSL( inlink, intype, "float" ); - context.addFunction("dither8x8", LGraphShaderDither.dither_func); - context.addCode("code", return_type + " " + outlink + " = dither8x8("+ inlink +");", this.shader_destination ); - this.setOutputData( 0, return_type ); - } - - LGraphShaderDither.dither_values = [0.515625,0.140625,0.640625,0.046875,0.546875,0.171875,0.671875,0.765625,0.265625,0.890625,0.390625,0.796875,0.296875,0.921875,0.421875,0.203125,0.703125,0.078125,0.578125,0.234375,0.734375,0.109375,0.609375,0.953125,0.453125,0.828125,0.328125,0.984375,0.484375,0.859375,0.359375,0.0625,0.5625,0.1875,0.6875,0.03125,0.53125,0.15625,0.65625,0.8125,0.3125,0.9375,0.4375,0.78125,0.28125,0.90625,0.40625,0.25,0.75,0.125,0.625,0.21875,0.71875,0.09375,0.59375,1.0001,0.5,0.875,0.375,0.96875,0.46875,0.84375,0.34375]; - - LGraphShaderDither.dither_func = "\n\ - float dither8x8(float brightness) {\n\ - vec2 position = vec2(0.0);\n\ - #ifdef FRAGMENT\n\ - position = gl_FragCoord.xy;\n\ - #endif\n\ - int x = int(mod(position.x, 8.0));\n\ - int y = int(mod(position.y, 8.0));\n\ - int index = x + y * 8;\n\ - float limit = 0.0;\n\ - if (x < 8) {\n\ - if(index==0) limit = 0.015625;\n\ - "+(LGraphShaderDither.dither_values.map( function(v,i){ return "else if(index== "+(i+1)+") limit = " + v + ";"}).join("\n"))+"\n\ - }\n\ - return brightness < limit ? 0.0 : 1.0;\n\ - }\n", - - registerShaderNode( "math/dither", LGraphShaderDither ); - - function LGraphShaderRemap() - { - this.addInput("", LGShaders.ALL_TYPES ); - this.addOutput("",""); - this.properties = { - min_value: 0, - max_value: 1, - min_value2: 0, - max_value2: 1 - }; - this.addWidget("number","min",0,{ step: 0.1, property: "min_value" }); - this.addWidget("number","max",1,{ step: 0.1, property: "max_value" }); - this.addWidget("number","min2",0,{ step: 0.1, property: "min_value2"}); - this.addWidget("number","max2",1,{ step: 0.1, property: "max_value2"}); - } - - LGraphShaderRemap.title = "Remap"; - - LGraphShaderRemap.prototype.onPropertyChanged = function() - { - if(this.graph) - this.graph._version++; - } - - LGraphShaderRemap.prototype.onConnectionsChange = function() - { - var return_type = this.getInputDataType(0); - this.outputs[0].type = return_type || "T"; - } - - LGraphShaderRemap.prototype.onGetCode = function( context ) - { - if(!this.shader_destination || !this.isOutputConnected(0)) - return; - - var inlink = getInputLinkID(this,0); - var outlink = getOutputLinkID(this,0); - if(!inlink && !outlink) //not connected - return; - - var return_type = this.getInputDataType(0); - this.outputs[0].type = return_type; - if(return_type == "T") - { - console.warn("node type is T and cannot be resolved"); - return; - } - - if(!inlink) - { - context.addCode("code"," " + return_type + " " + outlink + " = " + return_type + "(0.0);\n"); - return; - } - - var minv = valueToGLSL( this.properties.min_value ); - var maxv = valueToGLSL( this.properties.max_value ); - var minv2 = valueToGLSL( this.properties.min_value2 ); - var maxv2 = valueToGLSL( this.properties.max_value2 ); - - context.addCode("code", return_type + " " + outlink + " = ( (" + inlink + " - "+minv+") / ("+ maxv+" - "+minv+") ) * ("+ maxv2+" - "+minv2+") + " + minv2 + ";", this.shader_destination ); - this.setOutputData( 0, return_type ); - } - - registerShaderNode( "math/remap", LGraphShaderRemap ); - -})(this); - - diff --git a/src/nodes/gltextures.js b/src/nodes/gltextures.js deleted file mode 100755 index 12c0bd5a7..000000000 --- a/src/nodes/gltextures.js +++ /dev/null @@ -1,5488 +0,0 @@ -(function(global) { - var LiteGraph = global.LiteGraph; - var LGraphCanvas = global.LGraphCanvas; - - //Works with Litegl.js to create WebGL nodes - global.LGraphTexture = null; - - if (typeof GL == "undefined") - return; - - LGraphCanvas.link_type_colors["Texture"] = "#987"; - - function LGraphTexture() { - this.addOutput("tex", "Texture"); - this.addOutput("name", "string"); - this.properties = { name: "", filter: true }; - this.size = [ - LGraphTexture.image_preview_size, - LGraphTexture.image_preview_size - ]; - } - - global.LGraphTexture = LGraphTexture; - - LGraphTexture.title = "Texture"; - LGraphTexture.desc = "Texture"; - LGraphTexture.widgets_info = { - name: { widget: "texture" }, - filter: { widget: "checkbox" } - }; - - //REPLACE THIS TO INTEGRATE WITH YOUR FRAMEWORK - LGraphTexture.loadTextureCallback = null; //function in charge of loading textures when not present in the container - LGraphTexture.image_preview_size = 256; - - //flags to choose output texture type - LGraphTexture.UNDEFINED = 0; //not specified - LGraphTexture.PASS_THROUGH = 1; //do not apply FX (like disable but passing the in to the out) - LGraphTexture.COPY = 2; //create new texture with the same properties as the origin texture - LGraphTexture.LOW = 3; //create new texture with low precision (byte) - LGraphTexture.HIGH = 4; //create new texture with high precision (half-float) - LGraphTexture.REUSE = 5; //reuse input texture - LGraphTexture.DEFAULT = 2; //use the default - - LGraphTexture.MODE_VALUES = { - "undefined": LGraphTexture.UNDEFINED, - "pass through": LGraphTexture.PASS_THROUGH, - copy: LGraphTexture.COPY, - low: LGraphTexture.LOW, - high: LGraphTexture.HIGH, - reuse: LGraphTexture.REUSE, - default: LGraphTexture.DEFAULT - }; - - //returns the container where all the loaded textures are stored (overwrite if you have a Resources Manager) - LGraphTexture.getTexturesContainer = function() { - return gl.textures; - }; - - //process the loading of a texture (overwrite it if you have a Resources Manager) - LGraphTexture.loadTexture = function(name, options) { - options = options || {}; - var url = name; - if (url.substr(0, 7) == "http://") { - if (LiteGraph.proxy) { - //proxy external files - url = LiteGraph.proxy + url.substr(7); - } - } - - var container = LGraphTexture.getTexturesContainer(); - var tex = (container[name] = GL.Texture.fromURL(url, options)); - return tex; - }; - - LGraphTexture.getTexture = function(name) { - var container = this.getTexturesContainer(); - - if (!container) { - throw "Cannot load texture, container of textures not found"; - } - - var tex = container[name]; - if (!tex && name && name[0] != ":") { - return this.loadTexture(name); - } - - return tex; - }; - - //used to compute the appropiate output texture - LGraphTexture.getTargetTexture = function(origin, target, mode) { - if (!origin) { - throw "LGraphTexture.getTargetTexture expects a reference texture"; - } - - var tex_type = null; - - switch (mode) { - case LGraphTexture.LOW: - tex_type = gl.UNSIGNED_BYTE; - break; - case LGraphTexture.HIGH: - tex_type = gl.HIGH_PRECISION_FORMAT; - break; - case LGraphTexture.REUSE: - return origin; - break; - case LGraphTexture.COPY: - default: - tex_type = origin ? origin.type : gl.UNSIGNED_BYTE; - break; - } - - if ( - !target || - target.width != origin.width || - target.height != origin.height || - target.type != tex_type || - target.format != origin.format - ) { - target = new GL.Texture(origin.width, origin.height, { - type: tex_type, - format: origin.format, - filter: gl.LINEAR - }); - } - - return target; - }; - - LGraphTexture.getTextureType = function(precision, ref_texture) { - var type = ref_texture ? ref_texture.type : gl.UNSIGNED_BYTE; - switch (precision) { - case LGraphTexture.HIGH: - type = gl.HIGH_PRECISION_FORMAT; - break; - case LGraphTexture.LOW: - type = gl.UNSIGNED_BYTE; - break; - //no default - } - return type; - }; - - LGraphTexture.getWhiteTexture = function() { - if (this._white_texture) { - return this._white_texture; - } - var texture = (this._white_texture = GL.Texture.fromMemory( - 1, - 1, - [255, 255, 255, 255], - { format: gl.RGBA, wrap: gl.REPEAT, filter: gl.NEAREST } - )); - return texture; - }; - - LGraphTexture.getNoiseTexture = function() { - if (this._noise_texture) { - return this._noise_texture; - } - - var noise = new Uint8Array(512 * 512 * 4); - for (var i = 0; i < 512 * 512 * 4; ++i) { - noise[i] = Math.random() * 255; - } - - var texture = GL.Texture.fromMemory(512, 512, noise, { - format: gl.RGBA, - wrap: gl.REPEAT, - filter: gl.NEAREST - }); - this._noise_texture = texture; - return texture; - }; - - LGraphTexture.prototype.onDropFile = function(data, filename, file) { - if (!data) { - this._drop_texture = null; - this.properties.name = ""; - } else { - var texture = null; - if (typeof data == "string") { - texture = GL.Texture.fromURL(data); - } else if (filename.toLowerCase().indexOf(".dds") != -1) { - texture = GL.Texture.fromDDSInMemory(data); - } else { - var blob = new Blob([file]); - var url = URL.createObjectURL(blob); - texture = GL.Texture.fromURL(url); - } - - this._drop_texture = texture; - this.properties.name = filename; - } - }; - - LGraphTexture.prototype.getExtraMenuOptions = function(graphcanvas) { - var that = this; - if (!this._drop_texture) { - return; - } - return [ - { - content: "Clear", - callback: function() { - that._drop_texture = null; - that.properties.name = ""; - } - } - ]; - }; - - LGraphTexture.prototype.onExecute = function() { - var tex = null; - if (this.isOutputConnected(1)) { - tex = this.getInputData(0); - } - - if (!tex && this._drop_texture) { - tex = this._drop_texture; - } - - if (!tex && this.properties.name) { - tex = LGraphTexture.getTexture(this.properties.name); - } - - if (!tex) { - this.setOutputData( 0, null ); - this.setOutputData( 1, "" ); - return; - } - - this._last_tex = tex; - - if (this.properties.filter === false) { - tex.setParameter(gl.TEXTURE_MAG_FILTER, gl.NEAREST); - } else { - tex.setParameter(gl.TEXTURE_MAG_FILTER, gl.LINEAR); - } - - this.setOutputData( 0, tex ); - this.setOutputData( 1, tex.fullpath || tex.filename ); - - for (var i = 2; i < this.outputs.length; i++) { - var output = this.outputs[i]; - if (!output) { - continue; - } - var v = null; - if (output.name == "width") { - v = tex.width; - } else if (output.name == "height") { - v = tex.height; - } else if (output.name == "aspect") { - v = tex.width / tex.height; - } - this.setOutputData(i, v); - } - }; - - LGraphTexture.prototype.onResourceRenamed = function( - old_name, - new_name - ) { - if (this.properties.name == old_name) { - this.properties.name = new_name; - } - }; - - LGraphTexture.prototype.onDrawBackground = function(ctx) { - if (this.flags.collapsed || this.size[1] <= 20) { - return; - } - - if (this._drop_texture && ctx.webgl) { - ctx.drawImage( - this._drop_texture, - 0, - 0, - this.size[0], - this.size[1] - ); - //this._drop_texture.renderQuad(this.pos[0],this.pos[1],this.size[0],this.size[1]); - return; - } - - //Different texture? then get it from the GPU - if (this._last_preview_tex != this._last_tex) { - if (ctx.webgl) { - this._canvas = this._last_tex; - } else { - var tex_canvas = LGraphTexture.generateLowResTexturePreview( - this._last_tex - ); - if (!tex_canvas) { - return; - } - - this._last_preview_tex = this._last_tex; - this._canvas = cloneCanvas(tex_canvas); - } - } - - if (!this._canvas) { - return; - } - - //render to graph canvas - ctx.save(); - if (!ctx.webgl) { - //reverse image - ctx.translate(0, this.size[1]); - ctx.scale(1, -1); - } - ctx.drawImage(this._canvas, 0, 0, this.size[0], this.size[1]); - ctx.restore(); - }; - - //very slow, used at your own risk - LGraphTexture.generateLowResTexturePreview = function(tex) { - if (!tex) { - return null; - } - - var size = LGraphTexture.image_preview_size; - var temp_tex = tex; - - if (tex.format == gl.DEPTH_COMPONENT) { - return null; - } //cannot generate from depth - - //Generate low-level version in the GPU to speed up - if (tex.width > size || tex.height > size) { - temp_tex = this._preview_temp_tex; - if (!this._preview_temp_tex) { - temp_tex = new GL.Texture(size, size, { - minFilter: gl.NEAREST - }); - this._preview_temp_tex = temp_tex; - } - - //copy - tex.copyTo(temp_tex); - tex = temp_tex; - } - - //create intermediate canvas with lowquality version - var tex_canvas = this._preview_canvas; - if (!tex_canvas) { - tex_canvas = createCanvas(size, size); - this._preview_canvas = tex_canvas; - } - - if (temp_tex) { - temp_tex.toCanvas(tex_canvas); - } - return tex_canvas; - }; - - LGraphTexture.prototype.getResources = function(res) { - if(this.properties.name) - res[this.properties.name] = GL.Texture; - return res; - }; - - LGraphTexture.prototype.onGetInputs = function() { - return [["in", "Texture"]]; - }; - - LGraphTexture.prototype.onGetOutputs = function() { - return [ - ["width", "number"], - ["height", "number"], - ["aspect", "number"] - ]; - }; - - //used to replace shader code - LGraphTexture.replaceCode = function( code, context ) - { - return code.replace(/\{\{[a-zA-Z0-9_]*\}\}/g, function(v){ - v = v.replace( /[\{\}]/g, "" ); - return context[v] || ""; - }); - } - - LiteGraph.registerNodeType("texture/texture", LGraphTexture); - - //************************** - function LGraphTexturePreview() { - this.addInput("Texture", "Texture"); - this.properties = { flipY: false }; - this.size = [ - LGraphTexture.image_preview_size, - LGraphTexture.image_preview_size - ]; - } - - LGraphTexturePreview.title = "Preview"; - LGraphTexturePreview.desc = "Show a texture in the graph canvas"; - LGraphTexturePreview.allow_preview = false; - - LGraphTexturePreview.prototype.onDrawBackground = function(ctx) { - if (this.flags.collapsed) { - return; - } - - if (!ctx.webgl && !LGraphTexturePreview.allow_preview) { - return; - } //not working well - - var tex = this.getInputData(0); - if (!tex) { - return; - } - - var tex_canvas = null; - - if (!tex.handle && ctx.webgl) { - tex_canvas = tex; - } else { - tex_canvas = LGraphTexture.generateLowResTexturePreview(tex); - } - - //render to graph canvas - ctx.save(); - if (this.properties.flipY) { - ctx.translate(0, this.size[1]); - ctx.scale(1, -1); - } - ctx.drawImage(tex_canvas, 0, 0, this.size[0], this.size[1]); - ctx.restore(); - }; - - LiteGraph.registerNodeType("texture/preview", LGraphTexturePreview); - - //************************************** - - function LGraphTextureSave() { - this.addInput("Texture", "Texture"); - this.addOutput("tex", "Texture"); - this.addOutput("name", "string"); - this.properties = { name: "", generate_mipmaps: false }; - } - - LGraphTextureSave.title = "Save"; - LGraphTextureSave.desc = "Save a texture in the repository"; - - LGraphTextureSave.prototype.getPreviewTexture = function() - { - return this._texture; - } - - LGraphTextureSave.prototype.onExecute = function() { - var tex = this.getInputData(0); - if (!tex) { - return; - } - - if (this.properties.generate_mipmaps) { - tex.bind(0); - tex.setParameter( gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR ); - gl.generateMipmap(tex.texture_type); - tex.unbind(0); - } - - if (this.properties.name) { - //for cases where we want to perform something when storing it - if (LGraphTexture.storeTexture) { - LGraphTexture.storeTexture(this.properties.name, tex); - } else { - var container = LGraphTexture.getTexturesContainer(); - container[this.properties.name] = tex; - } - } - - this._texture = tex; - this.setOutputData(0, tex); - this.setOutputData(1, this.properties.name); - }; - - LiteGraph.registerNodeType("texture/save", LGraphTextureSave); - - //**************************************************** - - function LGraphTextureOperation() { - this.addInput("Texture", "Texture"); - this.addInput("TextureB", "Texture"); - this.addInput("value", "number"); - this.addOutput("Texture", "Texture"); - this.help = "

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

    \ -

    uv: tex. coords

    color: texture colorB: textureB

    time: scene time value: input value

    For multiline you must type: result = ...

    "; - - this.properties = { - value: 1, - pixelcode: "color + colorB * value", - uvcode: "", - precision: LGraphTexture.DEFAULT - }; - - this.has_error = false; - } - - LGraphTextureOperation.widgets_info = { - uvcode: { widget: "code" }, - pixelcode: { widget: "code" }, - precision: { widget: "combo", values: LGraphTexture.MODE_VALUES } - }; - - LGraphTextureOperation.title = "Operation"; - LGraphTextureOperation.desc = "Texture shader operation"; - - LGraphTextureOperation.presets = {}; - - LGraphTextureOperation.prototype.getExtraMenuOptions = function( - graphcanvas - ) { - var that = this; - var txt = !that.properties.show ? "Show Texture" : "Hide Texture"; - return [ - { - content: txt, - callback: function() { - that.properties.show = !that.properties.show; - } - } - ]; - }; - - LGraphTextureOperation.prototype.onPropertyChanged = function() - { - this.has_error = false; - } - - LGraphTextureOperation.prototype.onDrawBackground = function(ctx) { - if ( - this.flags.collapsed || - this.size[1] <= 20 || - !this.properties.show - ) { - return; - } - - if (!this._tex) { - return; - } - - //only works if using a webgl renderer - if (this._tex.gl != ctx) { - return; - } - - //render to graph canvas - ctx.save(); - ctx.drawImage(this._tex, 0, 0, this.size[0], this.size[1]); - ctx.restore(); - }; - - LGraphTextureOperation.prototype.onExecute = function() { - var tex = this.getInputData(0); - - if (!this.isOutputConnected(0)) { - return; - } //saves work - - if (this.properties.precision === LGraphTexture.PASS_THROUGH) { - this.setOutputData(0, tex); - return; - } - - var texB = this.getInputData(1); - - if (!this.properties.uvcode && !this.properties.pixelcode) { - return; - } - - var width = 512; - var height = 512; - if (tex) { - width = tex.width; - height = tex.height; - } else if (texB) { - width = texB.width; - height = texB.height; - } - - if(!texB) - texB = GL.Texture.getWhiteTexture(); - - var type = LGraphTexture.getTextureType( this.properties.precision, tex ); - - if (!tex && !this._tex) { - this._tex = new GL.Texture(width, height, { type: type, format: gl.RGBA, filter: gl.LINEAR }); - } else { - this._tex = LGraphTexture.getTargetTexture( tex || this._tex, this._tex, this.properties.precision ); - } - - var uvcode = ""; - if (this.properties.uvcode) { - uvcode = "uv = " + this.properties.uvcode; - if (this.properties.uvcode.indexOf(";") != -1) { - //there are line breaks, means multiline code - uvcode = this.properties.uvcode; - } - } - - var pixelcode = ""; - if (this.properties.pixelcode) { - pixelcode = "result = " + this.properties.pixelcode; - if (this.properties.pixelcode.indexOf(";") != -1) { - //there are line breaks, means multiline code - pixelcode = this.properties.pixelcode; - } - } - - var shader = this._shader; - - if ( !this.has_error && (!shader || this._shader_code != uvcode + "|" + pixelcode) ) { - - var final_pixel_code = LGraphTexture.replaceCode( LGraphTextureOperation.pixel_shader, { UV_CODE:uvcode, PIXEL_CODE:pixelcode }); - - try { - shader = new GL.Shader( Shader.SCREEN_VERTEX_SHADER, final_pixel_code ); - this.boxcolor = "#00FF00"; - } catch (err) { - //console.log("Error compiling shader: ", err, final_pixel_code ); - GL.Shader.dumpErrorToConsole(err,Shader.SCREEN_VERTEX_SHADER, final_pixel_code); - this.boxcolor = "#FF0000"; - this.has_error = true; - return; - } - this._shader = shader; - this._shader_code = uvcode + "|" + pixelcode; - } - - if(!this._shader) - return; - - var value = this.getInputData(2); - if (value != null) { - this.properties.value = value; - } else { - value = parseFloat(this.properties.value); - } - - var time = this.graph.getTime(); - - this._tex.drawTo(function() { - gl.disable(gl.DEPTH_TEST); - gl.disable(gl.CULL_FACE); - gl.disable(gl.BLEND); - if (tex) { - tex.bind(0); - } - if (texB) { - texB.bind(1); - } - var mesh = Mesh.getScreenQuad(); - shader - .uniforms({ - u_texture: 0, - u_textureB: 1, - value: value, - texSize: [width, height,1/width,1/height], - time: time - }) - .draw(mesh); - }); - - this.setOutputData(0, this._tex); - }; - - LGraphTextureOperation.pixel_shader = - "precision highp float;\n\ - \n\ - uniform sampler2D u_texture;\n\ - uniform sampler2D u_textureB;\n\ - varying vec2 v_coord;\n\ - uniform vec4 texSize;\n\ - uniform float time;\n\ - uniform float value;\n\ - \n\ - void main() {\n\ - vec2 uv = v_coord;\n\ - {{UV_CODE}};\n\ - vec4 color4 = texture2D(u_texture, uv);\n\ - vec3 color = color4.rgb;\n\ - vec4 color4B = texture2D(u_textureB, uv);\n\ - vec3 colorB = color4B.rgb;\n\ - vec3 result = color;\n\ - float alpha = 1.0;\n\ - {{PIXEL_CODE}};\n\ - gl_FragColor = vec4(result, alpha);\n\ - }\n\ - "; - - LGraphTextureOperation.registerPreset = function ( name, code ) - { - LGraphTextureOperation.presets[name] = code; - } - - LGraphTextureOperation.registerPreset("",""); - LGraphTextureOperation.registerPreset("bypass","color"); - LGraphTextureOperation.registerPreset("add","color + colorB * value"); - LGraphTextureOperation.registerPreset("substract","(color - colorB) * value"); - LGraphTextureOperation.registerPreset("mate","mix( color, colorB, color4B.a * value)"); - LGraphTextureOperation.registerPreset("invert","vec3(1.0) - color"); - LGraphTextureOperation.registerPreset("multiply","color * colorB * value"); - LGraphTextureOperation.registerPreset("divide","(color / colorB) / value"); - LGraphTextureOperation.registerPreset("difference","abs(color - colorB) * value"); - LGraphTextureOperation.registerPreset("max","max(color, colorB) * value"); - LGraphTextureOperation.registerPreset("min","min(color, colorB) * value"); - LGraphTextureOperation.registerPreset("displace","texture2D(u_texture, uv + (colorB.xy - vec2(0.5)) * value).xyz"); - LGraphTextureOperation.registerPreset("grayscale","vec3(color.x + color.y + color.z) * value / 3.0"); - LGraphTextureOperation.registerPreset("saturation","mix( vec3(color.x + color.y + color.z) / 3.0, color, value )"); - LGraphTextureOperation.registerPreset("normalmap","\n\ - float z0 = texture2D(u_texture, uv + vec2(-texSize.z, -texSize.w) ).x;\n\ - float z1 = texture2D(u_texture, uv + vec2(0.0, -texSize.w) ).x;\n\ - float z2 = texture2D(u_texture, uv + vec2(texSize.z, -texSize.w) ).x;\n\ - float z3 = texture2D(u_texture, uv + vec2(-texSize.z, 0.0) ).x;\n\ - float z4 = color.x;\n\ - float z5 = texture2D(u_texture, uv + vec2(texSize.z, 0.0) ).x;\n\ - float z6 = texture2D(u_texture, uv + vec2(-texSize.z, texSize.w) ).x;\n\ - float z7 = texture2D(u_texture, uv + vec2(0.0, texSize.w) ).x;\n\ - float z8 = texture2D(u_texture, uv + vec2(texSize.z, texSize.w) ).x;\n\ - vec3 normal = vec3( z2 + 2.0*z4 + z7 - z0 - 2.0*z3 - z5, z5 + 2.0*z6 + z7 -z0 - 2.0*z1 - z2, 1.0 );\n\ - normal.xy *= value;\n\ - result.xyz = normalize(normal) * 0.5 + vec3(0.5);\n\ - "); - LGraphTextureOperation.registerPreset("threshold","vec3(color.x > colorB.x * value ? 1.0 : 0.0,color.y > colorB.y * value ? 1.0 : 0.0,color.z > colorB.z * value ? 1.0 : 0.0)"); - - //webglstudio stuff... - LGraphTextureOperation.prototype.onInspect = function(widgets) - { - var that = this; - widgets.addCombo("Presets","",{ values: Object.keys(LGraphTextureOperation.presets), callback: function(v){ - var code = LGraphTextureOperation.presets[v]; - if(!code) - return; - that.setProperty("pixelcode",code); - that.title = v; - widgets.refresh(); - }}); - } - - LiteGraph.registerNodeType("texture/operation", LGraphTextureOperation); - - //**************************************************** - - function LGraphTextureShader() { - this.addOutput("out", "Texture"); - this.properties = { - code: "", - u_value: 1, - u_color: [1,1,1,1], - width: 512, - height: 512, - precision: LGraphTexture.DEFAULT - }; - - this.properties.code = LGraphTextureShader.pixel_shader; - this._uniforms = { u_value: 1, u_color: vec4.create(), in_texture: 0, texSize: vec4.create(), time: 0 }; - } - - LGraphTextureShader.title = "Shader"; - LGraphTextureShader.desc = "Texture shader"; - LGraphTextureShader.widgets_info = { - code: { type: "code", lang: "glsl" }, - precision: { widget: "combo", values: LGraphTexture.MODE_VALUES } - }; - - LGraphTextureShader.prototype.onPropertyChanged = function( - name, - value - ) { - if (name != "code") { - return; - } - - var shader = this.getShader(); - if (!shader) { - return; - } - - //update connections - var uniforms = shader.uniformInfo; - - //remove deprecated slots - if (this.inputs) { - var already = {}; - for (var i = 0; i < this.inputs.length; ++i) { - var info = this.getInputInfo(i); - if (!info) { - continue; - } - - if (uniforms[info.name] && !already[info.name]) { - already[info.name] = true; - continue; - } - this.removeInput(i); - i--; - } - } - - //update existing ones - for (var i in uniforms) { - var info = shader.uniformInfo[i]; - if (info.loc === null) { - continue; - } //is an attribute, not a uniform - if (i == "time") { - //default one - continue; - } - - var type = "number"; - if (this._shader.samplers[i]) { - type = "texture"; - } else { - switch (info.size) { - case 1: - type = "number"; - break; - case 2: - type = "vec2"; - break; - case 3: - type = "vec3"; - break; - case 4: - type = "vec4"; - break; - case 9: - type = "mat3"; - break; - case 16: - type = "mat4"; - break; - default: - continue; - } - } - - var slot = this.findInputSlot(i); - if (slot == -1) { - this.addInput(i, type); - continue; - } - - var input_info = this.getInputInfo(slot); - if (!input_info) { - this.addInput(i, type); - } else { - if (input_info.type == type) { - continue; - } - this.removeInput(slot, type); - this.addInput(i, type); - } - } - }; - - LGraphTextureShader.prototype.getShader = function() { - //replug - if (this._shader && this._shader_code == this.properties.code) { - return this._shader; - } - - this._shader_code = this.properties.code; - this._shader = new GL.Shader( Shader.SCREEN_VERTEX_SHADER, this.properties.code ); - if (!this._shader) { - this.boxcolor = "red"; - return null; - } else { - this.boxcolor = "green"; - } - return this._shader; - }; - - LGraphTextureShader.prototype.onExecute = function() { - if (!this.isOutputConnected(0)) { - return; - } //saves work - - var shader = this.getShader(); - if (!shader) { - return; - } - - var tex_slot = 0; - var in_tex = null; - - //set uniforms - if(this.inputs) - for (var i = 0; i < this.inputs.length; ++i) { - var info = this.getInputInfo(i); - var data = this.getInputData(i); - if (data == null) { - continue; - } - - if (data.constructor === GL.Texture) { - data.bind(tex_slot); - if (!in_tex) { - in_tex = data; - } - data = tex_slot; - tex_slot++; - } - shader.setUniform(info.name, data); //data is tex_slot - } - - var uniforms = this._uniforms; - var type = LGraphTexture.getTextureType( this.properties.precision, in_tex ); - - //render to texture - var w = this.properties.width | 0; - var h = this.properties.height | 0; - if (w == 0) { - w = in_tex ? in_tex.width : gl.canvas.width; - } - if (h == 0) { - h = in_tex ? in_tex.height : gl.canvas.height; - } - uniforms.texSize[0] = w; - uniforms.texSize[1] = h; - uniforms.texSize[2] = 1/w; - uniforms.texSize[3] = 1/h; - uniforms.time = this.graph.getTime(); - uniforms.u_value = this.properties.u_value; - uniforms.u_color.set( this.properties.u_color ); - - if ( !this._tex || this._tex.type != type || this._tex.width != w || this._tex.height != h ) { - this._tex = new GL.Texture(w, h, { type: type, format: gl.RGBA, filter: gl.LINEAR }); - } - var tex = this._tex; - tex.drawTo(function() { - shader.uniforms(uniforms).draw(GL.Mesh.getScreenQuad()); - }); - - this.setOutputData(0, this._tex); - }; - - LGraphTextureShader.pixel_shader = -"precision highp float;\n\ -\n\ -varying vec2 v_coord;\n\ -uniform float time; //time in seconds\n\ -uniform vec4 texSize; //tex resolution\n\ -uniform float u_value;\n\ -uniform vec4 u_color;\n\n\ -void main() {\n\ - vec2 uv = v_coord;\n\ - vec3 color = vec3(0.0);\n\ - //your code here\n\ - color.xy=uv;\n\n\ - gl_FragColor = vec4(color, 1.0);\n\ -}\n\ -"; - - LiteGraph.registerNodeType("texture/shader", LGraphTextureShader); - - // Texture Scale Offset - - function LGraphTextureScaleOffset() { - this.addInput("in", "Texture"); - this.addInput("scale", "vec2"); - this.addInput("offset", "vec2"); - this.addOutput("out", "Texture"); - this.properties = { - offset: vec2.fromValues(0, 0), - scale: vec2.fromValues(1, 1), - precision: LGraphTexture.DEFAULT - }; - } - - LGraphTextureScaleOffset.widgets_info = { - precision: { widget: "combo", values: LGraphTexture.MODE_VALUES } - }; - - LGraphTextureScaleOffset.title = "Scale/Offset"; - LGraphTextureScaleOffset.desc = "Applies an scaling and offseting"; - - LGraphTextureScaleOffset.prototype.onExecute = function() { - var tex = this.getInputData(0); - - if (!this.isOutputConnected(0) || !tex) { - return; - } //saves work - - if (this.properties.precision === LGraphTexture.PASS_THROUGH) { - this.setOutputData(0, tex); - return; - } - - var width = tex.width; - var height = tex.height; - var type = this.precision === LGraphTexture.LOW ? gl.UNSIGNED_BYTE : gl.HIGH_PRECISION_FORMAT; - if (this.precision === LGraphTexture.DEFAULT) { - type = tex.type; - } - - if ( - !this._tex || - this._tex.width != width || - this._tex.height != height || - this._tex.type != type - ) { - this._tex = new GL.Texture(width, height, { - type: type, - format: gl.RGBA, - filter: gl.LINEAR - }); - } - - var shader = this._shader; - - if (!shader) { - shader = new GL.Shader( - GL.Shader.SCREEN_VERTEX_SHADER, - LGraphTextureScaleOffset.pixel_shader - ); - } - - var scale = this.getInputData(1); - if (scale) { - this.properties.scale[0] = scale[0]; - this.properties.scale[1] = scale[1]; - } else { - scale = this.properties.scale; - } - - var offset = this.getInputData(2); - if (offset) { - this.properties.offset[0] = offset[0]; - this.properties.offset[1] = offset[1]; - } else { - offset = this.properties.offset; - } - - this._tex.drawTo(function() { - gl.disable(gl.DEPTH_TEST); - gl.disable(gl.CULL_FACE); - gl.disable(gl.BLEND); - tex.bind(0); - var mesh = Mesh.getScreenQuad(); - shader - .uniforms({ - u_texture: 0, - u_scale: scale, - u_offset: offset - }) - .draw(mesh); - }); - - this.setOutputData(0, this._tex); - }; - - LGraphTextureScaleOffset.pixel_shader = - "precision highp float;\n\ - \n\ - uniform sampler2D u_texture;\n\ - uniform sampler2D u_textureB;\n\ - varying vec2 v_coord;\n\ - uniform vec2 u_scale;\n\ - uniform vec2 u_offset;\n\ - \n\ - void main() {\n\ - vec2 uv = v_coord;\n\ - uv = uv / u_scale - u_offset;\n\ - gl_FragColor = texture2D(u_texture, uv);\n\ - }\n\ - "; - - LiteGraph.registerNodeType( - "texture/scaleOffset", - LGraphTextureScaleOffset - ); - - // Warp (distort a texture) ************************* - - function LGraphTextureWarp() { - this.addInput("in", "Texture"); - this.addInput("warp", "Texture"); - this.addInput("factor", "number"); - this.addOutput("out", "Texture"); - this.properties = { - factor: 0.01, - scale: [1,1], - offset: [0,0], - precision: LGraphTexture.DEFAULT - }; - - this._uniforms = { - u_texture: 0, - u_textureB: 1, - u_factor: 1, - u_scale: vec2.create(), - u_offset: vec2.create() - }; - } - - LGraphTextureWarp.widgets_info = { - precision: { widget: "combo", values: LGraphTexture.MODE_VALUES } - }; - - LGraphTextureWarp.title = "Warp"; - LGraphTextureWarp.desc = "Texture warp operation"; - - LGraphTextureWarp.prototype.onExecute = function() { - var tex = this.getInputData(0); - - if (!this.isOutputConnected(0)) { - return; - } //saves work - - if (this.properties.precision === LGraphTexture.PASS_THROUGH) { - this.setOutputData(0, tex); - return; - } - - var texB = this.getInputData(1); - - var width = 512; - var height = 512; - var type = gl.UNSIGNED_BYTE; - if (tex) { - width = tex.width; - height = tex.height; - type = tex.type; - } else if (texB) { - width = texB.width; - height = texB.height; - type = texB.type; - } - - if (!tex && !this._tex) { - this._tex = new GL.Texture(width, height, { - type: - this.precision === LGraphTexture.LOW - ? gl.UNSIGNED_BYTE - : gl.HIGH_PRECISION_FORMAT, - format: gl.RGBA, - filter: gl.LINEAR - }); - } else { - this._tex = LGraphTexture.getTargetTexture( - tex || this._tex, - this._tex, - this.properties.precision - ); - } - - var shader = this._shader; - - if (!shader) { - shader = new GL.Shader( - GL.Shader.SCREEN_VERTEX_SHADER, - LGraphTextureWarp.pixel_shader - ); - } - - var factor = this.getInputData(2); - if (factor != null) { - this.properties.factor = factor; - } else { - factor = parseFloat(this.properties.factor); - } - var uniforms = this._uniforms; - uniforms.u_factor = factor; - uniforms.u_scale.set( this.properties.scale ); - uniforms.u_offset.set( this.properties.offset ); - - this._tex.drawTo(function() { - gl.disable(gl.DEPTH_TEST); - gl.disable(gl.CULL_FACE); - gl.disable(gl.BLEND); - if (tex) { - tex.bind(0); - } - if (texB) { - texB.bind(1); - } - var mesh = Mesh.getScreenQuad(); - shader - .uniforms( uniforms ) - .draw(mesh); - }); - - this.setOutputData(0, this._tex); - }; - - LGraphTextureWarp.pixel_shader = - "precision highp float;\n\ - \n\ - uniform sampler2D u_texture;\n\ - uniform sampler2D u_textureB;\n\ - varying vec2 v_coord;\n\ - uniform float u_factor;\n\ - uniform vec2 u_scale;\n\ - uniform vec2 u_offset;\n\ - \n\ - void main() {\n\ - vec2 uv = v_coord;\n\ - uv += ( texture2D(u_textureB, uv).rg - vec2(0.5)) * u_factor * u_scale + u_offset;\n\ - gl_FragColor = texture2D(u_texture, uv);\n\ - }\n\ - "; - - LiteGraph.registerNodeType("texture/warp", LGraphTextureWarp); - - //**************************************************** - - // Texture to Viewport ***************************************** - function LGraphTextureToViewport() { - this.addInput("Texture", "Texture"); - this.properties = { - additive: false, - antialiasing: false, - filter: true, - disable_alpha: false, - gamma: 1.0, - viewport: [0,0,1,1] - }; - this.size[0] = 130; - } - - LGraphTextureToViewport.title = "to Viewport"; - LGraphTextureToViewport.desc = "Texture to viewport"; - - LGraphTextureToViewport._prev_viewport = new Float32Array(4); - - LGraphTextureToViewport.prototype.onDrawBackground = function( ctx ) - { - if ( this.flags.collapsed || this.size[1] <= 40 ) - return; - - var tex = this.getInputData(0); - if (!tex) { - return; - } - - ctx.drawImage( ctx == gl ? tex : gl.canvas, 10,30, this.size[0] -20, this.size[1] -40); - } - - LGraphTextureToViewport.prototype.onExecute = function() { - var tex = this.getInputData(0); - if (!tex) { - return; - } - - if (this.properties.disable_alpha) { - gl.disable(gl.BLEND); - } else { - gl.enable(gl.BLEND); - if (this.properties.additive) { - gl.blendFunc(gl.SRC_ALPHA, gl.ONE); - } else { - gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA); - } - } - - gl.disable(gl.DEPTH_TEST); - var gamma = this.properties.gamma || 1.0; - if (this.isInputConnected(1)) { - gamma = this.getInputData(1); - } - - tex.setParameter( - gl.TEXTURE_MAG_FILTER, - this.properties.filter ? gl.LINEAR : gl.NEAREST - ); - - var old_viewport = LGraphTextureToViewport._prev_viewport; - old_viewport.set( gl.viewport_data ); - var new_view = this.properties.viewport; - gl.viewport( old_viewport[0] + old_viewport[2] * new_view[0], old_viewport[1] + old_viewport[3] * new_view[1], old_viewport[2] * new_view[2], old_viewport[3] * new_view[3] ); - var viewport = gl.getViewport(); //gl.getParameter(gl.VIEWPORT); - - if (this.properties.antialiasing) { - if (!LGraphTextureToViewport._shader) { - LGraphTextureToViewport._shader = new GL.Shader( - GL.Shader.SCREEN_VERTEX_SHADER, - LGraphTextureToViewport.aa_pixel_shader - ); - } - - var mesh = Mesh.getScreenQuad(); - tex.bind(0); - LGraphTextureToViewport._shader - .uniforms({ - u_texture: 0, - uViewportSize: [tex.width, tex.height], - u_igamma: 1 / gamma, - inverseVP: [1 / tex.width, 1 / tex.height] - }) - .draw(mesh); - } else { - if (gamma != 1.0) { - if (!LGraphTextureToViewport._gamma_shader) { - LGraphTextureToViewport._gamma_shader = new GL.Shader( - Shader.SCREEN_VERTEX_SHADER, - LGraphTextureToViewport.gamma_pixel_shader - ); - } - tex.toViewport(LGraphTextureToViewport._gamma_shader, { - u_texture: 0, - u_igamma: 1 / gamma - }); - } else { - tex.toViewport(); - } - } - - gl.viewport( old_viewport[0], old_viewport[1], old_viewport[2], old_viewport[3] ); - }; - - LGraphTextureToViewport.prototype.onGetInputs = function() { - return [["gamma", "number"]]; - }; - - LGraphTextureToViewport.aa_pixel_shader = - "precision highp float;\n\ - precision highp float;\n\ - varying vec2 v_coord;\n\ - uniform sampler2D u_texture;\n\ - uniform vec2 uViewportSize;\n\ - uniform vec2 inverseVP;\n\ - uniform float u_igamma;\n\ - #define FXAA_REDUCE_MIN (1.0/ 128.0)\n\ - #define FXAA_REDUCE_MUL (1.0 / 8.0)\n\ - #define FXAA_SPAN_MAX 8.0\n\ - \n\ - /* from mitsuhiko/webgl-meincraft based on the code on geeks3d.com */\n\ - vec4 applyFXAA(sampler2D tex, vec2 fragCoord)\n\ - {\n\ - vec4 color = vec4(0.0);\n\ - /*vec2 inverseVP = vec2(1.0 / uViewportSize.x, 1.0 / uViewportSize.y);*/\n\ - vec3 rgbNW = texture2D(tex, (fragCoord + vec2(-1.0, -1.0)) * inverseVP).xyz;\n\ - vec3 rgbNE = texture2D(tex, (fragCoord + vec2(1.0, -1.0)) * inverseVP).xyz;\n\ - vec3 rgbSW = texture2D(tex, (fragCoord + vec2(-1.0, 1.0)) * inverseVP).xyz;\n\ - vec3 rgbSE = texture2D(tex, (fragCoord + vec2(1.0, 1.0)) * inverseVP).xyz;\n\ - vec3 rgbM = texture2D(tex, fragCoord * inverseVP).xyz;\n\ - vec3 luma = vec3(0.299, 0.587, 0.114);\n\ - float lumaNW = dot(rgbNW, luma);\n\ - float lumaNE = dot(rgbNE, luma);\n\ - float lumaSW = dot(rgbSW, luma);\n\ - float lumaSE = dot(rgbSE, luma);\n\ - float lumaM = dot(rgbM, luma);\n\ - float lumaMin = min(lumaM, min(min(lumaNW, lumaNE), min(lumaSW, lumaSE)));\n\ - float lumaMax = max(lumaM, max(max(lumaNW, lumaNE), max(lumaSW, lumaSE)));\n\ - \n\ - vec2 dir;\n\ - dir.x = -((lumaNW + lumaNE) - (lumaSW + lumaSE));\n\ - dir.y = ((lumaNW + lumaSW) - (lumaNE + lumaSE));\n\ - \n\ - float dirReduce = max((lumaNW + lumaNE + lumaSW + lumaSE) * (0.25 * FXAA_REDUCE_MUL), FXAA_REDUCE_MIN);\n\ - \n\ - float rcpDirMin = 1.0 / (min(abs(dir.x), abs(dir.y)) + dirReduce);\n\ - dir = min(vec2(FXAA_SPAN_MAX, FXAA_SPAN_MAX), max(vec2(-FXAA_SPAN_MAX, -FXAA_SPAN_MAX), dir * rcpDirMin)) * inverseVP;\n\ - \n\ - vec3 rgbA = 0.5 * (texture2D(tex, fragCoord * inverseVP + dir * (1.0 / 3.0 - 0.5)).xyz + \n\ - texture2D(tex, fragCoord * inverseVP + dir * (2.0 / 3.0 - 0.5)).xyz);\n\ - vec3 rgbB = rgbA * 0.5 + 0.25 * (texture2D(tex, fragCoord * inverseVP + dir * -0.5).xyz + \n\ - texture2D(tex, fragCoord * inverseVP + dir * 0.5).xyz);\n\ - \n\ - //return vec4(rgbA,1.0);\n\ - float lumaB = dot(rgbB, luma);\n\ - if ((lumaB < lumaMin) || (lumaB > lumaMax))\n\ - color = vec4(rgbA, 1.0);\n\ - else\n\ - color = vec4(rgbB, 1.0);\n\ - if(u_igamma != 1.0)\n\ - color.xyz = pow( color.xyz, vec3(u_igamma) );\n\ - return color;\n\ - }\n\ - \n\ - void main() {\n\ - gl_FragColor = applyFXAA( u_texture, v_coord * uViewportSize) ;\n\ - }\n\ - "; - - LGraphTextureToViewport.gamma_pixel_shader = - "precision highp float;\n\ - precision highp float;\n\ - varying vec2 v_coord;\n\ - uniform sampler2D u_texture;\n\ - uniform float u_igamma;\n\ - void main() {\n\ - vec4 color = texture2D( u_texture, v_coord);\n\ - color.xyz = pow(color.xyz, vec3(u_igamma) );\n\ - gl_FragColor = color;\n\ - }\n\ - "; - - LiteGraph.registerNodeType( - "texture/toviewport", - LGraphTextureToViewport - ); - - // Texture Copy ***************************************** - function LGraphTextureCopy() { - this.addInput("Texture", "Texture"); - this.addOutput("", "Texture"); - this.properties = { - size: 0, - generate_mipmaps: false, - precision: LGraphTexture.DEFAULT - }; - } - - LGraphTextureCopy.title = "Copy"; - LGraphTextureCopy.desc = "Copy Texture"; - LGraphTextureCopy.widgets_info = { - size: { - widget: "combo", - values: [0, 32, 64, 128, 256, 512, 1024, 2048] - }, - precision: { widget: "combo", values: LGraphTexture.MODE_VALUES } - }; - - LGraphTextureCopy.prototype.onExecute = function() { - var tex = this.getInputData(0); - if (!tex && !this._temp_texture) { - return; - } - - if (!this.isOutputConnected(0)) { - return; - } //saves work - - //copy the texture - if (tex) { - var width = tex.width; - var height = tex.height; - - if (this.properties.size != 0) { - width = this.properties.size; - height = this.properties.size; - } - - var temp = this._temp_texture; - - var type = tex.type; - if (this.properties.precision === LGraphTexture.LOW) { - type = gl.UNSIGNED_BYTE; - } else if (this.properties.precision === LGraphTexture.HIGH) { - type = gl.HIGH_PRECISION_FORMAT; - } - - if ( - !temp || - temp.width != width || - temp.height != height || - temp.type != type - ) { - var minFilter = gl.LINEAR; - if ( - this.properties.generate_mipmaps && - isPowerOfTwo(width) && - isPowerOfTwo(height) - ) { - minFilter = gl.LINEAR_MIPMAP_LINEAR; - } - this._temp_texture = new GL.Texture(width, height, { - type: type, - format: gl.RGBA, - minFilter: minFilter, - magFilter: gl.LINEAR - }); - } - tex.copyTo(this._temp_texture); - - if (this.properties.generate_mipmaps) { - this._temp_texture.bind(0); - gl.generateMipmap(this._temp_texture.texture_type); - this._temp_texture.unbind(0); - } - } - - this.setOutputData(0, this._temp_texture); - }; - - LiteGraph.registerNodeType("texture/copy", LGraphTextureCopy); - - // Texture Downsample ***************************************** - function LGraphTextureDownsample() { - this.addInput("Texture", "Texture"); - this.addOutput("", "Texture"); - this.properties = { - iterations: 1, - generate_mipmaps: false, - precision: LGraphTexture.DEFAULT - }; - } - - LGraphTextureDownsample.title = "Downsample"; - LGraphTextureDownsample.desc = "Downsample Texture"; - LGraphTextureDownsample.widgets_info = { - iterations: { type: "number", step: 1, precision: 0, min: 0 }, - precision: { widget: "combo", values: LGraphTexture.MODE_VALUES } - }; - - LGraphTextureDownsample.prototype.onExecute = function() { - var tex = this.getInputData(0); - if (!tex && !this._temp_texture) { - return; - } - - if (!this.isOutputConnected(0)) { - return; - } //saves work - - //we do not allow any texture different than texture 2D - if (!tex || tex.texture_type !== GL.TEXTURE_2D) { - return; - } - - if (this.properties.iterations < 1) { - this.setOutputData(0, tex); - return; - } - - var shader = LGraphTextureDownsample._shader; - if (!shader) { - LGraphTextureDownsample._shader = shader = new GL.Shader( - GL.Shader.SCREEN_VERTEX_SHADER, - LGraphTextureDownsample.pixel_shader - ); - } - - var width = tex.width | 0; - var height = tex.height | 0; - var type = tex.type; - if (this.properties.precision === LGraphTexture.LOW) { - type = gl.UNSIGNED_BYTE; - } else if (this.properties.precision === LGraphTexture.HIGH) { - type = gl.HIGH_PRECISION_FORMAT; - } - var iterations = this.properties.iterations || 1; - - var origin = tex; - var target = null; - - var temp = []; - var options = { - type: type, - format: tex.format - }; - - var offset = vec2.create(); - var uniforms = { - u_offset: offset - }; - - if (this._texture) { - GL.Texture.releaseTemporary(this._texture); - } - - for (var i = 0; i < iterations; ++i) { - offset[0] = 1 / width; - offset[1] = 1 / height; - width = width >> 1 || 0; - height = height >> 1 || 0; - target = GL.Texture.getTemporary(width, height, options); - temp.push(target); - origin.setParameter(GL.TEXTURE_MAG_FILTER, GL.NEAREST); - origin.copyTo(target, shader, uniforms); - if (width == 1 && height == 1) { - break; - } //nothing else to do - origin = target; - } - - //keep the last texture used - this._texture = temp.pop(); - - //free the rest - for (var i = 0; i < temp.length; ++i) { - GL.Texture.releaseTemporary(temp[i]); - } - - if (this.properties.generate_mipmaps) { - this._texture.bind(0); - gl.generateMipmap(this._texture.texture_type); - this._texture.unbind(0); - } - - this.setOutputData(0, this._texture); - }; - - LGraphTextureDownsample.pixel_shader = - "precision highp float;\n\ - precision highp float;\n\ - uniform sampler2D u_texture;\n\ - uniform vec2 u_offset;\n\ - varying vec2 v_coord;\n\ - \n\ - void main() {\n\ - vec4 color = texture2D(u_texture, v_coord );\n\ - color += texture2D(u_texture, v_coord + vec2( u_offset.x, 0.0 ) );\n\ - color += texture2D(u_texture, v_coord + vec2( 0.0, u_offset.y ) );\n\ - color += texture2D(u_texture, v_coord + vec2( u_offset.x, u_offset.y ) );\n\ - gl_FragColor = color * 0.25;\n\ - }\n\ - "; - - LiteGraph.registerNodeType( - "texture/downsample", - LGraphTextureDownsample - ); - - - - function LGraphTextureResize() { - this.addInput("Texture", "Texture"); - this.addOutput("", "Texture"); - this.properties = { - size: [512,512], - generate_mipmaps: false, - precision: LGraphTexture.DEFAULT - }; - } - - LGraphTextureResize.title = "Resize"; - LGraphTextureResize.desc = "Resize Texture"; - LGraphTextureResize.widgets_info = { - iterations: { type: "number", step: 1, precision: 0, min: 0 }, - precision: { widget: "combo", values: LGraphTexture.MODE_VALUES } - }; - - LGraphTextureResize.prototype.onExecute = function() { - var tex = this.getInputData(0); - if (!tex && !this._temp_texture) { - return; - } - - if (!this.isOutputConnected(0)) { - return; - } //saves work - - //we do not allow any texture different than texture 2D - if (!tex || tex.texture_type !== GL.TEXTURE_2D) { - return; - } - - var width = this.properties.size[0] | 0; - var height = this.properties.size[1] | 0; - if(width == 0) - width = tex.width; - if(height == 0) - height = tex.height; - var type = tex.type; - if (this.properties.precision === LGraphTexture.LOW) { - type = gl.UNSIGNED_BYTE; - } else if (this.properties.precision === LGraphTexture.HIGH) { - type = gl.HIGH_PRECISION_FORMAT; - } - - if( !this._texture || this._texture.width != width || this._texture.height != height || this._texture.type != type ) - this._texture = new GL.Texture( width, height, { type: type } ); - - tex.copyTo( this._texture ); - - if (this.properties.generate_mipmaps) { - this._texture.bind(0); - gl.generateMipmap(this._texture.texture_type); - this._texture.unbind(0); - } - - this.setOutputData(0, this._texture); - }; - - LiteGraph.registerNodeType( "texture/resize", LGraphTextureResize ); - - // Texture Average ***************************************** - function LGraphTextureAverage() { - this.addInput("Texture", "Texture"); - this.addOutput("tex", "Texture"); - this.addOutput("avg", "vec4"); - this.addOutput("lum", "number"); - this.properties = { - use_previous_frame: true, //to avoid stalls - high_quality: false //to use as much pixels as possible - }; - - this._uniforms = { - u_texture: 0, - u_mipmap_offset: 0 - }; - this._luminance = new Float32Array(4); - } - - LGraphTextureAverage.title = "Average"; - LGraphTextureAverage.desc = - "Compute a partial average (32 random samples) of a texture and stores it as a 1x1 pixel texture.\n If high_quality is true, then it generates the mipmaps first and reads from the lower one."; - - LGraphTextureAverage.prototype.onExecute = function() { - if (!this.properties.use_previous_frame) { - this.updateAverage(); - } - - var v = this._luminance; - this.setOutputData(0, this._temp_texture); - this.setOutputData(1, v); - this.setOutputData(2, (v[0] + v[1] + v[2]) / 3); - }; - - //executed before rendering the frame - LGraphTextureAverage.prototype.onPreRenderExecute = function() { - this.updateAverage(); - }; - - LGraphTextureAverage.prototype.updateAverage = function() { - var tex = this.getInputData(0); - if (!tex) { - return; - } - - if ( - !this.isOutputConnected(0) && - !this.isOutputConnected(1) && - !this.isOutputConnected(2) - ) { - return; - } //saves work - - if (!LGraphTextureAverage._shader) { - LGraphTextureAverage._shader = new GL.Shader( - GL.Shader.SCREEN_VERTEX_SHADER, - LGraphTextureAverage.pixel_shader - ); - //creates 256 random numbers and stores them in two mat4 - var samples = new Float32Array(16); - for (var i = 0; i < samples.length; ++i) { - samples[i] = Math.random(); //poorly distributed samples - } - //upload only once - LGraphTextureAverage._shader.uniforms({ - u_samples_a: samples.subarray(0, 16), - u_samples_b: samples.subarray(16, 32) - }); - } - - var temp = this._temp_texture; - var type = gl.UNSIGNED_BYTE; - if (tex.type != type) { - //force floats, half floats cannot be read with gl.readPixels - type = gl.FLOAT; - } - - if (!temp || temp.type != type) { - this._temp_texture = new GL.Texture(1, 1, { - type: type, - format: gl.RGBA, - filter: gl.NEAREST - }); - } - - this._uniforms.u_mipmap_offset = 0; - - if(this.properties.high_quality) - { - if( !this._temp_pot2_texture || this._temp_pot2_texture.type != type ) - this._temp_pot2_texture = new GL.Texture(512, 512, { - type: type, - format: gl.RGBA, - minFilter: gl.LINEAR_MIPMAP_LINEAR, - magFilter: gl.LINEAR - }); - - tex.copyTo( this._temp_pot2_texture ); - tex = this._temp_pot2_texture; - tex.bind(0); - gl.generateMipmap(GL.TEXTURE_2D); - this._uniforms.u_mipmap_offset = 9; - } - - var shader = LGraphTextureAverage._shader; - var uniforms = this._uniforms; - uniforms.u_mipmap_offset = this.properties.mipmap_offset; - gl.disable(gl.DEPTH_TEST); - gl.disable(gl.BLEND); - this._temp_texture.drawTo(function() { - tex.toViewport(shader, uniforms); - }); - - if (this.isOutputConnected(1) || this.isOutputConnected(2)) { - var pixel = this._temp_texture.getPixels(); - if (pixel) { - var v = this._luminance; - var type = this._temp_texture.type; - v.set(pixel); - if (type == gl.UNSIGNED_BYTE) { - vec4.scale(v, v, 1 / 255); - } else if ( - type == GL.HALF_FLOAT || - type == GL.HALF_FLOAT_OES - ) { - //no half floats possible, hard to read back unless copyed to a FLOAT texture, so temp_texture is always forced to FLOAT - } - } - } - }; - - LGraphTextureAverage.pixel_shader = - "precision highp float;\n\ - precision highp float;\n\ - uniform mat4 u_samples_a;\n\ - uniform mat4 u_samples_b;\n\ - uniform sampler2D u_texture;\n\ - uniform float u_mipmap_offset;\n\ - varying vec2 v_coord;\n\ - \n\ - void main() {\n\ - vec4 color = vec4(0.0);\n\ - //random average\n\ - for(int i = 0; i < 4; ++i)\n\ - for(int j = 0; j < 4; ++j)\n\ - {\n\ - color += texture2D(u_texture, vec2( u_samples_a[i][j], u_samples_b[i][j] ), u_mipmap_offset );\n\ - color += texture2D(u_texture, vec2( 1.0 - u_samples_a[i][j], 1.0 - u_samples_b[i][j] ), u_mipmap_offset );\n\ - }\n\ - gl_FragColor = color * 0.03125;\n\ - }\n\ - "; - - LiteGraph.registerNodeType("texture/average", LGraphTextureAverage); - - - - // Computes operation between pixels (max, min) ***************************************** - function LGraphTextureMinMax() { - this.addInput("Texture", "Texture"); - this.addOutput("min_t", "Texture"); - this.addOutput("max_t", "Texture"); - this.addOutput("min", "vec4"); - this.addOutput("max", "vec4"); - this.properties = { - mode: "max", - use_previous_frame: true //to avoid stalls - }; - - this._uniforms = { - u_texture: 0 - }; - - this._max = new Float32Array(4); - this._min = new Float32Array(4); - - this._textures_chain = []; - } - - LGraphTextureMinMax.widgets_info = { - mode: { widget: "combo", values: ["min","max","avg"] } - }; - - LGraphTextureMinMax.title = "MinMax"; - LGraphTextureMinMax.desc = "Compute the scene min max"; - - LGraphTextureMinMax.prototype.onExecute = function() { - if (!this.properties.use_previous_frame) { - this.update(); - } - - this.setOutputData(0, this._temp_texture); - this.setOutputData(1, this._luminance); - }; - - //executed before rendering the frame - LGraphTextureMinMax.prototype.onPreRenderExecute = function() { - this.update(); - }; - - LGraphTextureMinMax.prototype.update = function() { - var tex = this.getInputData(0); - if (!tex) { - return; - } - - if ( !this.isOutputConnected(0) && !this.isOutputConnected(1) ) { - return; - } //saves work - - if (!LGraphTextureMinMax._shader) { - LGraphTextureMinMax._shader = new GL.Shader( GL.Shader.SCREEN_VERTEX_SHADER, LGraphTextureMinMax.pixel_shader ); - } - - var temp = this._temp_texture; - var type = gl.UNSIGNED_BYTE; - if (tex.type != type) { - //force floats, half floats cannot be read with gl.readPixels - type = gl.FLOAT; - } - - var size = 512; - - if( !this._textures_chain.length || this._textures_chain[0].type != type ) - { - var index = 0; - while(i) - { - this._textures_chain[i] = new GL.Texture( size, size, { - type: type, - format: gl.RGBA, - filter: gl.NEAREST - }); - size = size >> 2; - i++; - if(size == 1) - break; - } - } - - tex.copyTo( this._textures_chain[0] ); - var prev = this._textures_chain[0]; - for(var i = 1; i <= this._textures_chain.length; ++i) - { - var tex = this._textures_chain[i]; - - prev = tex; - } - - var shader = LGraphTextureMinMax._shader; - var uniforms = this._uniforms; - uniforms.u_mipmap_offset = this.properties.mipmap_offset; - gl.disable(gl.DEPTH_TEST); - gl.disable(gl.BLEND); - this._temp_texture.drawTo(function() { - tex.toViewport(shader, uniforms); - }); - }; - - LGraphTextureMinMax.pixel_shader = - "precision highp float;\n\ - precision highp float;\n\ - uniform mat4 u_samples_a;\n\ - uniform mat4 u_samples_b;\n\ - uniform sampler2D u_texture;\n\ - uniform float u_mipmap_offset;\n\ - varying vec2 v_coord;\n\ - \n\ - void main() {\n\ - vec4 color = vec4(0.0);\n\ - //random average\n\ - for(int i = 0; i < 4; ++i)\n\ - for(int j = 0; j < 4; ++j)\n\ - {\n\ - color += texture2D(u_texture, vec2( u_samples_a[i][j], u_samples_b[i][j] ), u_mipmap_offset );\n\ - color += texture2D(u_texture, vec2( 1.0 - u_samples_a[i][j], 1.0 - u_samples_b[i][j] ), u_mipmap_offset );\n\ - }\n\ - gl_FragColor = color * 0.03125;\n\ - }\n\ - "; - - //LiteGraph.registerNodeType("texture/clustered_operation", LGraphTextureClusteredOperation); - - - function LGraphTextureTemporalSmooth() { - this.addInput("in", "Texture"); - this.addInput("factor", "Number"); - this.addOutput("out", "Texture"); - this.properties = { factor: 0.5 }; - this._uniforms = { - u_texture: 0, - u_textureB: 1, - u_factor: this.properties.factor - }; - } - - LGraphTextureTemporalSmooth.title = "Smooth"; - LGraphTextureTemporalSmooth.desc = "Smooth texture over time"; - - LGraphTextureTemporalSmooth.prototype.onExecute = function() { - var tex = this.getInputData(0); - if (!tex || !this.isOutputConnected(0)) { - return; - } - - if (!LGraphTextureTemporalSmooth._shader) { - LGraphTextureTemporalSmooth._shader = new GL.Shader( - GL.Shader.SCREEN_VERTEX_SHADER, - LGraphTextureTemporalSmooth.pixel_shader - ); - } - - var temp = this._temp_texture; - if ( - !temp || - temp.type != tex.type || - temp.width != tex.width || - temp.height != tex.height - ) { - var options = { - type: tex.type, - format: gl.RGBA, - filter: gl.NEAREST - }; - this._temp_texture = new GL.Texture(tex.width, tex.height, options ); - this._temp_texture2 = new GL.Texture(tex.width, tex.height, options ); - tex.copyTo(this._temp_texture2); - } - - var tempA = this._temp_texture; - var tempB = this._temp_texture2; - - var shader = LGraphTextureTemporalSmooth._shader; - var uniforms = this._uniforms; - uniforms.u_factor = 1.0 - this.getInputOrProperty("factor"); - - gl.disable(gl.BLEND); - gl.disable(gl.DEPTH_TEST); - tempA.drawTo(function() { - tempB.bind(1); - tex.toViewport(shader, uniforms); - }); - - this.setOutputData(0, tempA); - - //swap - this._temp_texture = tempB; - this._temp_texture2 = tempA; - }; - - LGraphTextureTemporalSmooth.pixel_shader = - "precision highp float;\n\ - precision highp float;\n\ - uniform sampler2D u_texture;\n\ - uniform sampler2D u_textureB;\n\ - uniform float u_factor;\n\ - varying vec2 v_coord;\n\ - \n\ - void main() {\n\ - gl_FragColor = mix( texture2D( u_texture, v_coord ), texture2D( u_textureB, v_coord ), u_factor );\n\ - }\n\ - "; - - LiteGraph.registerNodeType( "texture/temporal_smooth", LGraphTextureTemporalSmooth ); - - - function LGraphTextureLinearAvgSmooth() { - this.addInput("in", "Texture"); - this.addOutput("avg", "Texture"); - this.addOutput("array", "Texture"); - this.properties = { samples: 64, frames_interval: 1 }; - this._uniforms = { - u_texture: 0, - u_textureB: 1, - u_samples: this.properties.samples, - u_isamples: 1/this.properties.samples - }; - this.frame = 0; - } - - LGraphTextureLinearAvgSmooth.title = "Lineal Avg Smooth"; - LGraphTextureLinearAvgSmooth.desc = "Smooth texture linearly over time"; - - LGraphTextureLinearAvgSmooth["@samples"] = { type: "number", min: 1, max: 64, step: 1, precision: 1 }; - - LGraphTextureLinearAvgSmooth.prototype.getPreviewTexture = function() - { - return this._temp_texture2; - } - - LGraphTextureLinearAvgSmooth.prototype.onExecute = function() { - - var tex = this.getInputData(0); - if (!tex || !this.isOutputConnected(0)) { - return; - } - - if (!LGraphTextureLinearAvgSmooth._shader) { - LGraphTextureLinearAvgSmooth._shader_copy = new GL.Shader( GL.Shader.SCREEN_VERTEX_SHADER, LGraphTextureLinearAvgSmooth.pixel_shader_copy ); - LGraphTextureLinearAvgSmooth._shader_avg = new GL.Shader( GL.Shader.SCREEN_VERTEX_SHADER, LGraphTextureLinearAvgSmooth.pixel_shader_avg ); - } - - var samples = clamp(this.properties.samples,0,64); - var frame = this.frame; - var interval = this.properties.frames_interval; - - if( interval == 0 || frame % interval == 0 ) - { - var temp = this._temp_texture; - if ( !temp || temp.type != tex.type || temp.width != samples ) { - var options = { - type: tex.type, - format: gl.RGBA, - filter: gl.NEAREST - }; - this._temp_texture = new GL.Texture( samples, 1, options ); - this._temp_texture2 = new GL.Texture( samples, 1, options ); - this._temp_texture_out = new GL.Texture( 1, 1, options ); - } - - var tempA = this._temp_texture; - var tempB = this._temp_texture2; - - var shader_copy = LGraphTextureLinearAvgSmooth._shader_copy; - var shader_avg = LGraphTextureLinearAvgSmooth._shader_avg; - var uniforms = this._uniforms; - uniforms.u_samples = samples; - uniforms.u_isamples = 1.0 / samples; - - gl.disable(gl.BLEND); - gl.disable(gl.DEPTH_TEST); - tempA.drawTo(function() { - tempB.bind(1); - tex.toViewport( shader_copy, uniforms ); - }); - - this._temp_texture_out.drawTo(function() { - tempA.toViewport( shader_avg, uniforms ); - }); - - this.setOutputData( 0, this._temp_texture_out ); - - //swap - this._temp_texture = tempB; - this._temp_texture2 = tempA; - } - else - this.setOutputData(0, this._temp_texture_out); - this.setOutputData(1, this._temp_texture2); - this.frame++; - }; - - LGraphTextureLinearAvgSmooth.pixel_shader_copy = - "precision highp float;\n\ - precision highp float;\n\ - uniform sampler2D u_texture;\n\ - uniform sampler2D u_textureB;\n\ - uniform float u_isamples;\n\ - varying vec2 v_coord;\n\ - \n\ - void main() {\n\ - if( v_coord.x <= u_isamples )\n\ - gl_FragColor = texture2D( u_texture, vec2(0.5) );\n\ - else\n\ - gl_FragColor = texture2D( u_textureB, v_coord - vec2(u_isamples,0.0) );\n\ - }\n\ - "; - - LGraphTextureLinearAvgSmooth.pixel_shader_avg = - "precision highp float;\n\ - precision highp float;\n\ - uniform sampler2D u_texture;\n\ - uniform int u_samples;\n\ - uniform float u_isamples;\n\ - varying vec2 v_coord;\n\ - \n\ - void main() {\n\ - vec4 color = vec4(0.0);\n\ - for(int i = 0; i < 64; ++i)\n\ - {\n\ - color += texture2D( u_texture, vec2( float(i)*u_isamples,0.0) );\n\ - if(i == (u_samples - 1))\n\ - break;\n\ - }\n\ - gl_FragColor = color * u_isamples;\n\ - }\n\ - "; - - - LiteGraph.registerNodeType( "texture/linear_avg_smooth", LGraphTextureLinearAvgSmooth ); - - // Image To Texture ***************************************** - function LGraphImageToTexture() { - this.addInput("Image", "image"); - this.addOutput("", "Texture"); - this.properties = {}; - } - - LGraphImageToTexture.title = "Image to Texture"; - LGraphImageToTexture.desc = "Uploads an image to the GPU"; - //LGraphImageToTexture.widgets_info = { size: { widget:"combo", values:[0,32,64,128,256,512,1024,2048]} }; - - LGraphImageToTexture.prototype.onExecute = function() { - var img = this.getInputData(0); - if (!img) { - return; - } - - var width = img.videoWidth || img.width; - var height = img.videoHeight || img.height; - - //this is in case we are using a webgl canvas already, no need to reupload it - if (img.gltexture) { - this.setOutputData(0, img.gltexture); - return; - } - - var temp = this._temp_texture; - if (!temp || temp.width != width || temp.height != height) { - this._temp_texture = new GL.Texture(width, height, { - format: gl.RGBA, - filter: gl.LINEAR - }); - } - - try { - this._temp_texture.uploadImage(img); - } catch (err) { - console.error( - "image comes from an unsafe location, cannot be uploaded to webgl: " + - err - ); - return; - } - - this.setOutputData(0, this._temp_texture); - }; - - LiteGraph.registerNodeType( - "texture/imageToTexture", - LGraphImageToTexture - ); - - // Texture LUT ***************************************** - function LGraphTextureLUT() { - this.addInput("Texture", "Texture"); - this.addInput("LUT", "Texture"); - this.addInput("Intensity", "number"); - this.addOutput("", "Texture"); - this.properties = { enabled: true, intensity: 1, precision: LGraphTexture.DEFAULT, texture: null }; - - if (!LGraphTextureLUT._shader) { - LGraphTextureLUT._shader = new GL.Shader( Shader.SCREEN_VERTEX_SHADER, LGraphTextureLUT.pixel_shader ); - } - } - - LGraphTextureLUT.widgets_info = { - texture: { widget: "texture" }, - precision: { widget: "combo", values: LGraphTexture.MODE_VALUES } - }; - - LGraphTextureLUT.title = "LUT"; - LGraphTextureLUT.desc = "Apply LUT to Texture"; - - LGraphTextureLUT.prototype.onExecute = function() { - if (!this.isOutputConnected(0)) { - return; - } //saves work - - var tex = this.getInputData(0); - - if (this.properties.precision === LGraphTexture.PASS_THROUGH || this.properties.enabled === false) { - this.setOutputData(0, tex); - return; - } - - if (!tex) { - return; - } - - var lut_tex = this.getInputData(1); - - if (!lut_tex) { - lut_tex = LGraphTexture.getTexture(this.properties.texture); - } - - if (!lut_tex) { - this.setOutputData(0, tex); - return; - } - - lut_tex.bind(0); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); - gl.texParameteri( - gl.TEXTURE_2D, - gl.TEXTURE_WRAP_S, - gl.CLAMP_TO_EDGE - ); - gl.texParameteri( - gl.TEXTURE_2D, - gl.TEXTURE_WRAP_T, - gl.CLAMP_TO_EDGE - ); - gl.bindTexture(gl.TEXTURE_2D, null); - - var intensity = this.properties.intensity; - if (this.isInputConnected(2)) { - this.properties.intensity = intensity = this.getInputData(2); - } - - this._tex = LGraphTexture.getTargetTexture( - tex, - this._tex, - this.properties.precision - ); - - //var mesh = Mesh.getScreenQuad(); - - this._tex.drawTo(function() { - lut_tex.bind(1); - tex.toViewport(LGraphTextureLUT._shader, { - u_texture: 0, - u_textureB: 1, - u_amount: intensity - }); - }); - - this.setOutputData(0, this._tex); - }; - - LGraphTextureLUT.pixel_shader = - "precision highp float;\n\ - precision highp float;\n\ - varying vec2 v_coord;\n\ - uniform sampler2D u_texture;\n\ - uniform sampler2D u_textureB;\n\ - uniform float u_amount;\n\ - \n\ - void main() {\n\ - lowp vec4 textureColor = clamp( texture2D(u_texture, v_coord), vec4(0.0), vec4(1.0) );\n\ - mediump float blueColor = textureColor.b * 63.0;\n\ - mediump vec2 quad1;\n\ - quad1.y = floor(floor(blueColor) / 8.0);\n\ - quad1.x = floor(blueColor) - (quad1.y * 8.0);\n\ - mediump vec2 quad2;\n\ - quad2.y = floor(ceil(blueColor) / 8.0);\n\ - quad2.x = ceil(blueColor) - (quad2.y * 8.0);\n\ - highp vec2 texPos1;\n\ - texPos1.x = (quad1.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.r);\n\ - texPos1.y = 1.0 - ((quad1.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.g));\n\ - highp vec2 texPos2;\n\ - texPos2.x = (quad2.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.r);\n\ - texPos2.y = 1.0 - ((quad2.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.g));\n\ - lowp vec4 newColor1 = texture2D(u_textureB, texPos1);\n\ - lowp vec4 newColor2 = texture2D(u_textureB, texPos2);\n\ - lowp vec4 newColor = mix(newColor1, newColor2, fract(blueColor));\n\ - gl_FragColor = vec4( mix( textureColor.rgb, newColor.rgb, u_amount), textureColor.w);\n\ - }\n\ - "; - - LiteGraph.registerNodeType("texture/LUT", LGraphTextureLUT); - - - // Texture LUT ***************************************** - function LGraphTextureEncode() { - this.addInput("Texture", "Texture"); - this.addInput("Atlas", "Texture"); - this.addOutput("", "Texture"); - this.properties = { enabled: true, num_row_symbols: 4, symbol_size: 16, brightness: 1, colorize: false, filter: false, invert: false, precision: LGraphTexture.DEFAULT, generate_mipmaps: false, texture: null }; - - if (!LGraphTextureEncode._shader) { - LGraphTextureEncode._shader = new GL.Shader( Shader.SCREEN_VERTEX_SHADER, LGraphTextureEncode.pixel_shader ); - } - - this._uniforms = { - u_texture: 0, - u_textureB: 1, - u_row_simbols: 4, - u_simbol_size: 16, - u_res: vec2.create() - }; - } - - LGraphTextureEncode.widgets_info = { - texture: { widget: "texture" }, - precision: { widget: "combo", values: LGraphTexture.MODE_VALUES } - }; - - LGraphTextureEncode.title = "Encode"; - LGraphTextureEncode.desc = "Apply a texture atlas to encode a texture"; - - LGraphTextureEncode.prototype.onExecute = function() { - if (!this.isOutputConnected(0)) { - return; - } //saves work - - var tex = this.getInputData(0); - - if (this.properties.precision === LGraphTexture.PASS_THROUGH || this.properties.enabled === false) { - this.setOutputData(0, tex); - return; - } - - if (!tex) { - return; - } - - var symbols_tex = this.getInputData(1); - - if (!symbols_tex) { - symbols_tex = LGraphTexture.getTexture(this.properties.texture); - } - - if (!symbols_tex) { - this.setOutputData(0, tex); - return; - } - - symbols_tex.bind(0); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, this.properties.filter ? gl.LINEAR : gl.NEAREST ); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, this.properties.filter ? gl.LINEAR : gl.NEAREST ); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE ); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE ); - gl.bindTexture(gl.TEXTURE_2D, null); - - var uniforms = this._uniforms; - uniforms.u_row_simbols = Math.floor(this.properties.num_row_symbols); - uniforms.u_symbol_size = this.properties.symbol_size; - uniforms.u_brightness = this.properties.brightness; - uniforms.u_invert = this.properties.invert ? 1 : 0; - uniforms.u_colorize = this.properties.colorize ? 1 : 0; - - this._tex = LGraphTexture.getTargetTexture( tex, this._tex, this.properties.precision ); - uniforms.u_res[0] = this._tex.width; - uniforms.u_res[1] = this._tex.height; - this._tex.bind(0); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST ); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST ); - - this._tex.drawTo(function() { - symbols_tex.bind(1); - tex.toViewport(LGraphTextureEncode._shader, uniforms); - }); - - if (this.properties.generate_mipmaps) { - this._tex.bind(0); - gl.generateMipmap(this._tex.texture_type); - this._tex.unbind(0); - } - - this.setOutputData(0, this._tex); - }; - - LGraphTextureEncode.pixel_shader = - "precision highp float;\n\ - precision highp float;\n\ - varying vec2 v_coord;\n\ - uniform sampler2D u_texture;\n\ - uniform sampler2D u_textureB;\n\ - uniform float u_row_simbols;\n\ - uniform float u_symbol_size;\n\ - uniform float u_brightness;\n\ - uniform float u_invert;\n\ - uniform float u_colorize;\n\ - uniform vec2 u_res;\n\ - \n\ - void main() {\n\ - vec2 total_symbols = u_res / u_symbol_size;\n\ - vec2 uv = floor(v_coord * total_symbols) / total_symbols; //pixelate \n\ - vec2 local_uv = mod(v_coord * u_res, u_symbol_size) / u_symbol_size;\n\ - lowp vec4 textureColor = texture2D(u_texture, uv );\n\ - float lum = clamp(u_brightness * (textureColor.x + textureColor.y + textureColor.z)/3.0,0.0,1.0);\n\ - if( u_invert == 1.0 ) lum = 1.0 - lum;\n\ - float index = floor( lum * (u_row_simbols * u_row_simbols - 1.0));\n\ - float col = mod( index, u_row_simbols );\n\ - float row = u_row_simbols - floor( index / u_row_simbols ) - 1.0;\n\ - vec2 simbol_uv = ( vec2( col, row ) + local_uv ) / u_row_simbols;\n\ - vec4 color = texture2D( u_textureB, simbol_uv );\n\ - if(u_colorize == 1.0)\n\ - color *= textureColor;\n\ - gl_FragColor = color;\n\ - }\n\ - "; - - LiteGraph.registerNodeType("texture/encode", LGraphTextureEncode); - - // Texture Channels ***************************************** - function LGraphTextureChannels() { - this.addInput("Texture", "Texture"); - - this.addOutput("R", "Texture"); - this.addOutput("G", "Texture"); - this.addOutput("B", "Texture"); - this.addOutput("A", "Texture"); - - //this.properties = { use_single_channel: true }; - if (!LGraphTextureChannels._shader) { - LGraphTextureChannels._shader = new GL.Shader( - Shader.SCREEN_VERTEX_SHADER, - LGraphTextureChannels.pixel_shader - ); - } - } - - LGraphTextureChannels.title = "Texture to Channels"; - LGraphTextureChannels.desc = "Split texture channels"; - - LGraphTextureChannels.prototype.onExecute = function() { - var texA = this.getInputData(0); - if (!texA) { - return; - } - - if (!this._channels) { - this._channels = Array(4); - } - - //var format = this.properties.use_single_channel ? gl.LUMINANCE : gl.RGBA; //not supported by WebGL1 - var format = gl.RGB; - var connections = 0; - for (var i = 0; i < 4; i++) { - if (this.isOutputConnected(i)) { - if ( - !this._channels[i] || - this._channels[i].width != texA.width || - this._channels[i].height != texA.height || - this._channels[i].type != texA.type || - this._channels[i].format != format - ) { - this._channels[i] = new GL.Texture( - texA.width, - texA.height, - { - type: texA.type, - format: format, - filter: gl.LINEAR - } - ); - } - connections++; - } else { - this._channels[i] = null; - } - } - - if (!connections) { - return; - } - - gl.disable(gl.BLEND); - gl.disable(gl.DEPTH_TEST); - - var mesh = Mesh.getScreenQuad(); - var shader = LGraphTextureChannels._shader; - var masks = [ - [1, 0, 0, 0], - [0, 1, 0, 0], - [0, 0, 1, 0], - [0, 0, 0, 1] - ]; - - for (var i = 0; i < 4; i++) { - if (!this._channels[i]) { - continue; - } - - this._channels[i].drawTo(function() { - texA.bind(0); - shader - .uniforms({ u_texture: 0, u_mask: masks[i] }) - .draw(mesh); - }); - this.setOutputData(i, this._channels[i]); - } - }; - - LGraphTextureChannels.pixel_shader = - "precision highp float;\n\ - precision highp float;\n\ - varying vec2 v_coord;\n\ - uniform sampler2D u_texture;\n\ - uniform vec4 u_mask;\n\ - \n\ - void main() {\n\ - gl_FragColor = vec4( vec3( length( texture2D(u_texture, v_coord) * u_mask )), 1.0 );\n\ - }\n\ - "; - - LiteGraph.registerNodeType( - "texture/textureChannels", - LGraphTextureChannels - ); - - // Texture Channels to Texture ***************************************** - function LGraphChannelsTexture() { - this.addInput("R", "Texture"); - this.addInput("G", "Texture"); - this.addInput("B", "Texture"); - this.addInput("A", "Texture"); - - this.addOutput("Texture", "Texture"); - - this.properties = { - precision: LGraphTexture.DEFAULT, - R: 1, - G: 1, - B: 1, - A: 1 - }; - this._color = vec4.create(); - this._uniforms = { - u_textureR: 0, - u_textureG: 1, - u_textureB: 2, - u_textureA: 3, - u_color: this._color - }; - } - - LGraphChannelsTexture.title = "Channels to Texture"; - LGraphChannelsTexture.desc = "Split texture channels"; - LGraphChannelsTexture.widgets_info = { - precision: { widget: "combo", values: LGraphTexture.MODE_VALUES } - }; - - LGraphChannelsTexture.prototype.onExecute = function() { - var white = LGraphTexture.getWhiteTexture(); - var texR = this.getInputData(0) || white; - var texG = this.getInputData(1) || white; - var texB = this.getInputData(2) || white; - var texA = this.getInputData(3) || white; - - gl.disable(gl.BLEND); - gl.disable(gl.DEPTH_TEST); - - var mesh = Mesh.getScreenQuad(); - if (!LGraphChannelsTexture._shader) { - LGraphChannelsTexture._shader = new GL.Shader( - Shader.SCREEN_VERTEX_SHADER, - LGraphChannelsTexture.pixel_shader - ); - } - var shader = LGraphChannelsTexture._shader; - - var w = Math.max(texR.width, texG.width, texB.width, texA.width); - var h = Math.max( - texR.height, - texG.height, - texB.height, - texA.height - ); - var type = - this.properties.precision == LGraphTexture.HIGH - ? LGraphTexture.HIGH_PRECISION_FORMAT - : gl.UNSIGNED_BYTE; - - if ( - !this._texture || - this._texture.width != w || - this._texture.height != h || - this._texture.type != type - ) { - this._texture = new GL.Texture(w, h, { - type: type, - format: gl.RGBA, - filter: gl.LINEAR - }); - } - - var color = this._color; - color[0] = this.properties.R; - color[1] = this.properties.G; - color[2] = this.properties.B; - color[3] = this.properties.A; - var uniforms = this._uniforms; - - this._texture.drawTo(function() { - texR.bind(0); - texG.bind(1); - texB.bind(2); - texA.bind(3); - shader.uniforms(uniforms).draw(mesh); - }); - this.setOutputData(0, this._texture); - }; - - LGraphChannelsTexture.pixel_shader = - "precision highp float;\n\ - precision highp float;\n\ - varying vec2 v_coord;\n\ - uniform sampler2D u_textureR;\n\ - uniform sampler2D u_textureG;\n\ - uniform sampler2D u_textureB;\n\ - uniform sampler2D u_textureA;\n\ - uniform vec4 u_color;\n\ - \n\ - void main() {\n\ - gl_FragColor = u_color * vec4( \ - texture2D(u_textureR, v_coord).r,\ - texture2D(u_textureG, v_coord).r,\ - texture2D(u_textureB, v_coord).r,\ - texture2D(u_textureA, v_coord).r);\n\ - }\n\ - "; - - LiteGraph.registerNodeType( - "texture/channelsTexture", - LGraphChannelsTexture - ); - - // Texture Color ***************************************** - function LGraphTextureColor() { - this.addOutput("Texture", "Texture"); - - this._tex_color = vec4.create(); - this.properties = { - color: vec4.create(), - precision: LGraphTexture.DEFAULT - }; - } - - LGraphTextureColor.title = "Color"; - LGraphTextureColor.desc = - "Generates a 1x1 texture with a constant color"; - - LGraphTextureColor.widgets_info = { - precision: { widget: "combo", values: LGraphTexture.MODE_VALUES } - }; - - LGraphTextureColor.prototype.onDrawBackground = function(ctx) { - var c = this.properties.color; - ctx.fillStyle = - "rgb(" + - Math.floor(clamp(c[0], 0, 1) * 255) + - "," + - Math.floor(clamp(c[1], 0, 1) * 255) + - "," + - Math.floor(clamp(c[2], 0, 1) * 255) + - ")"; - if (this.flags.collapsed) { - this.boxcolor = ctx.fillStyle; - } else { - ctx.fillRect(0, 0, this.size[0], this.size[1]); - } - }; - - LGraphTextureColor.prototype.onExecute = function() { - var type = - this.properties.precision == LGraphTexture.HIGH - ? LGraphTexture.HIGH_PRECISION_FORMAT - : gl.UNSIGNED_BYTE; - - if (!this._tex || this._tex.type != type) { - this._tex = new GL.Texture(1, 1, { - format: gl.RGBA, - type: type, - minFilter: gl.NEAREST - }); - } - var color = this.properties.color; - - if (this.inputs) { - for (var i = 0; i < this.inputs.length; i++) { - var input = this.inputs[i]; - var v = this.getInputData(i); - if (v === undefined) { - continue; - } - switch (input.name) { - case "RGB": - case "RGBA": - color.set(v); - break; - case "R": - color[0] = v; - break; - case "G": - color[1] = v; - break; - case "B": - color[2] = v; - break; - case "A": - color[3] = v; - break; - } - } - } - - if (vec4.sqrDist(this._tex_color, color) > 0.001) { - this._tex_color.set(color); - this._tex.fill(color); - } - this.setOutputData(0, this._tex); - }; - - LGraphTextureColor.prototype.onGetInputs = function() { - return [ - ["RGB", "vec3"], - ["RGBA", "vec4"], - ["R", "number"], - ["G", "number"], - ["B", "number"], - ["A", "number"] - ]; - }; - - LiteGraph.registerNodeType("texture/color", LGraphTextureColor); - - // Texture Channels to Texture ***************************************** - function LGraphTextureGradient() { - this.addInput("A", "color"); - this.addInput("B", "color"); - this.addOutput("Texture", "Texture"); - - this.properties = { - angle: 0, - scale: 1, - A: [0, 0, 0], - B: [1, 1, 1], - texture_size: 32 - }; - if (!LGraphTextureGradient._shader) { - LGraphTextureGradient._shader = new GL.Shader( - Shader.SCREEN_VERTEX_SHADER, - LGraphTextureGradient.pixel_shader - ); - } - - this._uniforms = { - u_angle: 0, - u_colorA: vec3.create(), - u_colorB: vec3.create() - }; - } - - LGraphTextureGradient.title = "Gradient"; - LGraphTextureGradient.desc = "Generates a gradient"; - LGraphTextureGradient["@A"] = { type: "color" }; - LGraphTextureGradient["@B"] = { type: "color" }; - LGraphTextureGradient["@texture_size"] = { - type: "enum", - values: [32, 64, 128, 256, 512] - }; - - LGraphTextureGradient.prototype.onExecute = function() { - gl.disable(gl.BLEND); - gl.disable(gl.DEPTH_TEST); - - var mesh = GL.Mesh.getScreenQuad(); - var shader = LGraphTextureGradient._shader; - - var A = this.getInputData(0); - if (!A) { - A = this.properties.A; - } - var B = this.getInputData(1); - if (!B) { - B = this.properties.B; - } - - //angle and scale - for (var i = 2; i < this.inputs.length; i++) { - var input = this.inputs[i]; - var v = this.getInputData(i); - if (v === undefined) { - continue; - } - this.properties[input.name] = v; - } - - var uniforms = this._uniforms; - this._uniforms.u_angle = this.properties.angle * DEG2RAD; - this._uniforms.u_scale = this.properties.scale; - vec3.copy(uniforms.u_colorA, A); - vec3.copy(uniforms.u_colorB, B); - - var size = parseInt(this.properties.texture_size); - if (!this._tex || this._tex.width != size) { - this._tex = new GL.Texture(size, size, { - format: gl.RGB, - filter: gl.LINEAR - }); - } - - this._tex.drawTo(function() { - shader.uniforms(uniforms).draw(mesh); - }); - this.setOutputData(0, this._tex); - }; - - LGraphTextureGradient.prototype.onGetInputs = function() { - return [["angle", "number"], ["scale", "number"]]; - }; - - LGraphTextureGradient.pixel_shader = - "precision highp float;\n\ - precision highp float;\n\ - varying vec2 v_coord;\n\ - uniform float u_angle;\n\ - uniform float u_scale;\n\ - uniform vec3 u_colorA;\n\ - uniform vec3 u_colorB;\n\ - \n\ - vec2 rotate(vec2 v, float angle)\n\ - {\n\ - vec2 result;\n\ - float _cos = cos(angle);\n\ - float _sin = sin(angle);\n\ - result.x = v.x * _cos - v.y * _sin;\n\ - result.y = v.x * _sin + v.y * _cos;\n\ - return result;\n\ - }\n\ - void main() {\n\ - float f = (rotate(u_scale * (v_coord - vec2(0.5)), u_angle) + vec2(0.5)).x;\n\ - vec3 color = mix(u_colorA,u_colorB,clamp(f,0.0,1.0));\n\ - gl_FragColor = vec4(color,1.0);\n\ - }\n\ - "; - - LiteGraph.registerNodeType("texture/gradient", LGraphTextureGradient); - - // Texture Mix ***************************************** - function LGraphTextureMix() { - this.addInput("A", "Texture"); - this.addInput("B", "Texture"); - this.addInput("Mixer", "Texture"); - - this.addOutput("Texture", "Texture"); - this.properties = { factor: 0.5, size_from_biggest: true, invert: false, precision: LGraphTexture.DEFAULT }; - this._uniforms = { - u_textureA: 0, - u_textureB: 1, - u_textureMix: 2, - u_mix: vec4.create() - }; - } - - LGraphTextureMix.title = "Mix"; - LGraphTextureMix.desc = "Generates a texture mixing two textures"; - - LGraphTextureMix.widgets_info = { - precision: { widget: "combo", values: LGraphTexture.MODE_VALUES } - }; - - LGraphTextureMix.prototype.onExecute = function() { - var texA = this.getInputData(0); - - if (!this.isOutputConnected(0)) { - return; - } //saves work - - if (this.properties.precision === LGraphTexture.PASS_THROUGH) { - this.setOutputData(0, texA); - return; - } - - var texB = this.getInputData(1); - if (!texA || !texB) { - return; - } - - var texMix = this.getInputData(2); - - var factor = this.getInputData(3); - - this._tex = LGraphTexture.getTargetTexture( - this.properties.size_from_biggest && texB.width > texA.width ? texB : texA, - this._tex, - this.properties.precision - ); - - gl.disable(gl.BLEND); - gl.disable(gl.DEPTH_TEST); - - var mesh = Mesh.getScreenQuad(); - var shader = null; - var uniforms = this._uniforms; - if (texMix) { - shader = LGraphTextureMix._shader_tex; - if (!shader) { - shader = LGraphTextureMix._shader_tex = new GL.Shader( - Shader.SCREEN_VERTEX_SHADER, - LGraphTextureMix.pixel_shader, - { MIX_TEX: "" } - ); - } - } else { - shader = LGraphTextureMix._shader_factor; - if (!shader) { - shader = LGraphTextureMix._shader_factor = new GL.Shader( - Shader.SCREEN_VERTEX_SHADER, - LGraphTextureMix.pixel_shader - ); - } - var f = factor == null ? this.properties.factor : factor; - uniforms.u_mix.set([f, f, f, f]); - } - - var invert = this.properties.invert; - - this._tex.drawTo(function() { - texA.bind( invert ? 1 : 0 ); - texB.bind( invert ? 0 : 1 ); - if (texMix) { - texMix.bind(2); - } - shader.uniforms(uniforms).draw(mesh); - }); - - this.setOutputData(0, this._tex); - }; - - LGraphTextureMix.prototype.onGetInputs = function() { - return [["factor", "number"]]; - }; - - LGraphTextureMix.pixel_shader = - "precision highp float;\n\ - precision highp float;\n\ - varying vec2 v_coord;\n\ - uniform sampler2D u_textureA;\n\ - uniform sampler2D u_textureB;\n\ - #ifdef MIX_TEX\n\ - uniform sampler2D u_textureMix;\n\ - #else\n\ - uniform vec4 u_mix;\n\ - #endif\n\ - \n\ - void main() {\n\ - #ifdef MIX_TEX\n\ - vec4 f = texture2D(u_textureMix, v_coord);\n\ - #else\n\ - vec4 f = u_mix;\n\ - #endif\n\ - gl_FragColor = mix( texture2D(u_textureA, v_coord), texture2D(u_textureB, v_coord), f );\n\ - }\n\ - "; - - LiteGraph.registerNodeType("texture/mix", LGraphTextureMix); - - // Texture Edges detection ***************************************** - function LGraphTextureEdges() { - this.addInput("Tex.", "Texture"); - - this.addOutput("Edges", "Texture"); - this.properties = { - invert: true, - threshold: false, - factor: 1, - precision: LGraphTexture.DEFAULT - }; - - if (!LGraphTextureEdges._shader) { - LGraphTextureEdges._shader = new GL.Shader( - Shader.SCREEN_VERTEX_SHADER, - LGraphTextureEdges.pixel_shader - ); - } - } - - LGraphTextureEdges.title = "Edges"; - LGraphTextureEdges.desc = "Detects edges"; - - LGraphTextureEdges.widgets_info = { - precision: { widget: "combo", values: LGraphTexture.MODE_VALUES } - }; - - LGraphTextureEdges.prototype.onExecute = function() { - if (!this.isOutputConnected(0)) { - return; - } //saves work - - var tex = this.getInputData(0); - - if (this.properties.precision === LGraphTexture.PASS_THROUGH) { - this.setOutputData(0, tex); - return; - } - - if (!tex) { - return; - } - - this._tex = LGraphTexture.getTargetTexture( - tex, - this._tex, - this.properties.precision - ); - - gl.disable(gl.BLEND); - gl.disable(gl.DEPTH_TEST); - - var mesh = Mesh.getScreenQuad(); - var shader = LGraphTextureEdges._shader; - var invert = this.properties.invert; - var factor = this.properties.factor; - var threshold = this.properties.threshold ? 1 : 0; - - this._tex.drawTo(function() { - tex.bind(0); - shader - .uniforms({ - u_texture: 0, - u_isize: [1 / tex.width, 1 / tex.height], - u_factor: factor, - u_threshold: threshold, - u_invert: invert ? 1 : 0 - }) - .draw(mesh); - }); - - this.setOutputData(0, this._tex); - }; - - LGraphTextureEdges.pixel_shader = - "precision highp float;\n\ - precision highp float;\n\ - varying vec2 v_coord;\n\ - uniform sampler2D u_texture;\n\ - uniform vec2 u_isize;\n\ - uniform int u_invert;\n\ - uniform float u_factor;\n\ - uniform float u_threshold;\n\ - \n\ - void main() {\n\ - vec4 center = texture2D(u_texture, v_coord);\n\ - vec4 up = texture2D(u_texture, v_coord + u_isize * vec2(0.0,1.0) );\n\ - vec4 down = texture2D(u_texture, v_coord + u_isize * vec2(0.0,-1.0) );\n\ - vec4 left = texture2D(u_texture, v_coord + u_isize * vec2(1.0,0.0) );\n\ - vec4 right = texture2D(u_texture, v_coord + u_isize * vec2(-1.0,0.0) );\n\ - vec4 diff = abs(center - up) + abs(center - down) + abs(center - left) + abs(center - right);\n\ - diff *= u_factor;\n\ - if(u_invert == 1)\n\ - diff.xyz = vec3(1.0) - diff.xyz;\n\ - if( u_threshold == 0.0 )\n\ - gl_FragColor = vec4( diff.xyz, center.a );\n\ - else\n\ - gl_FragColor = vec4( diff.x > 0.5 ? 1.0 : 0.0, diff.y > 0.5 ? 1.0 : 0.0, diff.z > 0.5 ? 1.0 : 0.0, center.a );\n\ - }\n\ - "; - - LiteGraph.registerNodeType("texture/edges", LGraphTextureEdges); - - // Texture Depth ***************************************** - function LGraphTextureDepthRange() { - this.addInput("Texture", "Texture"); - this.addInput("Distance", "number"); - this.addInput("Range", "number"); - this.addOutput("Texture", "Texture"); - this.properties = { - distance: 100, - range: 50, - only_depth: false, - high_precision: false - }; - this._uniforms = { - u_texture: 0, - u_distance: 100, - u_range: 50, - u_camera_planes: null - }; - } - - LGraphTextureDepthRange.title = "Depth Range"; - LGraphTextureDepthRange.desc = "Generates a texture with a depth range"; - - LGraphTextureDepthRange.prototype.onExecute = function() { - if (!this.isOutputConnected(0)) { - return; - } //saves work - - var tex = this.getInputData(0); - if (!tex) { - return; - } - - var precision = gl.UNSIGNED_BYTE; - if (this.properties.high_precision) { - precision = gl.half_float_ext ? gl.HALF_FLOAT_OES : gl.FLOAT; - } - - if ( - !this._temp_texture || - this._temp_texture.type != precision || - this._temp_texture.width != tex.width || - this._temp_texture.height != tex.height - ) { - this._temp_texture = new GL.Texture(tex.width, tex.height, { - type: precision, - format: gl.RGBA, - filter: gl.LINEAR - }); - } - - var uniforms = this._uniforms; - - //iterations - var distance = this.properties.distance; - if (this.isInputConnected(1)) { - distance = this.getInputData(1); - this.properties.distance = distance; - } - - var range = this.properties.range; - if (this.isInputConnected(2)) { - range = this.getInputData(2); - this.properties.range = range; - } - - uniforms.u_distance = distance; - uniforms.u_range = range; - - gl.disable(gl.BLEND); - gl.disable(gl.DEPTH_TEST); - var mesh = Mesh.getScreenQuad(); - if (!LGraphTextureDepthRange._shader) { - LGraphTextureDepthRange._shader = new GL.Shader( - Shader.SCREEN_VERTEX_SHADER, - LGraphTextureDepthRange.pixel_shader - ); - LGraphTextureDepthRange._shader_onlydepth = new GL.Shader( - Shader.SCREEN_VERTEX_SHADER, - LGraphTextureDepthRange.pixel_shader, - { ONLY_DEPTH: "" } - ); - } - var shader = this.properties.only_depth - ? LGraphTextureDepthRange._shader_onlydepth - : LGraphTextureDepthRange._shader; - - //NEAR AND FAR PLANES - var planes = null; - if (tex.near_far_planes) { - planes = tex.near_far_planes; - } else if (window.LS && LS.Renderer._main_camera) { - planes = LS.Renderer._main_camera._uniforms.u_camera_planes; - } else { - planes = [0.1, 1000]; - } //hardcoded - uniforms.u_camera_planes = planes; - - this._temp_texture.drawTo(function() { - tex.bind(0); - shader.uniforms(uniforms).draw(mesh); - }); - - this._temp_texture.near_far_planes = planes; - this.setOutputData(0, this._temp_texture); - }; - - LGraphTextureDepthRange.pixel_shader = - "precision highp float;\n\ - precision highp float;\n\ - varying vec2 v_coord;\n\ - uniform sampler2D u_texture;\n\ - uniform vec2 u_camera_planes;\n\ - uniform float u_distance;\n\ - uniform float u_range;\n\ - \n\ - float LinearDepth()\n\ - {\n\ - float zNear = u_camera_planes.x;\n\ - float zFar = u_camera_planes.y;\n\ - float depth = texture2D(u_texture, v_coord).x;\n\ - depth = depth * 2.0 - 1.0;\n\ - return zNear * (depth + 1.0) / (zFar + zNear - depth * (zFar - zNear));\n\ - }\n\ - \n\ - void main() {\n\ - float depth = LinearDepth();\n\ - #ifdef ONLY_DEPTH\n\ - gl_FragColor = vec4(depth);\n\ - #else\n\ - float diff = abs(depth * u_camera_planes.y - u_distance);\n\ - float dof = 1.0;\n\ - if(diff <= u_range)\n\ - dof = diff / u_range;\n\ - gl_FragColor = vec4(dof);\n\ - #endif\n\ - }\n\ - "; - - LiteGraph.registerNodeType( "texture/depth_range", LGraphTextureDepthRange ); - - - // Texture Depth ***************************************** - function LGraphTextureLinearDepth() { - this.addInput("Texture", "Texture"); - this.addOutput("Texture", "Texture"); - this.properties = { - precision: LGraphTexture.DEFAULT, - invert: false - }; - this._uniforms = { - u_texture: 0, - u_camera_planes: null, //filled later - u_ires: vec2.create() - }; - } - - LGraphTextureLinearDepth.widgets_info = { - precision: { widget: "combo", values: LGraphTexture.MODE_VALUES } - }; - - LGraphTextureLinearDepth.title = "Linear Depth"; - LGraphTextureLinearDepth.desc = "Creates a color texture with linear depth"; - - LGraphTextureLinearDepth.prototype.onExecute = function() { - if (!this.isOutputConnected(0)) { - return; - } //saves work - - var tex = this.getInputData(0); - if (!tex || (tex.format != gl.DEPTH_COMPONENT && tex.format != gl.DEPTH_STENCIL) ) { - return; - } - - var precision = this.properties.precision == LGraphTexture.HIGH ? gl.HIGH_PRECISION_FORMAT : gl.UNSIGNED_BYTE; - - if ( !this._temp_texture || this._temp_texture.type != precision || this._temp_texture.width != tex.width || this._temp_texture.height != tex.height ) { - this._temp_texture = new GL.Texture(tex.width, tex.height, { - type: precision, - format: gl.RGB, - filter: gl.LINEAR - }); - } - - var uniforms = this._uniforms; - uniforms.u_invert = this.properties.invert ? 1 : 0; - - gl.disable(gl.BLEND); - gl.disable(gl.DEPTH_TEST); - var mesh = Mesh.getScreenQuad(); - if(!LGraphTextureLinearDepth._shader) - LGraphTextureLinearDepth._shader = new GL.Shader( GL.Shader.SCREEN_VERTEX_SHADER, LGraphTextureLinearDepth.pixel_shader); - var shader = LGraphTextureLinearDepth._shader; - - //NEAR AND FAR PLANES - var planes = null; - if (tex.near_far_planes) { - planes = tex.near_far_planes; - } else if (window.LS && LS.Renderer._main_camera) { - planes = LS.Renderer._main_camera._uniforms.u_camera_planes; - } else { - planes = [0.1, 1000]; - } //hardcoded - uniforms.u_camera_planes = planes; - //uniforms.u_ires.set([1/tex.width, 1/tex.height]); - uniforms.u_ires.set([0,0]); - - this._temp_texture.drawTo(function() { - tex.bind(0); - shader.uniforms(uniforms).draw(mesh); - }); - - this._temp_texture.near_far_planes = planes; - this.setOutputData(0, this._temp_texture); - }; - - LGraphTextureLinearDepth.pixel_shader = - "precision highp float;\n\ - precision highp float;\n\ - varying vec2 v_coord;\n\ - uniform sampler2D u_texture;\n\ - uniform vec2 u_camera_planes;\n\ - uniform int u_invert;\n\ - uniform vec2 u_ires;\n\ - \n\ - void main() {\n\ - float zNear = u_camera_planes.x;\n\ - float zFar = u_camera_planes.y;\n\ - float depth = texture2D(u_texture, v_coord + u_ires*0.5).x * 2.0 - 1.0;\n\ - float f = zNear * (depth + 1.0) / (zFar + zNear - depth * (zFar - zNear));\n\ - if( u_invert == 1 )\n\ - f = 1.0 - f;\n\ - gl_FragColor = vec4(vec3(f),1.0);\n\ - }\n\ - "; - - LiteGraph.registerNodeType( "texture/linear_depth", LGraphTextureLinearDepth ); - - // Texture Blur ***************************************** - function LGraphTextureBlur() { - this.addInput("Texture", "Texture"); - this.addInput("Iterations", "number"); - this.addInput("Intensity", "number"); - this.addOutput("Blurred", "Texture"); - this.properties = { - intensity: 1, - iterations: 1, - preserve_aspect: false, - scale: [1, 1], - precision: LGraphTexture.DEFAULT - }; - } - - LGraphTextureBlur.title = "Blur"; - LGraphTextureBlur.desc = "Blur a texture"; - - LGraphTextureBlur.widgets_info = { - precision: { widget: "combo", values: LGraphTexture.MODE_VALUES } - }; - - LGraphTextureBlur.max_iterations = 20; - - LGraphTextureBlur.prototype.onExecute = function() { - var tex = this.getInputData(0); - if (!tex) { - return; - } - - if (!this.isOutputConnected(0)) { - return; - } //saves work - - var temp = this._final_texture; - - if ( - !temp || - temp.width != tex.width || - temp.height != tex.height || - temp.type != tex.type - ) { - //we need two textures to do the blurring - //this._temp_texture = new GL.Texture( tex.width, tex.height, { type: tex.type, format: gl.RGBA, filter: gl.LINEAR }); - temp = this._final_texture = new GL.Texture( - tex.width, - tex.height, - { type: tex.type, format: gl.RGBA, filter: gl.LINEAR } - ); - } - - //iterations - var iterations = this.properties.iterations; - if (this.isInputConnected(1)) { - iterations = this.getInputData(1); - this.properties.iterations = iterations; - } - iterations = Math.min( - Math.floor(iterations), - LGraphTextureBlur.max_iterations - ); - if (iterations == 0) { - //skip blurring - this.setOutputData(0, tex); - return; - } - - var intensity = this.properties.intensity; - if (this.isInputConnected(2)) { - intensity = this.getInputData(2); - this.properties.intensity = intensity; - } - - //blur sometimes needs an aspect correction - var aspect = LiteGraph.camera_aspect; - if (!aspect && window.gl !== undefined) { - aspect = gl.canvas.height / gl.canvas.width; - } - if (!aspect) { - aspect = 1; - } - aspect = this.properties.preserve_aspect ? aspect : 1; - - var scale = this.properties.scale || [1, 1]; - tex.applyBlur(aspect * scale[0], scale[1], intensity, temp); - for (var i = 1; i < iterations; ++i) { - temp.applyBlur( - aspect * scale[0] * (i + 1), - scale[1] * (i + 1), - intensity - ); - } - - this.setOutputData(0, temp); - }; - - /* -LGraphTextureBlur.pixel_shader = "precision highp float;\n\ - precision highp float;\n\ - varying vec2 v_coord;\n\ - uniform sampler2D u_texture;\n\ - uniform vec2 u_offset;\n\ - uniform float u_intensity;\n\ - void main() {\n\ - vec4 sum = vec4(0.0);\n\ - vec4 center = texture2D(u_texture, v_coord);\n\ - sum += texture2D(u_texture, v_coord + u_offset * -4.0) * 0.05/0.98;\n\ - sum += texture2D(u_texture, v_coord + u_offset * -3.0) * 0.09/0.98;\n\ - sum += texture2D(u_texture, v_coord + u_offset * -2.0) * 0.12/0.98;\n\ - sum += texture2D(u_texture, v_coord + u_offset * -1.0) * 0.15/0.98;\n\ - sum += center * 0.16/0.98;\n\ - sum += texture2D(u_texture, v_coord + u_offset * 4.0) * 0.05/0.98;\n\ - sum += texture2D(u_texture, v_coord + u_offset * 3.0) * 0.09/0.98;\n\ - sum += texture2D(u_texture, v_coord + u_offset * 2.0) * 0.12/0.98;\n\ - sum += texture2D(u_texture, v_coord + u_offset * 1.0) * 0.15/0.98;\n\ - gl_FragColor = u_intensity * sum;\n\ - }\n\ - "; -*/ - - LiteGraph.registerNodeType("texture/blur", LGraphTextureBlur); - - //Independent glow FX - //based on https://catlikecoding.com/unity/tutorials/advanced-rendering/bloom/ - function FXGlow() - { - this.intensity = 0.5; - this.persistence = 0.6; - this.iterations = 8; - this.threshold = 0.8; - this.scale = 1; - - this.dirt_texture = null; - this.dirt_factor = 0.5; - - this._textures = []; - this._uniforms = { - u_intensity: 1, - u_texture: 0, - u_glow_texture: 1, - u_threshold: 0, - u_texel_size: vec2.create() - }; - } - - FXGlow.prototype.applyFX = function( tex, output_texture, glow_texture, average_texture ) { - - var width = tex.width; - var height = tex.height; - - var texture_info = { - format: tex.format, - type: tex.type, - minFilter: GL.LINEAR, - magFilter: GL.LINEAR, - wrap: gl.CLAMP_TO_EDGE - }; - - var uniforms = this._uniforms; - var textures = this._textures; - - //cut - var shader = FXGlow._cut_shader; - if (!shader) { - shader = FXGlow._cut_shader = new GL.Shader( - GL.Shader.SCREEN_VERTEX_SHADER, - FXGlow.cut_pixel_shader - ); - } - - gl.disable(gl.DEPTH_TEST); - gl.disable(gl.BLEND); - - uniforms.u_threshold = this.threshold; - var currentDestination = (textures[0] = GL.Texture.getTemporary( - width, - height, - texture_info - )); - tex.blit( currentDestination, shader.uniforms(uniforms) ); - var currentSource = currentDestination; - - var iterations = this.iterations; - iterations = clamp(iterations, 1, 16) | 0; - var texel_size = uniforms.u_texel_size; - var intensity = this.intensity; - - uniforms.u_intensity = 1; - uniforms.u_delta = this.scale; //1 - - //downscale/upscale shader - var shader = FXGlow._shader; - if (!shader) { - shader = FXGlow._shader = new GL.Shader( - GL.Shader.SCREEN_VERTEX_SHADER, - FXGlow.scale_pixel_shader - ); - } - - var i = 1; - //downscale - for (; i < iterations; i++) { - width = width >> 1; - if ((height | 0) > 1) { - height = height >> 1; - } - if (width < 2) { - break; - } - currentDestination = textures[i] = GL.Texture.getTemporary( - width, - height, - texture_info - ); - texel_size[0] = 1 / currentSource.width; - texel_size[1] = 1 / currentSource.height; - currentSource.blit( - currentDestination, - shader.uniforms(uniforms) - ); - currentSource = currentDestination; - } - - //average - if (average_texture) { - texel_size[0] = 1 / currentSource.width; - texel_size[1] = 1 / currentSource.height; - uniforms.u_intensity = intensity; - uniforms.u_delta = 1; - currentSource.blit(average_texture, shader.uniforms(uniforms)); - } - - //upscale and blend - gl.enable(gl.BLEND); - gl.blendFunc(gl.ONE, gl.ONE); - uniforms.u_intensity = this.persistence; - uniforms.u_delta = 0.5; - - // i-=2 => -1 to point to last element in array, -1 to go to texture above - for ( i -= 2; i >= 0; i-- ) - { - currentDestination = textures[i]; - textures[i] = null; - texel_size[0] = 1 / currentSource.width; - texel_size[1] = 1 / currentSource.height; - currentSource.blit( - currentDestination, - shader.uniforms(uniforms) - ); - GL.Texture.releaseTemporary(currentSource); - currentSource = currentDestination; - } - gl.disable(gl.BLEND); - - //glow - if (glow_texture) { - currentSource.blit(glow_texture); - } - - //final composition - if ( output_texture ) { - var final_texture = output_texture; - var dirt_texture = this.dirt_texture; - var dirt_factor = this.dirt_factor; - uniforms.u_intensity = intensity; - - shader = dirt_texture - ? FXGlow._dirt_final_shader - : FXGlow._final_shader; - if (!shader) { - if (dirt_texture) { - shader = FXGlow._dirt_final_shader = new GL.Shader( - GL.Shader.SCREEN_VERTEX_SHADER, - FXGlow.final_pixel_shader, - { USE_DIRT: "" } - ); - } else { - shader = FXGlow._final_shader = new GL.Shader( - GL.Shader.SCREEN_VERTEX_SHADER, - FXGlow.final_pixel_shader - ); - } - } - - final_texture.drawTo(function() { - tex.bind(0); - currentSource.bind(1); - if (dirt_texture) { - shader.setUniform("u_dirt_factor", dirt_factor); - shader.setUniform( - "u_dirt_texture", - dirt_texture.bind(2) - ); - } - shader.toViewport(uniforms); - }); - } - - GL.Texture.releaseTemporary(currentSource); - }; - - FXGlow.cut_pixel_shader = - "precision highp float;\n\ - varying vec2 v_coord;\n\ - uniform sampler2D u_texture;\n\ - uniform float u_threshold;\n\ - void main() {\n\ - gl_FragColor = max( texture2D( u_texture, v_coord ) - vec4( u_threshold ), vec4(0.0) );\n\ - }"; - - FXGlow.scale_pixel_shader = - "precision highp float;\n\ - varying vec2 v_coord;\n\ - uniform sampler2D u_texture;\n\ - uniform vec2 u_texel_size;\n\ - uniform float u_delta;\n\ - uniform float u_intensity;\n\ - \n\ - vec4 sampleBox(vec2 uv) {\n\ - vec4 o = u_texel_size.xyxy * vec2(-u_delta, u_delta).xxyy;\n\ - vec4 s = texture2D( u_texture, uv + o.xy ) + texture2D( u_texture, uv + o.zy) + texture2D( u_texture, uv + o.xw) + texture2D( u_texture, uv + o.zw);\n\ - return s * 0.25;\n\ - }\n\ - void main() {\n\ - gl_FragColor = u_intensity * sampleBox( v_coord );\n\ - }"; - - FXGlow.final_pixel_shader = - "precision highp float;\n\ - varying vec2 v_coord;\n\ - uniform sampler2D u_texture;\n\ - uniform sampler2D u_glow_texture;\n\ - #ifdef USE_DIRT\n\ - uniform sampler2D u_dirt_texture;\n\ - #endif\n\ - uniform vec2 u_texel_size;\n\ - uniform float u_delta;\n\ - uniform float u_intensity;\n\ - uniform float u_dirt_factor;\n\ - \n\ - vec4 sampleBox(vec2 uv) {\n\ - vec4 o = u_texel_size.xyxy * vec2(-u_delta, u_delta).xxyy;\n\ - vec4 s = texture2D( u_glow_texture, uv + o.xy ) + texture2D( u_glow_texture, uv + o.zy) + texture2D( u_glow_texture, uv + o.xw) + texture2D( u_glow_texture, uv + o.zw);\n\ - return s * 0.25;\n\ - }\n\ - void main() {\n\ - vec4 glow = sampleBox( v_coord );\n\ - #ifdef USE_DIRT\n\ - glow = mix( glow, glow * texture2D( u_dirt_texture, v_coord ), u_dirt_factor );\n\ - #endif\n\ - gl_FragColor = texture2D( u_texture, v_coord ) + u_intensity * glow;\n\ - }"; - - - // Texture Glow ***************************************** - function LGraphTextureGlow() { - this.addInput("in", "Texture"); - this.addInput("dirt", "Texture"); - this.addOutput("out", "Texture"); - this.addOutput("glow", "Texture"); - this.properties = { - enabled: true, - intensity: 1, - persistence: 0.99, - iterations: 16, - threshold: 0, - scale: 1, - dirt_factor: 0.5, - precision: LGraphTexture.DEFAULT - }; - - this.fx = new FXGlow(); - } - - LGraphTextureGlow.title = "Glow"; - LGraphTextureGlow.desc = "Filters a texture giving it a glow effect"; - - LGraphTextureGlow.widgets_info = { - iterations: { - type: "number", - min: 0, - max: 16, - step: 1, - precision: 0 - }, - threshold: { - type: "number", - min: 0, - max: 10, - step: 0.01, - precision: 2 - }, - precision: { widget: "combo", values: LGraphTexture.MODE_VALUES } - }; - - LGraphTextureGlow.prototype.onGetInputs = function() { - return [ - ["enabled", "boolean"], - ["threshold", "number"], - ["intensity", "number"], - ["persistence", "number"], - ["iterations", "number"], - ["dirt_factor", "number"] - ]; - }; - - LGraphTextureGlow.prototype.onGetOutputs = function() { - return [["average", "Texture"]]; - }; - - LGraphTextureGlow.prototype.onExecute = function() { - var tex = this.getInputData(0); - if (!tex) { - return; - } - - if (!this.isAnyOutputConnected()) { - return; - } //saves work - - if ( - this.properties.precision === LGraphTexture.PASS_THROUGH || - this.getInputOrProperty("enabled") === false - ) { - this.setOutputData(0, tex); - return; - } - - var width = tex.width; - var height = tex.height; - - var fx = this.fx; - fx.threshold = this.getInputOrProperty("threshold"); - fx.iterations = this.getInputOrProperty("iterations"); - fx.intensity = this.getInputOrProperty("intensity"); - fx.persistence = this.getInputOrProperty("persistence"); - fx.dirt_texture = this.getInputData(1); - fx.dirt_factor = this.getInputOrProperty("dirt_factor"); - fx.scale = this.properties.scale; - - var type = LGraphTexture.getTextureType( this.properties.precision, tex ); - - var average_texture = null; - if (this.isOutputConnected(2)) { - average_texture = this._average_texture; - if ( - !average_texture || - average_texture.type != tex.type || - average_texture.format != tex.format - ) { - average_texture = this._average_texture = new GL.Texture( - 1, - 1, - { - type: tex.type, - format: tex.format, - filter: gl.LINEAR - } - ); - } - } - - var glow_texture = null; - if (this.isOutputConnected(1)) { - glow_texture = this._glow_texture; - if ( - !glow_texture || - glow_texture.width != tex.width || - glow_texture.height != tex.height || - glow_texture.type != type || - glow_texture.format != tex.format - ) { - glow_texture = this._glow_texture = new GL.Texture( - tex.width, - tex.height, - { type: type, format: tex.format, filter: gl.LINEAR } - ); - } - } - - var final_texture = null; - if (this.isOutputConnected(0)) { - final_texture = this._final_texture; - if ( - !final_texture || - final_texture.width != tex.width || - final_texture.height != tex.height || - final_texture.type != type || - final_texture.format != tex.format - ) { - final_texture = this._final_texture = new GL.Texture( - tex.width, - tex.height, - { type: type, format: tex.format, filter: gl.LINEAR } - ); - } - - } - - //apply FX - fx.applyFX(tex, final_texture, glow_texture, average_texture ); - - if (this.isOutputConnected(0)) - this.setOutputData(0, final_texture); - - if (this.isOutputConnected(1)) - this.setOutputData(1, average_texture); - - if (this.isOutputConnected(2)) - this.setOutputData(2, glow_texture); - }; - - LiteGraph.registerNodeType("texture/glow", LGraphTextureGlow); - - // Texture Filter ***************************************** - function LGraphTextureKuwaharaFilter() { - this.addInput("Texture", "Texture"); - this.addOutput("Filtered", "Texture"); - this.properties = { intensity: 1, radius: 5 }; - } - - LGraphTextureKuwaharaFilter.title = "Kuwahara Filter"; - LGraphTextureKuwaharaFilter.desc = - "Filters a texture giving an artistic oil canvas painting"; - - LGraphTextureKuwaharaFilter.max_radius = 10; - LGraphTextureKuwaharaFilter._shaders = []; - - LGraphTextureKuwaharaFilter.prototype.onExecute = function() { - var tex = this.getInputData(0); - if (!tex) { - return; - } - - if (!this.isOutputConnected(0)) { - return; - } //saves work - - var temp = this._temp_texture; - - if ( - !temp || - temp.width != tex.width || - temp.height != tex.height || - temp.type != tex.type - ) { - this._temp_texture = new GL.Texture(tex.width, tex.height, { - type: tex.type, - format: gl.RGBA, - filter: gl.LINEAR - }); - } - - //iterations - var radius = this.properties.radius; - radius = Math.min( - Math.floor(radius), - LGraphTextureKuwaharaFilter.max_radius - ); - if (radius == 0) { - //skip blurring - this.setOutputData(0, tex); - return; - } - - var intensity = this.properties.intensity; - - //blur sometimes needs an aspect correction - var aspect = LiteGraph.camera_aspect; - if (!aspect && window.gl !== undefined) { - aspect = gl.canvas.height / gl.canvas.width; - } - if (!aspect) { - aspect = 1; - } - aspect = this.properties.preserve_aspect ? aspect : 1; - - if (!LGraphTextureKuwaharaFilter._shaders[radius]) { - LGraphTextureKuwaharaFilter._shaders[radius] = new GL.Shader( - Shader.SCREEN_VERTEX_SHADER, - LGraphTextureKuwaharaFilter.pixel_shader, - { RADIUS: radius.toFixed(0) } - ); - } - - var shader = LGraphTextureKuwaharaFilter._shaders[radius]; - var mesh = GL.Mesh.getScreenQuad(); - tex.bind(0); - - this._temp_texture.drawTo(function() { - shader - .uniforms({ - u_texture: 0, - u_intensity: intensity, - u_resolution: [tex.width, tex.height], - u_iResolution: [1 / tex.width, 1 / tex.height] - }) - .draw(mesh); - }); - - this.setOutputData(0, this._temp_texture); - }; - - //from https://www.shadertoy.com/view/MsXSz4 - LGraphTextureKuwaharaFilter.pixel_shader = - "\n\ -precision highp float;\n\ -varying vec2 v_coord;\n\ -uniform sampler2D u_texture;\n\ -uniform float u_intensity;\n\ -uniform vec2 u_resolution;\n\ -uniform vec2 u_iResolution;\n\ -#ifndef RADIUS\n\ - #define RADIUS 7\n\ -#endif\n\ -void main() {\n\ -\n\ - const int radius = RADIUS;\n\ - vec2 fragCoord = v_coord;\n\ - vec2 src_size = u_iResolution;\n\ - vec2 uv = v_coord;\n\ - float n = float((radius + 1) * (radius + 1));\n\ - int i;\n\ - int j;\n\ - vec3 m0 = vec3(0.0); vec3 m1 = vec3(0.0); vec3 m2 = vec3(0.0); vec3 m3 = vec3(0.0);\n\ - vec3 s0 = vec3(0.0); vec3 s1 = vec3(0.0); vec3 s2 = vec3(0.0); vec3 s3 = vec3(0.0);\n\ - vec3 c;\n\ - \n\ - for (int j = -radius; j <= 0; ++j) {\n\ - for (int i = -radius; i <= 0; ++i) {\n\ - c = texture2D(u_texture, uv + vec2(i,j) * src_size).rgb;\n\ - m0 += c;\n\ - s0 += c * c;\n\ - }\n\ - }\n\ - \n\ - for (int j = -radius; j <= 0; ++j) {\n\ - for (int i = 0; i <= radius; ++i) {\n\ - c = texture2D(u_texture, uv + vec2(i,j) * src_size).rgb;\n\ - m1 += c;\n\ - s1 += c * c;\n\ - }\n\ - }\n\ - \n\ - for (int j = 0; j <= radius; ++j) {\n\ - for (int i = 0; i <= radius; ++i) {\n\ - c = texture2D(u_texture, uv + vec2(i,j) * src_size).rgb;\n\ - m2 += c;\n\ - s2 += c * c;\n\ - }\n\ - }\n\ - \n\ - for (int j = 0; j <= radius; ++j) {\n\ - for (int i = -radius; i <= 0; ++i) {\n\ - c = texture2D(u_texture, uv + vec2(i,j) * src_size).rgb;\n\ - m3 += c;\n\ - s3 += c * c;\n\ - }\n\ - }\n\ - \n\ - float min_sigma2 = 1e+2;\n\ - m0 /= n;\n\ - s0 = abs(s0 / n - m0 * m0);\n\ - \n\ - float sigma2 = s0.r + s0.g + s0.b;\n\ - if (sigma2 < min_sigma2) {\n\ - min_sigma2 = sigma2;\n\ - gl_FragColor = vec4(m0, 1.0);\n\ - }\n\ - \n\ - m1 /= n;\n\ - s1 = abs(s1 / n - m1 * m1);\n\ - \n\ - sigma2 = s1.r + s1.g + s1.b;\n\ - if (sigma2 < min_sigma2) {\n\ - min_sigma2 = sigma2;\n\ - gl_FragColor = vec4(m1, 1.0);\n\ - }\n\ - \n\ - m2 /= n;\n\ - s2 = abs(s2 / n - m2 * m2);\n\ - \n\ - sigma2 = s2.r + s2.g + s2.b;\n\ - if (sigma2 < min_sigma2) {\n\ - min_sigma2 = sigma2;\n\ - gl_FragColor = vec4(m2, 1.0);\n\ - }\n\ - \n\ - m3 /= n;\n\ - s3 = abs(s3 / n - m3 * m3);\n\ - \n\ - sigma2 = s3.r + s3.g + s3.b;\n\ - if (sigma2 < min_sigma2) {\n\ - min_sigma2 = sigma2;\n\ - gl_FragColor = vec4(m3, 1.0);\n\ - }\n\ -}\n\ -"; - - LiteGraph.registerNodeType( - "texture/kuwahara", - LGraphTextureKuwaharaFilter - ); - - // Texture ***************************************** - function LGraphTextureXDoGFilter() { - this.addInput("Texture", "Texture"); - this.addOutput("Filtered", "Texture"); - this.properties = { - sigma: 1.4, - k: 1.6, - p: 21.7, - epsilon: 79, - phi: 0.017 - }; - } - - LGraphTextureXDoGFilter.title = "XDoG Filter"; - LGraphTextureXDoGFilter.desc = - "Filters a texture giving an artistic ink style"; - - LGraphTextureXDoGFilter.max_radius = 10; - LGraphTextureXDoGFilter._shaders = []; - - LGraphTextureXDoGFilter.prototype.onExecute = function() { - var tex = this.getInputData(0); - if (!tex) { - return; - } - - if (!this.isOutputConnected(0)) { - return; - } //saves work - - var temp = this._temp_texture; - if ( - !temp || - temp.width != tex.width || - temp.height != tex.height || - temp.type != tex.type - ) { - this._temp_texture = new GL.Texture(tex.width, tex.height, { - type: tex.type, - format: gl.RGBA, - filter: gl.LINEAR - }); - } - - if (!LGraphTextureXDoGFilter._xdog_shader) { - LGraphTextureXDoGFilter._xdog_shader = new GL.Shader( - Shader.SCREEN_VERTEX_SHADER, - LGraphTextureXDoGFilter.xdog_pixel_shader - ); - } - var shader = LGraphTextureXDoGFilter._xdog_shader; - var mesh = GL.Mesh.getScreenQuad(); - - var sigma = this.properties.sigma; - var k = this.properties.k; - var p = this.properties.p; - var epsilon = this.properties.epsilon; - var phi = this.properties.phi; - tex.bind(0); - this._temp_texture.drawTo(function() { - shader - .uniforms({ - src: 0, - sigma: sigma, - k: k, - p: p, - epsilon: epsilon, - phi: phi, - cvsWidth: tex.width, - cvsHeight: tex.height - }) - .draw(mesh); - }); - - this.setOutputData(0, this._temp_texture); - }; - - //from https://github.com/RaymondMcGuire/GPU-Based-Image-Processing-Tools/blob/master/lib_webgl/scripts/main.js - LGraphTextureXDoGFilter.xdog_pixel_shader = - "\n\ -precision highp float;\n\ -uniform sampler2D src;\n\n\ -uniform float cvsHeight;\n\ -uniform float cvsWidth;\n\n\ -uniform float sigma;\n\ -uniform float k;\n\ -uniform float p;\n\ -uniform float epsilon;\n\ -uniform float phi;\n\ -varying vec2 v_coord;\n\n\ -float cosh(float val)\n\ -{\n\ - float tmp = exp(val);\n\ - float cosH = (tmp + 1.0 / tmp) / 2.0;\n\ - return cosH;\n\ -}\n\n\ -float tanh(float val)\n\ -{\n\ - float tmp = exp(val);\n\ - float tanH = (tmp - 1.0 / tmp) / (tmp + 1.0 / tmp);\n\ - return tanH;\n\ -}\n\n\ -float sinh(float val)\n\ -{\n\ - float tmp = exp(val);\n\ - float sinH = (tmp - 1.0 / tmp) / 2.0;\n\ - return sinH;\n\ -}\n\n\ -void main(void){\n\ - vec3 destColor = vec3(0.0);\n\ - float tFrag = 1.0 / cvsHeight;\n\ - float sFrag = 1.0 / cvsWidth;\n\ - vec2 Frag = vec2(sFrag,tFrag);\n\ - vec2 uv = gl_FragCoord.st;\n\ - float twoSigmaESquared = 2.0 * sigma * sigma;\n\ - float twoSigmaRSquared = twoSigmaESquared * k * k;\n\ - int halfWidth = int(ceil( 1.0 * sigma * k ));\n\n\ - const int MAX_NUM_ITERATION = 99999;\n\ - vec2 sum = vec2(0.0);\n\ - vec2 norm = vec2(0.0);\n\n\ - for(int cnt=0;cnt (2*halfWidth+1)*(2*halfWidth+1)){break;}\n\ - int i = int(cnt / (2*halfWidth+1)) - halfWidth;\n\ - int j = cnt - halfWidth - int(cnt / (2*halfWidth+1)) * (2*halfWidth+1);\n\n\ - float d = length(vec2(i,j));\n\ - vec2 kernel = vec2( exp( -d * d / twoSigmaESquared ), \n\ - exp( -d * d / twoSigmaRSquared ));\n\n\ - vec2 L = texture2D(src, (uv + vec2(i,j)) * Frag).xx;\n\n\ - norm += kernel;\n\ - sum += kernel * L;\n\ - }\n\n\ - sum /= norm;\n\n\ - float H = 100.0 * ((1.0 + p) * sum.x - p * sum.y);\n\ - float edge = ( H > epsilon )? 1.0 : 1.0 + tanh( phi * (H - epsilon));\n\ - destColor = vec3(edge);\n\ - gl_FragColor = vec4(destColor, 1.0);\n\ -}"; - - LiteGraph.registerNodeType("texture/xDoG", LGraphTextureXDoGFilter); - - // Texture Webcam ***************************************** - function LGraphTextureWebcam() { - this.addOutput("Webcam", "Texture"); - this.properties = { texture_name: "", facingMode: "user" }; - this.boxcolor = "black"; - this.version = 0; - } - - LGraphTextureWebcam.title = "Webcam"; - LGraphTextureWebcam.desc = "Webcam texture"; - - LGraphTextureWebcam.is_webcam_open = false; - - LGraphTextureWebcam.prototype.openStream = function() { - if (!navigator.getUserMedia) { - //console.log('getUserMedia() is not supported in your browser, use chrome and enable WebRTC from about://flags'); - return; - } - - this._waiting_confirmation = true; - - // Not showing vendor prefixes. - var constraints = { - audio: false, - video: { facingMode: this.properties.facingMode } - }; - navigator.mediaDevices - .getUserMedia(constraints) - .then(this.streamReady.bind(this)) - .catch(onFailSoHard); - - var that = this; - function onFailSoHard(e) { - LGraphTextureWebcam.is_webcam_open = false; - console.log("Webcam rejected", e); - that._webcam_stream = false; - that.boxcolor = "red"; - that.trigger("stream_error"); - } - }; - - LGraphTextureWebcam.prototype.closeStream = function() { - if (this._webcam_stream) { - var tracks = this._webcam_stream.getTracks(); - if (tracks.length) { - for (var i = 0; i < tracks.length; ++i) { - tracks[i].stop(); - } - } - LGraphTextureWebcam.is_webcam_open = false; - this._webcam_stream = null; - this._video = null; - this.boxcolor = "black"; - this.trigger("stream_closed"); - } - }; - - LGraphTextureWebcam.prototype.streamReady = function(localMediaStream) { - this._webcam_stream = localMediaStream; - //this._waiting_confirmation = false; - this.boxcolor = "green"; - var video = this._video; - if (!video) { - video = document.createElement("video"); - video.autoplay = true; - video.srcObject = localMediaStream; - this._video = video; - //document.body.appendChild( video ); //debug - //when video info is loaded (size and so) - video.onloadedmetadata = function(e) { - // Ready to go. Do some stuff. - LGraphTextureWebcam.is_webcam_open = true; - console.log(e); - }; - } - this.trigger("stream_ready", video); - }; - - LGraphTextureWebcam.prototype.onPropertyChanged = function( - name, - value - ) { - if (name == "facingMode") { - this.properties.facingMode = value; - this.closeStream(); - this.openStream(); - } - }; - - LGraphTextureWebcam.prototype.onRemoved = function() { - if (!this._webcam_stream) { - return; - } - - var tracks = this._webcam_stream.getTracks(); - if (tracks.length) { - for (var i = 0; i < tracks.length; ++i) { - tracks[i].stop(); - } - } - - this._webcam_stream = null; - this._video = null; - }; - - LGraphTextureWebcam.prototype.onDrawBackground = function(ctx) { - if (this.flags.collapsed || this.size[1] <= 20) { - return; - } - - if (!this._video) { - return; - } - - //render to graph canvas - ctx.save(); - if (!ctx.webgl) { - //reverse image - ctx.drawImage(this._video, 0, 0, this.size[0], this.size[1]); - } else { - if (this._video_texture) { - ctx.drawImage( - this._video_texture, - 0, - 0, - this.size[0], - this.size[1] - ); - } - } - ctx.restore(); - }; - - LGraphTextureWebcam.prototype.onExecute = function() { - if (this._webcam_stream == null && !this._waiting_confirmation) { - this.openStream(); - } - - if (!this._video || !this._video.videoWidth) { - return; - } - - var width = this._video.videoWidth; - var height = this._video.videoHeight; - - var temp = this._video_texture; - if (!temp || temp.width != width || temp.height != height) { - this._video_texture = new GL.Texture(width, height, { - format: gl.RGB, - filter: gl.LINEAR - }); - } - - this._video_texture.uploadImage(this._video); - this._video_texture.version = ++this.version; - - if (this.properties.texture_name) { - var container = LGraphTexture.getTexturesContainer(); - container[this.properties.texture_name] = this._video_texture; - } - - this.setOutputData(0, this._video_texture); - for (var i = 1; i < this.outputs.length; ++i) { - if (!this.outputs[i]) { - continue; - } - switch (this.outputs[i].name) { - case "width": - this.setOutputData(i, this._video.videoWidth); - break; - case "height": - this.setOutputData(i, this._video.videoHeight); - break; - } - } - }; - - LGraphTextureWebcam.prototype.onGetOutputs = function() { - return [ - ["width", "number"], - ["height", "number"], - ["stream_ready", LiteGraph.EVENT], - ["stream_closed", LiteGraph.EVENT], - ["stream_error", LiteGraph.EVENT] - ]; - }; - - LiteGraph.registerNodeType("texture/webcam", LGraphTextureWebcam); - - //from https://github.com/spite/Wagner - function LGraphLensFX() { - this.addInput("in", "Texture"); - this.addInput("f", "number"); - this.addOutput("out", "Texture"); - this.properties = { - enabled: true, - factor: 1, - precision: LGraphTexture.LOW - }; - - this._uniforms = { u_texture: 0, u_factor: 1 }; - } - - LGraphLensFX.title = "Lens FX"; - LGraphLensFX.desc = "distortion and chromatic aberration"; - - LGraphLensFX.widgets_info = { - precision: { widget: "combo", values: LGraphTexture.MODE_VALUES } - }; - - LGraphLensFX.prototype.onGetInputs = function() { - return [["enabled", "boolean"]]; - }; - - LGraphLensFX.prototype.onExecute = function() { - var tex = this.getInputData(0); - if (!tex) { - return; - } - - if (!this.isOutputConnected(0)) { - return; - } //saves work - - if ( - this.properties.precision === LGraphTexture.PASS_THROUGH || - this.getInputOrProperty("enabled") === false - ) { - this.setOutputData(0, tex); - return; - } - - var temp = this._temp_texture; - if ( - !temp || - temp.width != tex.width || - temp.height != tex.height || - temp.type != tex.type - ) { - temp = this._temp_texture = new GL.Texture( - tex.width, - tex.height, - { type: tex.type, format: gl.RGBA, filter: gl.LINEAR } - ); - } - - var shader = LGraphLensFX._shader; - if (!shader) { - shader = LGraphLensFX._shader = new GL.Shader( - GL.Shader.SCREEN_VERTEX_SHADER, - LGraphLensFX.pixel_shader - ); - } - - var factor = this.getInputData(1); - if (factor == null) { - factor = this.properties.factor; - } - - var uniforms = this._uniforms; - uniforms.u_factor = factor; - - //apply shader - gl.disable(gl.DEPTH_TEST); - temp.drawTo(function() { - tex.bind(0); - shader.uniforms(uniforms).draw(GL.Mesh.getScreenQuad()); - }); - - this.setOutputData(0, temp); - }; - - LGraphLensFX.pixel_shader = - "precision highp float;\n\ - varying vec2 v_coord;\n\ - uniform sampler2D u_texture;\n\ - uniform float u_factor;\n\ - vec2 barrelDistortion(vec2 coord, float amt) {\n\ - vec2 cc = coord - 0.5;\n\ - float dist = dot(cc, cc);\n\ - return coord + cc * dist * amt;\n\ - }\n\ - \n\ - float sat( float t )\n\ - {\n\ - return clamp( t, 0.0, 1.0 );\n\ - }\n\ - \n\ - float linterp( float t ) {\n\ - return sat( 1.0 - abs( 2.0*t - 1.0 ) );\n\ - }\n\ - \n\ - float remap( float t, float a, float b ) {\n\ - return sat( (t - a) / (b - a) );\n\ - }\n\ - \n\ - vec4 spectrum_offset( float t ) {\n\ - vec4 ret;\n\ - float lo = step(t,0.5);\n\ - float hi = 1.0-lo;\n\ - float w = linterp( remap( t, 1.0/6.0, 5.0/6.0 ) );\n\ - ret = vec4(lo,1.0,hi, 1.) * vec4(1.0-w, w, 1.0-w, 1.);\n\ - \n\ - return pow( ret, vec4(1.0/2.2) );\n\ - }\n\ - \n\ - const float max_distort = 2.2;\n\ - const int num_iter = 12;\n\ - const float reci_num_iter_f = 1.0 / float(num_iter);\n\ - \n\ - void main()\n\ - { \n\ - vec2 uv=v_coord;\n\ - vec4 sumcol = vec4(0.0);\n\ - vec4 sumw = vec4(0.0); \n\ - for ( int i=0; i= res)\n\ - break;\n\ - iCount++;\n\ - }\n\ - float nf = n/normK;\n\ - return nf*nf*nf*nf;\n\ - }\n\ - void main() {\n\ - vec2 uv = v_coord * u_scale * u_viewport + u_offset * u_scale;\n\ - vec4 color = vec4( pNoise( uv, u_octaves ) * u_amplitude );\n\ - gl_FragColor = color;\n\ - }"; - - LiteGraph.registerNodeType("texture/perlin", LGraphTexturePerlin); - - function LGraphTextureCanvas2D() { - this.addInput("v"); - this.addOutput("out", "Texture"); - this.properties = { - code: LGraphTextureCanvas2D.default_code, - width: 512, - height: 512, - clear: true, - precision: LGraphTexture.DEFAULT, - use_html_canvas: false - }; - this._func = null; - this._temp_texture = null; - this.compileCode(); - } - - LGraphTextureCanvas2D.title = "Canvas2D"; - LGraphTextureCanvas2D.desc = "Executes Canvas2D code inside a texture or the viewport."; - LGraphTextureCanvas2D.help = "Set width and height to 0 to match viewport size."; - - LGraphTextureCanvas2D.default_code = "//vars: canvas,ctx,time\nctx.fillStyle='red';\nctx.fillRect(0,0,50,50);\n"; - - LGraphTextureCanvas2D.widgets_info = { - precision: { widget: "combo", values: LGraphTexture.MODE_VALUES }, - code: { type: "code" }, - width: { type: "number", precision: 0, step: 1 }, - height: { type: "number", precision: 0, step: 1 } - }; - - LGraphTextureCanvas2D.prototype.onPropertyChanged = function( name, value ) { - if (name == "code" ) - this.compileCode( value ); - } - - LGraphTextureCanvas2D.prototype.compileCode = function( code ) { - this._func = null; - if( !LiteGraph.allow_scripts ) - return; - - try { - this._func = new Function( "canvas", "ctx", "time", "script","v", code ); - this.boxcolor = "#00FF00"; - } catch (err) { - this.boxcolor = "#FF0000"; - console.error("Error parsing script"); - console.error(err); - } - }; - - LGraphTextureCanvas2D.prototype.onExecute = function() { - var func = this._func; - if (!func || !this.isOutputConnected(0)) { - return; - } - this.executeDraw( func ); - } - - LGraphTextureCanvas2D.prototype.executeDraw = function( func_context ) { - - var width = this.properties.width || gl.canvas.width; - var height = this.properties.height || gl.canvas.height; - var temp = this._temp_texture; - var type = LGraphTexture.getTextureType( this.properties.precision ); - if (!temp || temp.width != width || temp.height != height || temp.type != type ) { - temp = this._temp_texture = new GL.Texture(width, height, { - format: gl.RGBA, - filter: gl.LINEAR, - type: type - }); - } - - var v = this.getInputData(0); - - var properties = this.properties; - var that = this; - var time = this.graph.getTime(); - var ctx = gl; - var canvas = gl.canvas; - if( this.properties.use_html_canvas || !global.enableWebGLCanvas ) - { - if(!this._canvas) - { - canvas = this._canvas = createCanvas(width.height); - ctx = this._ctx = canvas.getContext("2d"); - } - else - { - canvas = this._canvas; - ctx = this._ctx; - } - canvas.width = width; - canvas.height = height; - } - - if(ctx == gl) //using Canvas2DtoWebGL - temp.drawTo(function() { - gl.start2D(); - if(properties.clear) - { - gl.clearColor(0,0,0,0); - gl.clear( gl.COLOR_BUFFER_BIT ); - } - - try { - if (func_context.draw) { - func_context.draw.call(that, canvas, ctx, time, func_context, v); - } else { - func_context.call(that, canvas, ctx, time, func_context,v); - } - that.boxcolor = "#00FF00"; - } catch (err) { - that.boxcolor = "#FF0000"; - console.error("Error executing script"); - console.error(err); - } - gl.finish2D(); - }); - else //rendering to offscreen canvas and uploading to texture - { - if(properties.clear) - ctx.clearRect(0,0,canvas.width,canvas.height); - - try { - if (func_context.draw) { - func_context.draw.call(this, canvas, ctx, time, func_context, v); - } else { - func_context.call(this, canvas, ctx, time, func_context,v); - } - this.boxcolor = "#00FF00"; - } catch (err) { - this.boxcolor = "#FF0000"; - console.error("Error executing script"); - console.error(err); - } - temp.uploadImage( canvas ); - } - - this.setOutputData(0, temp); - }; - - LiteGraph.registerNodeType("texture/canvas2D", LGraphTextureCanvas2D); - - // To do chroma keying ***************** - - function LGraphTextureMatte() { - this.addInput("in", "Texture"); - - this.addOutput("out", "Texture"); - this.properties = { - key_color: vec3.fromValues(0, 1, 0), - threshold: 0.8, - slope: 0.2, - precision: LGraphTexture.DEFAULT - }; - } - - LGraphTextureMatte.title = "Matte"; - LGraphTextureMatte.desc = "Extracts background"; - - LGraphTextureMatte.widgets_info = { - key_color: { widget: "color" }, - precision: { widget: "combo", values: LGraphTexture.MODE_VALUES } - }; - - LGraphTextureMatte.prototype.onExecute = function() { - if (!this.isOutputConnected(0)) { - return; - } //saves work - - var tex = this.getInputData(0); - - if (this.properties.precision === LGraphTexture.PASS_THROUGH) { - this.setOutputData(0, tex); - return; - } - - if (!tex) { - return; - } - - this._tex = LGraphTexture.getTargetTexture( - tex, - this._tex, - this.properties.precision - ); - - gl.disable(gl.BLEND); - gl.disable(gl.DEPTH_TEST); - - if (!this._uniforms) { - this._uniforms = { - u_texture: 0, - u_key_color: this.properties.key_color, - u_threshold: 1, - u_slope: 1 - }; - } - var uniforms = this._uniforms; - - var mesh = Mesh.getScreenQuad(); - var shader = LGraphTextureMatte._shader; - if (!shader) { - shader = LGraphTextureMatte._shader = new GL.Shader( - GL.Shader.SCREEN_VERTEX_SHADER, - LGraphTextureMatte.pixel_shader - ); - } - - uniforms.u_key_color = this.properties.key_color; - uniforms.u_threshold = this.properties.threshold; - uniforms.u_slope = this.properties.slope; - - this._tex.drawTo(function() { - tex.bind(0); - shader.uniforms(uniforms).draw(mesh); - }); - - this.setOutputData(0, this._tex); - }; - - LGraphTextureMatte.pixel_shader = - "precision highp float;\n\ - varying vec2 v_coord;\n\ - uniform sampler2D u_texture;\n\ - uniform vec3 u_key_color;\n\ - uniform float u_threshold;\n\ - uniform float u_slope;\n\ - \n\ - void main() {\n\ - vec3 color = texture2D( u_texture, v_coord ).xyz;\n\ - float diff = length( normalize(color) - normalize(u_key_color) );\n\ - float edge = u_threshold * (1.0 - u_slope);\n\ - float alpha = smoothstep( edge, u_threshold, diff);\n\ - gl_FragColor = vec4( color, alpha );\n\ - }"; - - LiteGraph.registerNodeType("texture/matte", LGraphTextureMatte); - - //*********************************** - function LGraphCubemapToTexture2D() { - this.addInput("in", "texture"); - this.addInput("yaw", "number"); - this.addOutput("out", "texture"); - this.properties = { yaw: 0 }; - } - - LGraphCubemapToTexture2D.title = "CubemapToTexture2D"; - LGraphCubemapToTexture2D.desc = "Transforms a CUBEMAP texture into a TEXTURE2D in Polar Representation"; - - LGraphCubemapToTexture2D.prototype.onExecute = function() { - if (!this.isOutputConnected(0)) - return; - - var tex = this.getInputData(0); - if ( !tex || tex.texture_type != GL.TEXTURE_CUBE_MAP ) - return; - if( this._last_tex && ( this._last_tex.height != tex.height || this._last_tex.type != tex.type )) - this._last_tex = null; - var yaw = this.getInputOrProperty("yaw"); - this._last_tex = GL.Texture.cubemapToTexture2D( tex, tex.height, this._last_tex, true, yaw ); - this.setOutputData( 0, this._last_tex ); - }; - - LiteGraph.registerNodeType( "texture/cubemapToTexture2D", LGraphCubemapToTexture2D ); -})(this); diff --git a/src/nodes/graphics.js b/src/nodes/graphics.js deleted file mode 100755 index 17431994b..000000000 --- a/src/nodes/graphics.js +++ /dev/null @@ -1,896 +0,0 @@ -(function(global) { - var LiteGraph = global.LiteGraph; - - function GraphicsPlot() { - this.addInput("A", "Number"); - this.addInput("B", "Number"); - this.addInput("C", "Number"); - this.addInput("D", "Number"); - - this.values = [[], [], [], []]; - this.properties = { scale: 2 }; - } - - GraphicsPlot.title = "Plot"; - GraphicsPlot.desc = "Plots data over time"; - GraphicsPlot.colors = ["#FFF", "#F99", "#9F9", "#99F"]; - - GraphicsPlot.prototype.onExecute = function(ctx) { - if (this.flags.collapsed) { - return; - } - - var size = this.size; - - for (var i = 0; i < 4; ++i) { - var v = this.getInputData(i); - if (v == null) { - continue; - } - var values = this.values[i]; - values.push(v); - if (values.length > size[0]) { - values.shift(); - } - } - }; - - GraphicsPlot.prototype.onDrawBackground = function(ctx) { - if (this.flags.collapsed) { - return; - } - - var size = this.size; - - var scale = (0.5 * size[1]) / this.properties.scale; - var colors = GraphicsPlot.colors; - var offset = size[1] * 0.5; - - ctx.fillStyle = "#000"; - ctx.fillRect(0, 0, size[0], size[1]); - ctx.strokeStyle = "#555"; - ctx.beginPath(); - ctx.moveTo(0, offset); - ctx.lineTo(size[0], offset); - ctx.stroke(); - - if (this.inputs) { - for (var i = 0; i < 4; ++i) { - var values = this.values[i]; - if (!this.inputs[i] || !this.inputs[i].link) { - continue; - } - ctx.strokeStyle = colors[i]; - ctx.beginPath(); - var v = values[0] * scale * -1 + offset; - ctx.moveTo(0, clamp(v, 0, size[1])); - for (var j = 1; j < values.length && j < size[0]; ++j) { - var v = values[j] * scale * -1 + offset; - ctx.lineTo(j, clamp(v, 0, size[1])); - } - ctx.stroke(); - } - } - }; - - LiteGraph.registerNodeType("graphics/plot", GraphicsPlot); - - function GraphicsImage() { - this.addOutput("frame", "image"); - this.properties = { url: "" }; - } - - GraphicsImage.title = "Image"; - GraphicsImage.desc = "Image loader"; - GraphicsImage.widgets = [{ name: "load", text: "Load", type: "button" }]; - - GraphicsImage.supported_extensions = ["jpg", "jpeg", "png", "gif"]; - - GraphicsImage.prototype.onAdded = function() { - if (this.properties["url"] != "" && this.img == null) { - this.loadImage(this.properties["url"]); - } - }; - - GraphicsImage.prototype.onDrawBackground = function(ctx) { - if (this.flags.collapsed) { - return; - } - if (this.img && this.size[0] > 5 && this.size[1] > 5 && this.img.width) { - ctx.drawImage(this.img, 0, 0, this.size[0], this.size[1]); - } - }; - - GraphicsImage.prototype.onExecute = function() { - if (!this.img) { - this.boxcolor = "#000"; - } - if (this.img && this.img.width) { - this.setOutputData(0, this.img); - } else { - this.setOutputData(0, null); - } - if (this.img && this.img.dirty) { - this.img.dirty = false; - } - }; - - GraphicsImage.prototype.onPropertyChanged = function(name, value) { - this.properties[name] = value; - if (name == "url" && value != "") { - this.loadImage(value); - } - - return true; - }; - - GraphicsImage.prototype.loadImage = function(url, callback) { - if (url == "") { - this.img = null; - return; - } - - this.img = document.createElement("img"); - - if (url.substr(0, 4) == "http" && LiteGraph.proxy) { - url = LiteGraph.proxy + url.substr(url.indexOf(":") + 3); - } - - this.img.src = url; - this.boxcolor = "#F95"; - var that = this; - this.img.onload = function() { - if (callback) { - callback(this); - } - console.log( "Image loaded, size: " + that.img.width + "x" + that.img.height ); - this.dirty = true; - that.boxcolor = "#9F9"; - that.setDirtyCanvas(true); - }; - this.img.onerror = function() { - console.log("error loading the image:" + url); - } - }; - - GraphicsImage.prototype.onWidget = function(e, widget) { - if (widget.name == "load") { - this.loadImage(this.properties["url"]); - } - }; - - GraphicsImage.prototype.onDropFile = function(file) { - var that = this; - if (this._url) { - URL.revokeObjectURL(this._url); - } - this._url = URL.createObjectURL(file); - this.properties.url = this._url; - this.loadImage(this._url, function(img) { - that.size[1] = (img.height / img.width) * that.size[0]; - }); - }; - - LiteGraph.registerNodeType("graphics/image", GraphicsImage); - - function ColorPalette() { - this.addInput("f", "number"); - this.addOutput("Color", "color"); - this.properties = { - colorA: "#444444", - colorB: "#44AAFF", - colorC: "#44FFAA", - colorD: "#FFFFFF" - }; - } - - ColorPalette.title = "Palette"; - ColorPalette.desc = "Generates a color"; - - ColorPalette.prototype.onExecute = function() { - var c = []; - - if (this.properties.colorA != null) { - c.push(hex2num(this.properties.colorA)); - } - if (this.properties.colorB != null) { - c.push(hex2num(this.properties.colorB)); - } - if (this.properties.colorC != null) { - c.push(hex2num(this.properties.colorC)); - } - if (this.properties.colorD != null) { - c.push(hex2num(this.properties.colorD)); - } - - var f = this.getInputData(0); - if (f == null) { - f = 0.5; - } - if (f > 1.0) { - f = 1.0; - } else if (f < 0.0) { - f = 0.0; - } - - if (c.length == 0) { - return; - } - - var result = [0, 0, 0]; - if (f == 0) { - result = c[0]; - } else if (f == 1) { - result = c[c.length - 1]; - } else { - var pos = (c.length - 1) * f; - var c1 = c[Math.floor(pos)]; - var c2 = c[Math.floor(pos) + 1]; - var t = pos - Math.floor(pos); - result[0] = c1[0] * (1 - t) + c2[0] * t; - result[1] = c1[1] * (1 - t) + c2[1] * t; - result[2] = c1[2] * (1 - t) + c2[2] * t; - } - - /* - c[0] = 1.0 - Math.abs( Math.sin( 0.1 * reModular.getTime() * Math.PI) ); - c[1] = Math.abs( Math.sin( 0.07 * reModular.getTime() * Math.PI) ); - c[2] = Math.abs( Math.sin( 0.01 * reModular.getTime() * Math.PI) ); - */ - - for (var i=0; i < result.length; i++) { - result[i] /= 255; - } - - this.boxcolor = colorToString(result); - this.setOutputData(0, result); - }; - - LiteGraph.registerNodeType("color/palette", ColorPalette); - - function ImageFrame() { - this.addInput("", "image,canvas"); - this.size = [200, 200]; - } - - ImageFrame.title = "Frame"; - ImageFrame.desc = "Frame viewerew"; - ImageFrame.widgets = [ - { name: "resize", text: "Resize box", type: "button" }, - { name: "view", text: "View Image", type: "button" } - ]; - - ImageFrame.prototype.onDrawBackground = function(ctx) { - if (this.frame && !this.flags.collapsed) { - ctx.drawImage(this.frame, 0, 0, this.size[0], this.size[1]); - } - }; - - ImageFrame.prototype.onExecute = function() { - this.frame = this.getInputData(0); - this.setDirtyCanvas(true); - }; - - ImageFrame.prototype.onWidget = function(e, widget) { - if (widget.name == "resize" && this.frame) { - var width = this.frame.width; - var height = this.frame.height; - - if (!width && this.frame.videoWidth != null) { - width = this.frame.videoWidth; - height = this.frame.videoHeight; - } - - if (width && height) { - this.size = [width, height]; - } - this.setDirtyCanvas(true, true); - } else if (widget.name == "view") { - this.show(); - } - }; - - ImageFrame.prototype.show = function() { - //var str = this.canvas.toDataURL("image/png"); - if (showElement && this.frame) { - showElement(this.frame); - } - }; - - LiteGraph.registerNodeType("graphics/frame", ImageFrame); - - function ImageFade() { - this.addInputs([ - ["img1", "image"], - ["img2", "image"], - ["fade", "number"] - ]); - this.addOutput("", "image"); - this.properties = { fade: 0.5, width: 512, height: 512 }; - } - - ImageFade.title = "Image fade"; - ImageFade.desc = "Fades between images"; - ImageFade.widgets = [ - { name: "resizeA", text: "Resize to A", type: "button" }, - { name: "resizeB", text: "Resize to B", type: "button" } - ]; - - ImageFade.prototype.onAdded = function() { - this.createCanvas(); - var ctx = this.canvas.getContext("2d"); - ctx.fillStyle = "#000"; - ctx.fillRect(0, 0, this.properties["width"], this.properties["height"]); - }; - - ImageFade.prototype.createCanvas = function() { - this.canvas = document.createElement("canvas"); - this.canvas.width = this.properties["width"]; - this.canvas.height = this.properties["height"]; - }; - - ImageFade.prototype.onExecute = function() { - var ctx = this.canvas.getContext("2d"); - this.canvas.width = this.canvas.width; - - var A = this.getInputData(0); - if (A != null) { - ctx.drawImage(A, 0, 0, this.canvas.width, this.canvas.height); - } - - var fade = this.getInputData(2); - if (fade == null) { - fade = this.properties["fade"]; - } else { - this.properties["fade"] = fade; - } - - ctx.globalAlpha = fade; - var B = this.getInputData(1); - if (B != null) { - ctx.drawImage(B, 0, 0, this.canvas.width, this.canvas.height); - } - ctx.globalAlpha = 1.0; - - this.setOutputData(0, this.canvas); - this.setDirtyCanvas(true); - }; - - LiteGraph.registerNodeType("graphics/imagefade", ImageFade); - - function ImageCrop() { - this.addInput("", "image"); - this.addOutput("", "image"); - this.properties = { width: 256, height: 256, x: 0, y: 0, scale: 1.0 }; - this.size = [50, 20]; - } - - ImageCrop.title = "Crop"; - ImageCrop.desc = "Crop Image"; - - ImageCrop.prototype.onAdded = function() { - this.createCanvas(); - }; - - ImageCrop.prototype.createCanvas = function() { - this.canvas = document.createElement("canvas"); - this.canvas.width = this.properties["width"]; - this.canvas.height = this.properties["height"]; - }; - - ImageCrop.prototype.onExecute = function() { - var input = this.getInputData(0); - if (!input) { - return; - } - - if (input.width) { - var ctx = this.canvas.getContext("2d"); - - ctx.drawImage( - input, - -this.properties["x"], - -this.properties["y"], - input.width * this.properties["scale"], - input.height * this.properties["scale"] - ); - this.setOutputData(0, this.canvas); - } else { - this.setOutputData(0, null); - } - }; - - ImageCrop.prototype.onDrawBackground = function(ctx) { - if (this.flags.collapsed) { - return; - } - if (this.canvas) { - ctx.drawImage( - this.canvas, - 0, - 0, - this.canvas.width, - this.canvas.height, - 0, - 0, - this.size[0], - this.size[1] - ); - } - }; - - ImageCrop.prototype.onPropertyChanged = function(name, value) { - this.properties[name] = value; - - if (name == "scale") { - this.properties[name] = parseFloat(value); - if (this.properties[name] == 0) { - console.error("Error in scale"); - this.properties[name] = 1.0; - } - } else { - this.properties[name] = parseInt(value); - } - - this.createCanvas(); - - return true; - }; - - LiteGraph.registerNodeType("graphics/cropImage", ImageCrop); - - //CANVAS stuff - - function CanvasNode() { - this.addInput("clear", LiteGraph.ACTION); - this.addOutput("", "canvas"); - this.properties = { width: 512, height: 512, autoclear: true }; - - this.canvas = document.createElement("canvas"); - this.ctx = this.canvas.getContext("2d"); - } - - CanvasNode.title = "Canvas"; - CanvasNode.desc = "Canvas to render stuff"; - - CanvasNode.prototype.onExecute = function() { - var canvas = this.canvas; - var w = this.properties.width | 0; - var h = this.properties.height | 0; - if (canvas.width != w) { - canvas.width = w; - } - if (canvas.height != h) { - canvas.height = h; - } - - if (this.properties.autoclear) { - this.ctx.clearRect(0, 0, canvas.width, canvas.height); - } - this.setOutputData(0, canvas); - }; - - CanvasNode.prototype.onAction = function(action, param) { - if (action == "clear") { - this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height); - } - }; - - LiteGraph.registerNodeType("graphics/canvas", CanvasNode); - - function DrawImageNode() { - this.addInput("canvas", "canvas"); - this.addInput("img", "image,canvas"); - this.addInput("x", "number"); - this.addInput("y", "number"); - this.properties = { x: 0, y: 0, opacity: 1 }; - } - - DrawImageNode.title = "DrawImage"; - DrawImageNode.desc = "Draws image into a canvas"; - - DrawImageNode.prototype.onExecute = function() { - var canvas = this.getInputData(0); - if (!canvas) { - return; - } - - var img = this.getInputOrProperty("img"); - if (!img) { - return; - } - - var x = this.getInputOrProperty("x"); - var y = this.getInputOrProperty("y"); - var ctx = canvas.getContext("2d"); - ctx.drawImage(img, x, y); - }; - - LiteGraph.registerNodeType("graphics/drawImage", DrawImageNode); - - function DrawRectangleNode() { - this.addInput("canvas", "canvas"); - this.addInput("x", "number"); - this.addInput("y", "number"); - this.addInput("w", "number"); - this.addInput("h", "number"); - this.properties = { - x: 0, - y: 0, - w: 10, - h: 10, - color: "white", - opacity: 1 - }; - } - - DrawRectangleNode.title = "DrawRectangle"; - DrawRectangleNode.desc = "Draws rectangle in canvas"; - - DrawRectangleNode.prototype.onExecute = function() { - var canvas = this.getInputData(0); - if (!canvas) { - return; - } - - var x = this.getInputOrProperty("x"); - var y = this.getInputOrProperty("y"); - var w = this.getInputOrProperty("w"); - var h = this.getInputOrProperty("h"); - var ctx = canvas.getContext("2d"); - ctx.fillRect(x, y, w, h); - }; - - LiteGraph.registerNodeType("graphics/drawRectangle", DrawRectangleNode); - - function ImageVideo() { - this.addInput("t", "number"); - this.addOutputs([["frame", "image"], ["t", "number"], ["d", "number"]]); - this.properties = { url: "", use_proxy: true }; - } - - ImageVideo.title = "Video"; - ImageVideo.desc = "Video playback"; - ImageVideo.widgets = [ - { name: "play", text: "PLAY", type: "minibutton" }, - { name: "stop", text: "STOP", type: "minibutton" }, - { name: "demo", text: "Demo video", type: "button" }, - { name: "mute", text: "Mute video", type: "button" } - ]; - - ImageVideo.prototype.onExecute = function() { - if (!this.properties.url) { - return; - } - - if (this.properties.url != this._video_url) { - this.loadVideo(this.properties.url); - } - - if (!this._video || this._video.width == 0) { - return; - } - - var t = this.getInputData(0); - if (t && t >= 0 && t <= 1.0) { - this._video.currentTime = t * this._video.duration; - this._video.pause(); - } - - this._video.dirty = true; - this.setOutputData(0, this._video); - this.setOutputData(1, this._video.currentTime); - this.setOutputData(2, this._video.duration); - this.setDirtyCanvas(true); - }; - - ImageVideo.prototype.onStart = function() { - this.play(); - }; - - ImageVideo.prototype.onStop = function() { - this.stop(); - }; - - ImageVideo.prototype.loadVideo = function(url) { - this._video_url = url; - - var pos = url.substr(0,10).indexOf(":"); - var protocol = ""; - if(pos != -1) - protocol = url.substr(0,pos); - - var host = ""; - if(protocol) - { - host = url.substr(0,url.indexOf("/",protocol.length + 3)); - host = host.substr(protocol.length+3); - } - - if ( - this.properties.use_proxy && - protocol && - LiteGraph.proxy && - host != location.host - ) { - url = LiteGraph.proxy + url.substr(url.indexOf(":") + 3); - } - - this._video = document.createElement("video"); - this._video.src = url; - this._video.type = "type=video/mp4"; - - this._video.muted = true; - this._video.autoplay = true; - - var that = this; - this._video.addEventListener("loadedmetadata", function(e) { - //onload - console.log("Duration: " + this.duration + " seconds"); - console.log("Size: " + this.videoWidth + "," + this.videoHeight); - that.setDirtyCanvas(true); - this.width = this.videoWidth; - this.height = this.videoHeight; - }); - this._video.addEventListener("progress", function(e) { - //onload - console.log("video loading..."); - }); - this._video.addEventListener("error", function(e) { - console.error("Error loading video: " + this.src); - if (this.error) { - switch (this.error.code) { - case this.error.MEDIA_ERR_ABORTED: - console.error("You stopped the video."); - break; - case this.error.MEDIA_ERR_NETWORK: - console.error("Network error - please try again later."); - break; - case this.error.MEDIA_ERR_DECODE: - console.error("Video is broken.."); - break; - case this.error.MEDIA_ERR_SRC_NOT_SUPPORTED: - console.error("Sorry, your browser can't play this video."); - break; - } - } - }); - - this._video.addEventListener("ended", function(e) { - console.log("Video Ended."); - this.play(); //loop - }); - - //document.body.appendChild(this.video); - }; - - ImageVideo.prototype.onPropertyChanged = function(name, value) { - this.properties[name] = value; - if (name == "url" && value != "") { - this.loadVideo(value); - } - - return true; - }; - - ImageVideo.prototype.play = function() { - if (this._video && this._video.videoWidth ) { //is loaded - this._video.play(); - } - }; - - ImageVideo.prototype.playPause = function() { - if (!this._video) { - return; - } - if (this._video.paused) { - this.play(); - } else { - this.pause(); - } - }; - - ImageVideo.prototype.stop = function() { - if (!this._video) { - return; - } - this._video.pause(); - this._video.currentTime = 0; - }; - - ImageVideo.prototype.pause = function() { - if (!this._video) { - return; - } - console.log("Video paused"); - this._video.pause(); - }; - - ImageVideo.prototype.onWidget = function(e, widget) { - /* - if(widget.name == "demo") - { - this.loadVideo(); - } - else if(widget.name == "play") - { - if(this._video) - this.playPause(); - } - if(widget.name == "stop") - { - this.stop(); - } - else if(widget.name == "mute") - { - if(this._video) - this._video.muted = !this._video.muted; - } - */ - }; - - LiteGraph.registerNodeType("graphics/video", ImageVideo); - - // Texture Webcam ***************************************** - function ImageWebcam() { - this.addOutput("Webcam", "image"); - this.properties = { filterFacingMode: false, facingMode: "user" }; - this.boxcolor = "black"; - this.frame = 0; - } - - ImageWebcam.title = "Webcam"; - ImageWebcam.desc = "Webcam image"; - ImageWebcam.is_webcam_open = false; - - ImageWebcam.prototype.openStream = function() { - if (!navigator.mediaDevices.getUserMedia) { - console.log('getUserMedia() is not supported in your browser, use chrome and enable WebRTC from about://flags'); - return; - } - - this._waiting_confirmation = true; - - // Not showing vendor prefixes. - var constraints = { - audio: false, - video: !this.properties.filterFacingMode ? true : { facingMode: this.properties.facingMode } - }; - navigator.mediaDevices - .getUserMedia(constraints) - .then(this.streamReady.bind(this)) - .catch(onFailSoHard); - - var that = this; - function onFailSoHard(e) { - console.log("Webcam rejected", e); - that._webcam_stream = false; - ImageWebcam.is_webcam_open = false; - that.boxcolor = "red"; - that.trigger("stream_error"); - } - }; - - ImageWebcam.prototype.closeStream = function() { - if (this._webcam_stream) { - var tracks = this._webcam_stream.getTracks(); - if (tracks.length) { - for (var i = 0; i < tracks.length; ++i) { - tracks[i].stop(); - } - } - ImageWebcam.is_webcam_open = false; - this._webcam_stream = null; - this._video = null; - this.boxcolor = "black"; - this.trigger("stream_closed"); - } - }; - - ImageWebcam.prototype.onPropertyChanged = function(name, value) { - if (name == "facingMode") { - this.properties.facingMode = value; - this.closeStream(); - this.openStream(); - } - }; - - ImageWebcam.prototype.onRemoved = function() { - this.closeStream(); - }; - - ImageWebcam.prototype.streamReady = function(localMediaStream) { - this._webcam_stream = localMediaStream; - //this._waiting_confirmation = false; - this.boxcolor = "green"; - - var video = this._video; - if (!video) { - video = document.createElement("video"); - video.autoplay = true; - video.srcObject = localMediaStream; - this._video = video; - //document.body.appendChild( video ); //debug - //when video info is loaded (size and so) - video.onloadedmetadata = function(e) { - // Ready to go. Do some stuff. - console.log(e); - ImageWebcam.is_webcam_open = true; - }; - } - - this.trigger("stream_ready", video); - }; - - ImageWebcam.prototype.onExecute = function() { - if (this._webcam_stream == null && !this._waiting_confirmation) { - this.openStream(); - } - - if (!this._video || !this._video.videoWidth) { - return; - } - - this._video.frame = ++this.frame; - this._video.width = this._video.videoWidth; - this._video.height = this._video.videoHeight; - this.setOutputData(0, this._video); - for (var i = 1; i < this.outputs.length; ++i) { - if (!this.outputs[i]) { - continue; - } - switch (this.outputs[i].name) { - case "width": - this.setOutputData(i, this._video.videoWidth); - break; - case "height": - this.setOutputData(i, this._video.videoHeight); - break; - } - } - }; - - ImageWebcam.prototype.getExtraMenuOptions = function(graphcanvas) { - var that = this; - var txt = !that.properties.show ? "Show Frame" : "Hide Frame"; - return [ - { - content: txt, - callback: function() { - that.properties.show = !that.properties.show; - } - } - ]; - }; - - ImageWebcam.prototype.onDrawBackground = function(ctx) { - if ( - this.flags.collapsed || - this.size[1] <= 20 || - !this.properties.show - ) { - return; - } - - if (!this._video) { - return; - } - - //render to graph canvas - ctx.save(); - ctx.drawImage(this._video, 0, 0, this.size[0], this.size[1]); - ctx.restore(); - }; - - ImageWebcam.prototype.onGetOutputs = function() { - return [ - ["width", "number"], - ["height", "number"], - ["stream_ready", LiteGraph.EVENT], - ["stream_closed", LiteGraph.EVENT], - ["stream_error", LiteGraph.EVENT] - ]; - }; - - LiteGraph.registerNodeType("graphics/webcam", ImageWebcam); -})(this); diff --git a/src/nodes/input.js b/src/nodes/input.js deleted file mode 100755 index b8f5fda80..000000000 --- a/src/nodes/input.js +++ /dev/null @@ -1,354 +0,0 @@ -(function(global) { - var LiteGraph = global.LiteGraph; - - function GamepadInput() { - this.addOutput("left_x_axis", "number"); - this.addOutput("left_y_axis", "number"); - this.addOutput("button_pressed", LiteGraph.EVENT); - this.properties = { gamepad_index: 0, threshold: 0.1 }; - - this._left_axis = new Float32Array(2); - this._right_axis = new Float32Array(2); - this._triggers = new Float32Array(2); - this._previous_buttons = new Uint8Array(17); - this._current_buttons = new Uint8Array(17); - } - - GamepadInput.title = "Gamepad"; - GamepadInput.desc = "gets the input of the gamepad"; - - GamepadInput.CENTER = 0; - GamepadInput.LEFT = 1; - GamepadInput.RIGHT = 2; - GamepadInput.UP = 4; - GamepadInput.DOWN = 8; - - GamepadInput.zero = new Float32Array(2); - GamepadInput.buttons = [ - "a", - "b", - "x", - "y", - "lb", - "rb", - "lt", - "rt", - "back", - "start", - "ls", - "rs", - "home" - ]; - - GamepadInput.prototype.onExecute = function() { - //get gamepad - var gamepad = this.getGamepad(); - var threshold = this.properties.threshold || 0.0; - - if (gamepad) { - this._left_axis[0] = - Math.abs(gamepad.xbox.axes["lx"]) > threshold - ? gamepad.xbox.axes["lx"] - : 0; - this._left_axis[1] = - Math.abs(gamepad.xbox.axes["ly"]) > threshold - ? gamepad.xbox.axes["ly"] - : 0; - this._right_axis[0] = - Math.abs(gamepad.xbox.axes["rx"]) > threshold - ? gamepad.xbox.axes["rx"] - : 0; - this._right_axis[1] = - Math.abs(gamepad.xbox.axes["ry"]) > threshold - ? gamepad.xbox.axes["ry"] - : 0; - this._triggers[0] = - Math.abs(gamepad.xbox.axes["ltrigger"]) > threshold - ? gamepad.xbox.axes["ltrigger"] - : 0; - this._triggers[1] = - Math.abs(gamepad.xbox.axes["rtrigger"]) > threshold - ? gamepad.xbox.axes["rtrigger"] - : 0; - } - - if (this.outputs) { - for (var i = 0; i < this.outputs.length; i++) { - var output = this.outputs[i]; - if (!output.links || !output.links.length) { - continue; - } - var v = null; - - if (gamepad) { - switch (output.name) { - case "left_axis": - v = this._left_axis; - break; - case "right_axis": - v = this._right_axis; - break; - case "left_x_axis": - v = this._left_axis[0]; - break; - case "left_y_axis": - v = this._left_axis[1]; - break; - case "right_x_axis": - v = this._right_axis[0]; - break; - case "right_y_axis": - v = this._right_axis[1]; - break; - case "trigger_left": - v = this._triggers[0]; - break; - case "trigger_right": - v = this._triggers[1]; - break; - case "a_button": - v = gamepad.xbox.buttons["a"] ? 1 : 0; - break; - case "b_button": - v = gamepad.xbox.buttons["b"] ? 1 : 0; - break; - case "x_button": - v = gamepad.xbox.buttons["x"] ? 1 : 0; - break; - case "y_button": - v = gamepad.xbox.buttons["y"] ? 1 : 0; - break; - case "lb_button": - v = gamepad.xbox.buttons["lb"] ? 1 : 0; - break; - case "rb_button": - v = gamepad.xbox.buttons["rb"] ? 1 : 0; - break; - case "ls_button": - v = gamepad.xbox.buttons["ls"] ? 1 : 0; - break; - case "rs_button": - v = gamepad.xbox.buttons["rs"] ? 1 : 0; - break; - case "hat_left": - v = gamepad.xbox.hatmap & GamepadInput.LEFT; - break; - case "hat_right": - v = gamepad.xbox.hatmap & GamepadInput.RIGHT; - break; - case "hat_up": - v = gamepad.xbox.hatmap & GamepadInput.UP; - break; - case "hat_down": - v = gamepad.xbox.hatmap & GamepadInput.DOWN; - break; - case "hat": - v = gamepad.xbox.hatmap; - break; - case "start_button": - v = gamepad.xbox.buttons["start"] ? 1 : 0; - break; - case "back_button": - v = gamepad.xbox.buttons["back"] ? 1 : 0; - break; - case "button_pressed": - for ( - var j = 0; - j < this._current_buttons.length; - ++j - ) { - if ( - this._current_buttons[j] && - !this._previous_buttons[j] - ) { - this.triggerSlot( - i, - GamepadInput.buttons[j] - ); - } - } - break; - default: - break; - } - } else { - //if no gamepad is connected, output 0 - switch (output.name) { - case "button_pressed": - break; - case "left_axis": - case "right_axis": - v = GamepadInput.zero; - break; - default: - v = 0; - } - } - this.setOutputData(i, v); - } - } - }; - - GamepadInput.mapping = {a:0,b:1,x:2,y:3,lb:4,rb:5,lt:6,rt:7,back:8,start:9,ls:10,rs:11 }; - GamepadInput.mapping_array = ["a","b","x","y","lb","rb","lt","rt","back","start","ls","rs"]; - - GamepadInput.prototype.getGamepad = function() { - var getGamepads = - navigator.getGamepads || - navigator.webkitGetGamepads || - navigator.mozGetGamepads; - if (!getGamepads) { - return null; - } - var gamepads = getGamepads.call(navigator); - var gamepad = null; - - this._previous_buttons.set(this._current_buttons); - - //pick the first connected - for (var i = this.properties.gamepad_index; i < 4; i++) { - if (!gamepads[i]) { - continue; - } - gamepad = gamepads[i]; - - //xbox controller mapping - var xbox = this.xbox_mapping; - if (!xbox) { - xbox = this.xbox_mapping = { - axes: [], - buttons: {}, - hat: "", - hatmap: GamepadInput.CENTER - }; - } - - xbox.axes["lx"] = gamepad.axes[0]; - xbox.axes["ly"] = gamepad.axes[1]; - xbox.axes["rx"] = gamepad.axes[2]; - xbox.axes["ry"] = gamepad.axes[3]; - xbox.axes["ltrigger"] = gamepad.buttons[6].value; - xbox.axes["rtrigger"] = gamepad.buttons[7].value; - xbox.hat = ""; - xbox.hatmap = GamepadInput.CENTER; - - for (var j = 0; j < gamepad.buttons.length; j++) { - this._current_buttons[j] = gamepad.buttons[j].pressed; - - if(j < 12) - { - xbox.buttons[ GamepadInput.mapping_array[j] ] = gamepad.buttons[j].pressed; - if(gamepad.buttons[j].was_pressed) - this.trigger( GamepadInput.mapping_array[j] + "_button_event" ); - } - else //mapping of XBOX - switch ( j ) //I use a switch to ensure that a player with another gamepad could play - { - case 12: - if (gamepad.buttons[j].pressed) { - xbox.hat += "up"; - xbox.hatmap |= GamepadInput.UP; - } - break; - case 13: - if (gamepad.buttons[j].pressed) { - xbox.hat += "down"; - xbox.hatmap |= GamepadInput.DOWN; - } - break; - case 14: - if (gamepad.buttons[j].pressed) { - xbox.hat += "left"; - xbox.hatmap |= GamepadInput.LEFT; - } - break; - case 15: - if (gamepad.buttons[j].pressed) { - xbox.hat += "right"; - xbox.hatmap |= GamepadInput.RIGHT; - } - break; - case 16: - xbox.buttons["home"] = gamepad.buttons[j].pressed; - break; - default: - } - } - gamepad.xbox = xbox; - return gamepad; - } - }; - - GamepadInput.prototype.onDrawBackground = function(ctx) { - if (this.flags.collapsed) { - return; - } - - //render gamepad state? - var la = this._left_axis; - var ra = this._right_axis; - ctx.strokeStyle = "#88A"; - ctx.strokeRect( - (la[0] + 1) * 0.5 * this.size[0] - 4, - (la[1] + 1) * 0.5 * this.size[1] - 4, - 8, - 8 - ); - ctx.strokeStyle = "#8A8"; - ctx.strokeRect( - (ra[0] + 1) * 0.5 * this.size[0] - 4, - (ra[1] + 1) * 0.5 * this.size[1] - 4, - 8, - 8 - ); - var h = this.size[1] / this._current_buttons.length; - ctx.fillStyle = "#AEB"; - for (var i = 0; i < this._current_buttons.length; ++i) { - if (this._current_buttons[i]) { - ctx.fillRect(0, h * i, 6, h); - } - } - }; - - GamepadInput.prototype.onGetOutputs = function() { - return [ - ["left_axis", "vec2"], - ["right_axis", "vec2"], - ["left_x_axis", "number"], - ["left_y_axis", "number"], - ["right_x_axis", "number"], - ["right_y_axis", "number"], - ["trigger_left", "number"], - ["trigger_right", "number"], - ["a_button", "number"], - ["b_button", "number"], - ["x_button", "number"], - ["y_button", "number"], - ["lb_button", "number"], - ["rb_button", "number"], - ["ls_button", "number"], - ["rs_button", "number"], - ["start_button", "number"], - ["back_button", "number"], - ["a_button_event", LiteGraph.EVENT ], - ["b_button_event", LiteGraph.EVENT ], - ["x_button_event", LiteGraph.EVENT ], - ["y_button_event", LiteGraph.EVENT ], - ["lb_button_event", LiteGraph.EVENT ], - ["rb_button_event", LiteGraph.EVENT ], - ["ls_button_event", LiteGraph.EVENT ], - ["rs_button_event", LiteGraph.EVENT ], - ["start_button_event", LiteGraph.EVENT ], - ["back_button_event", LiteGraph.EVENT ], - ["hat_left", "number"], - ["hat_right", "number"], - ["hat_up", "number"], - ["hat_down", "number"], - ["hat", "number"], - ["button_pressed", LiteGraph.EVENT] - ]; - }; - - LiteGraph.registerNodeType("input/gamepad", GamepadInput); - -})(this); diff --git a/src/nodes/interface.js b/src/nodes/interface.js deleted file mode 100755 index a73c098cd..000000000 --- a/src/nodes/interface.js +++ /dev/null @@ -1,798 +0,0 @@ -//widgets -(function(global) { - var LiteGraph = global.LiteGraph; - - /* Button ****************/ - - function WidgetButton() { - this.addOutput("", LiteGraph.EVENT); - this.addOutput("", "boolean"); - this.addProperty("text", "click me"); - this.addProperty("font_size", 30); - this.addProperty("message", ""); - this.size = [164, 84]; - this.clicked = false; - } - - WidgetButton.title = "Button"; - WidgetButton.desc = "Triggers an event"; - - WidgetButton.font = "Arial"; - WidgetButton.prototype.onDrawForeground = function(ctx) { - if (this.flags.collapsed) { - return; - } - var margin = 10; - ctx.fillStyle = "black"; - ctx.fillRect( - margin + 1, - margin + 1, - this.size[0] - margin * 2, - this.size[1] - margin * 2 - ); - ctx.fillStyle = "#AAF"; - ctx.fillRect( - margin - 1, - margin - 1, - this.size[0] - margin * 2, - this.size[1] - margin * 2 - ); - ctx.fillStyle = this.clicked - ? "white" - : this.mouseOver - ? "#668" - : "#334"; - ctx.fillRect( - margin, - margin, - this.size[0] - margin * 2, - this.size[1] - margin * 2 - ); - - if (this.properties.text || this.properties.text === 0) { - var font_size = this.properties.font_size || 30; - ctx.textAlign = "center"; - ctx.fillStyle = this.clicked ? "black" : "white"; - ctx.font = font_size + "px " + WidgetButton.font; - ctx.fillText( - this.properties.text, - this.size[0] * 0.5, - this.size[1] * 0.5 + font_size * 0.3 - ); - ctx.textAlign = "left"; - } - }; - - WidgetButton.prototype.onMouseDown = function(e, local_pos) { - if ( - local_pos[0] > 1 && - local_pos[1] > 1 && - local_pos[0] < this.size[0] - 2 && - local_pos[1] < this.size[1] - 2 - ) { - this.clicked = true; - this.setOutputData(1, this.clicked); - this.triggerSlot(0, this.properties.message); - return true; - } - }; - - WidgetButton.prototype.onExecute = function() { - this.setOutputData(1, this.clicked); - }; - - WidgetButton.prototype.onMouseUp = function(e) { - this.clicked = false; - }; - - LiteGraph.registerNodeType("widget/button", WidgetButton); - - function WidgetToggle() { - this.addInput("", "boolean"); - this.addInput("e", LiteGraph.ACTION); - this.addOutput("v", "boolean"); - this.addOutput("e", LiteGraph.EVENT); - this.properties = { font: "", value: false }; - this.size = [160, 44]; - } - - WidgetToggle.title = "Toggle"; - WidgetToggle.desc = "Toggles between true or false"; - - WidgetToggle.prototype.onDrawForeground = function(ctx) { - if (this.flags.collapsed) { - return; - } - - var size = this.size[1] * 0.5; - var margin = 0.25; - var h = this.size[1] * 0.8; - ctx.font = this.properties.font || (size * 0.8).toFixed(0) + "px Arial"; - var w = ctx.measureText(this.title).width; - var x = (this.size[0] - (w + size)) * 0.5; - - ctx.fillStyle = "#AAA"; - ctx.fillRect(x, h - size, size, size); - - ctx.fillStyle = this.properties.value ? "#AEF" : "#000"; - ctx.fillRect( - x + size * margin, - h - size + size * margin, - size * (1 - margin * 2), - size * (1 - margin * 2) - ); - - ctx.textAlign = "left"; - ctx.fillStyle = "#AAA"; - ctx.fillText(this.title, size * 1.2 + x, h * 0.85); - ctx.textAlign = "left"; - }; - - WidgetToggle.prototype.onAction = function(action) { - this.properties.value = !this.properties.value; - this.trigger("e", this.properties.value); - }; - - WidgetToggle.prototype.onExecute = function() { - var v = this.getInputData(0); - if (v != null) { - this.properties.value = v; - } - this.setOutputData(0, this.properties.value); - }; - - WidgetToggle.prototype.onMouseDown = function(e, local_pos) { - if ( - local_pos[0] > 1 && - local_pos[1] > 1 && - local_pos[0] < this.size[0] - 2 && - local_pos[1] < this.size[1] - 2 - ) { - this.properties.value = !this.properties.value; - this.graph._version++; - this.trigger("e", this.properties.value); - return true; - } - }; - - LiteGraph.registerNodeType("widget/toggle", WidgetToggle); - - /* Number ****************/ - - function WidgetNumber() { - this.addOutput("", "number"); - this.size = [80, 60]; - this.properties = { min: -1000, max: 1000, value: 1, step: 1 }; - this.old_y = -1; - this._remainder = 0; - this._precision = 0; - this.mouse_captured = false; - } - - WidgetNumber.title = "Number"; - WidgetNumber.desc = "Widget to select number value"; - - WidgetNumber.pixels_threshold = 10; - WidgetNumber.markers_color = "#666"; - - WidgetNumber.prototype.onDrawForeground = function(ctx) { - var x = this.size[0] * 0.5; - var h = this.size[1]; - if (h > 30) { - ctx.fillStyle = WidgetNumber.markers_color; - ctx.beginPath(); - ctx.moveTo(x, h * 0.1); - ctx.lineTo(x + h * 0.1, h * 0.2); - ctx.lineTo(x + h * -0.1, h * 0.2); - ctx.fill(); - ctx.beginPath(); - ctx.moveTo(x, h * 0.9); - ctx.lineTo(x + h * 0.1, h * 0.8); - ctx.lineTo(x + h * -0.1, h * 0.8); - ctx.fill(); - ctx.font = (h * 0.7).toFixed(1) + "px Arial"; - } else { - ctx.font = (h * 0.8).toFixed(1) + "px Arial"; - } - - ctx.textAlign = "center"; - ctx.font = (h * 0.7).toFixed(1) + "px Arial"; - ctx.fillStyle = "#EEE"; - ctx.fillText( - this.properties.value.toFixed(this._precision), - x, - h * 0.75 - ); - }; - - WidgetNumber.prototype.onExecute = function() { - this.setOutputData(0, this.properties.value); - }; - - WidgetNumber.prototype.onPropertyChanged = function(name, value) { - var t = (this.properties.step + "").split("."); - this._precision = t.length > 1 ? t[1].length : 0; - }; - - WidgetNumber.prototype.onMouseDown = function(e, pos) { - if (pos[1] < 0) { - return; - } - - this.old_y = e.canvasY; - this.captureInput(true); - this.mouse_captured = true; - - return true; - }; - - WidgetNumber.prototype.onMouseMove = function(e) { - if (!this.mouse_captured) { - return; - } - - var delta = this.old_y - e.canvasY; - if (e.shiftKey) { - delta *= 10; - } - if (e.metaKey || e.altKey) { - delta *= 0.1; - } - this.old_y = e.canvasY; - - var steps = this._remainder + delta / WidgetNumber.pixels_threshold; - this._remainder = steps % 1; - steps = steps | 0; - - var v = clamp( - this.properties.value + steps * this.properties.step, - this.properties.min, - this.properties.max - ); - this.properties.value = v; - this.graph._version++; - this.setDirtyCanvas(true); - }; - - WidgetNumber.prototype.onMouseUp = function(e, pos) { - if (e.click_time < 200) { - var steps = pos[1] > this.size[1] * 0.5 ? -1 : 1; - this.properties.value = clamp( - this.properties.value + steps * this.properties.step, - this.properties.min, - this.properties.max - ); - this.graph._version++; - this.setDirtyCanvas(true); - } - - if (this.mouse_captured) { - this.mouse_captured = false; - this.captureInput(false); - } - }; - - LiteGraph.registerNodeType("widget/number", WidgetNumber); - - - /* Combo ****************/ - - function WidgetCombo() { - this.addOutput("", "string"); - this.addOutput("change", LiteGraph.EVENT); - this.size = [80, 60]; - this.properties = { value: "A", values:"A;B;C" }; - this.old_y = -1; - this.mouse_captured = false; - this._values = this.properties.values.split(";"); - var that = this; - this.widgets_up = true; - this.widget = this.addWidget("combo","", this.properties.value, function(v){ - that.properties.value = v; - that.triggerSlot(1, v); - }, { property: "value", values: this._values } ); - } - - WidgetCombo.title = "Combo"; - WidgetCombo.desc = "Widget to select from a list"; - - WidgetCombo.prototype.onExecute = function() { - this.setOutputData( 0, this.properties.value ); - }; - - WidgetCombo.prototype.onPropertyChanged = function(name, value) { - if(name == "values") - { - this._values = value.split(";"); - this.widget.options.values = this._values; - } - else if(name == "value") - { - this.widget.value = value; - } - }; - - LiteGraph.registerNodeType("widget/combo", WidgetCombo); - - - /* Knob ****************/ - - function WidgetKnob() { - this.addOutput("", "number"); - this.size = [64, 84]; - this.properties = { - min: 0, - max: 1, - value: 0.5, - color: "#7AF", - precision: 2 - }; - this.value = -1; - } - - WidgetKnob.title = "Knob"; - WidgetKnob.desc = "Circular controller"; - WidgetKnob.size = [80, 100]; - - WidgetKnob.prototype.onDrawForeground = function(ctx) { - if (this.flags.collapsed) { - return; - } - - if (this.value == -1) { - this.value = - (this.properties.value - this.properties.min) / - (this.properties.max - this.properties.min); - } - - var center_x = this.size[0] * 0.5; - var center_y = this.size[1] * 0.5; - var radius = Math.min(this.size[0], this.size[1]) * 0.5 - 5; - var w = Math.floor(radius * 0.05); - - ctx.globalAlpha = 1; - ctx.save(); - ctx.translate(center_x, center_y); - ctx.rotate(Math.PI * 0.75); - - //bg - ctx.fillStyle = "rgba(0,0,0,0.5)"; - ctx.beginPath(); - ctx.moveTo(0, 0); - ctx.arc(0, 0, radius, 0, Math.PI * 1.5); - ctx.fill(); - - //value - ctx.strokeStyle = "black"; - ctx.fillStyle = this.properties.color; - ctx.lineWidth = 2; - ctx.beginPath(); - ctx.moveTo(0, 0); - ctx.arc( - 0, - 0, - radius - 4, - 0, - Math.PI * 1.5 * Math.max(0.01, this.value) - ); - ctx.closePath(); - ctx.fill(); - //ctx.stroke(); - ctx.lineWidth = 1; - ctx.globalAlpha = 1; - ctx.restore(); - - //inner - ctx.fillStyle = "black"; - ctx.beginPath(); - ctx.arc(center_x, center_y, radius * 0.75, 0, Math.PI * 2, true); - ctx.fill(); - - //miniball - ctx.fillStyle = this.mouseOver ? "white" : this.properties.color; - ctx.beginPath(); - var angle = this.value * Math.PI * 1.5 + Math.PI * 0.75; - ctx.arc( - center_x + Math.cos(angle) * radius * 0.65, - center_y + Math.sin(angle) * radius * 0.65, - radius * 0.05, - 0, - Math.PI * 2, - true - ); - ctx.fill(); - - //text - ctx.fillStyle = this.mouseOver ? "white" : "#AAA"; - ctx.font = Math.floor(radius * 0.5) + "px Arial"; - ctx.textAlign = "center"; - ctx.fillText( - this.properties.value.toFixed(this.properties.precision), - center_x, - center_y + radius * 0.15 - ); - }; - - WidgetKnob.prototype.onExecute = function() { - this.setOutputData(0, this.properties.value); - this.boxcolor = LiteGraph.colorToString([ - this.value, - this.value, - this.value - ]); - }; - - WidgetKnob.prototype.onMouseDown = function(e) { - this.center = [this.size[0] * 0.5, this.size[1] * 0.5 + 20]; - this.radius = this.size[0] * 0.5; - if ( - e.canvasY - this.pos[1] < 20 || - LiteGraph.distance( - [e.canvasX, e.canvasY], - [this.pos[0] + this.center[0], this.pos[1] + this.center[1]] - ) > this.radius - ) { - return false; - } - this.oldmouse = [e.canvasX - this.pos[0], e.canvasY - this.pos[1]]; - this.captureInput(true); - return true; - }; - - WidgetKnob.prototype.onMouseMove = function(e) { - if (!this.oldmouse) { - return; - } - - var m = [e.canvasX - this.pos[0], e.canvasY - this.pos[1]]; - - var v = this.value; - v -= (m[1] - this.oldmouse[1]) * 0.01; - if (v > 1.0) { - v = 1.0; - } else if (v < 0.0) { - v = 0.0; - } - this.value = v; - this.properties.value = - this.properties.min + - (this.properties.max - this.properties.min) * this.value; - this.oldmouse = m; - this.setDirtyCanvas(true); - }; - - WidgetKnob.prototype.onMouseUp = function(e) { - if (this.oldmouse) { - this.oldmouse = null; - this.captureInput(false); - } - }; - - WidgetKnob.prototype.onPropertyChanged = function(name, value) { - if (name == "min" || name == "max" || name == "value") { - this.properties[name] = parseFloat(value); - return true; //block - } - }; - - LiteGraph.registerNodeType("widget/knob", WidgetKnob); - - //Show value inside the debug console - function WidgetSliderGUI() { - this.addOutput("", "number"); - this.properties = { - value: 0.5, - min: 0, - max: 1, - text: "V" - }; - var that = this; - this.size = [140, 40]; - this.slider = this.addWidget( - "slider", - "V", - this.properties.value, - function(v) { - that.properties.value = v; - }, - this.properties - ); - this.widgets_up = true; - } - - WidgetSliderGUI.title = "Inner Slider"; - - WidgetSliderGUI.prototype.onPropertyChanged = function(name, value) { - if (name == "value") { - this.slider.value = value; - } - }; - - WidgetSliderGUI.prototype.onExecute = function() { - this.setOutputData(0, this.properties.value); - }; - - LiteGraph.registerNodeType("widget/internal_slider", WidgetSliderGUI); - - //Widget H SLIDER - function WidgetHSlider() { - this.size = [160, 26]; - this.addOutput("", "number"); - this.properties = { color: "#7AF", min: 0, max: 1, value: 0.5 }; - this.value = -1; - } - - WidgetHSlider.title = "H.Slider"; - WidgetHSlider.desc = "Linear slider controller"; - - WidgetHSlider.prototype.onDrawForeground = function(ctx) { - if (this.value == -1) { - this.value = - (this.properties.value - this.properties.min) / - (this.properties.max - this.properties.min); - } - - //border - ctx.globalAlpha = 1; - ctx.lineWidth = 1; - ctx.fillStyle = "#000"; - ctx.fillRect(2, 2, this.size[0] - 4, this.size[1] - 4); - - ctx.fillStyle = this.properties.color; - ctx.beginPath(); - ctx.rect(4, 4, (this.size[0] - 8) * this.value, this.size[1] - 8); - ctx.fill(); - }; - - WidgetHSlider.prototype.onExecute = function() { - this.properties.value = - this.properties.min + - (this.properties.max - this.properties.min) * this.value; - this.setOutputData(0, this.properties.value); - this.boxcolor = LiteGraph.colorToString([ - this.value, - this.value, - this.value - ]); - }; - - WidgetHSlider.prototype.onMouseDown = function(e) { - if (e.canvasY - this.pos[1] < 0) { - return false; - } - - this.oldmouse = [e.canvasX - this.pos[0], e.canvasY - this.pos[1]]; - this.captureInput(true); - return true; - }; - - WidgetHSlider.prototype.onMouseMove = function(e) { - if (!this.oldmouse) { - return; - } - - var m = [e.canvasX - this.pos[0], e.canvasY - this.pos[1]]; - - var v = this.value; - var delta = m[0] - this.oldmouse[0]; - v += delta / this.size[0]; - if (v > 1.0) { - v = 1.0; - } else if (v < 0.0) { - v = 0.0; - } - - this.value = v; - - this.oldmouse = m; - this.setDirtyCanvas(true); - }; - - WidgetHSlider.prototype.onMouseUp = function(e) { - this.oldmouse = null; - this.captureInput(false); - }; - - WidgetHSlider.prototype.onMouseLeave = function(e) { - //this.oldmouse = null; - }; - - LiteGraph.registerNodeType("widget/hslider", WidgetHSlider); - - function WidgetProgress() { - this.size = [160, 26]; - this.addInput("", "number"); - this.properties = { min: 0, max: 1, value: 0, color: "#AAF" }; - } - - WidgetProgress.title = "Progress"; - WidgetProgress.desc = "Shows data in linear progress"; - - WidgetProgress.prototype.onExecute = function() { - var v = this.getInputData(0); - if (v != undefined) { - this.properties["value"] = v; - } - }; - - WidgetProgress.prototype.onDrawForeground = function(ctx) { - //border - ctx.lineWidth = 1; - ctx.fillStyle = this.properties.color; - var v = - (this.properties.value - this.properties.min) / - (this.properties.max - this.properties.min); - v = Math.min(1, v); - v = Math.max(0, v); - ctx.fillRect(2, 2, (this.size[0] - 4) * v, this.size[1] - 4); - }; - - LiteGraph.registerNodeType("widget/progress", WidgetProgress); - - function WidgetText() { - this.addInputs("", 0); - this.properties = { - value: "...", - font: "Arial", - fontsize: 18, - color: "#AAA", - align: "left", - glowSize: 0, - decimals: 1 - }; - } - - WidgetText.title = "Text"; - WidgetText.desc = "Shows the input value"; - WidgetText.widgets = [ - { name: "resize", text: "Resize box", type: "button" }, - { name: "led_text", text: "LED", type: "minibutton" }, - { name: "normal_text", text: "Normal", type: "minibutton" } - ]; - - WidgetText.prototype.onDrawForeground = function(ctx) { - //ctx.fillStyle="#000"; - //ctx.fillRect(0,0,100,60); - ctx.fillStyle = this.properties["color"]; - var v = this.properties["value"]; - - if (this.properties["glowSize"]) { - ctx.shadowColor = this.properties.color; - ctx.shadowOffsetX = 0; - ctx.shadowOffsetY = 0; - ctx.shadowBlur = this.properties["glowSize"]; - } else { - ctx.shadowColor = "transparent"; - } - - var fontsize = this.properties["fontsize"]; - - ctx.textAlign = this.properties["align"]; - ctx.font = fontsize.toString() + "px " + this.properties["font"]; - this.str = - typeof v == "number" ? v.toFixed(this.properties["decimals"]) : v; - - if (typeof this.str == "string") { - var lines = this.str.replace(/[\r\n]/g, "\\n").split("\\n"); - for (var i=0; i < lines.length; i++) { - ctx.fillText( - lines[i], - this.properties["align"] == "left" ? 15 : this.size[0] - 15, - fontsize * -0.15 + fontsize * (parseInt(i) + 1) - ); - } - } - - ctx.shadowColor = "transparent"; - this.last_ctx = ctx; - ctx.textAlign = "left"; - }; - - WidgetText.prototype.onExecute = function() { - var v = this.getInputData(0); - if (v != null) { - this.properties["value"] = v; - } - //this.setDirtyCanvas(true); - }; - - WidgetText.prototype.resize = function() { - if (!this.last_ctx) { - return; - } - - var lines = this.str.split("\\n"); - this.last_ctx.font = - this.properties["fontsize"] + "px " + this.properties["font"]; - var max = 0; - for (var i=0; i < lines.length; i++) { - var w = this.last_ctx.measureText(lines[i]).width; - if (max < w) { - max = w; - } - } - this.size[0] = max + 20; - this.size[1] = 4 + lines.length * this.properties["fontsize"]; - - this.setDirtyCanvas(true); - }; - - WidgetText.prototype.onPropertyChanged = function(name, value) { - this.properties[name] = value; - this.str = typeof value == "number" ? value.toFixed(3) : value; - //this.resize(); - return true; - }; - - LiteGraph.registerNodeType("widget/text", WidgetText); - - function WidgetPanel() { - this.size = [200, 100]; - this.properties = { - borderColor: "#ffffff", - bgcolorTop: "#f0f0f0", - bgcolorBottom: "#e0e0e0", - shadowSize: 2, - borderRadius: 3 - }; - } - - WidgetPanel.title = "Panel"; - WidgetPanel.desc = "Non interactive panel"; - WidgetPanel.widgets = [{ name: "update", text: "Update", type: "button" }]; - - WidgetPanel.prototype.createGradient = function(ctx) { - if ( - this.properties["bgcolorTop"] == "" || - this.properties["bgcolorBottom"] == "" - ) { - this.lineargradient = 0; - return; - } - - this.lineargradient = ctx.createLinearGradient(0, 0, 0, this.size[1]); - this.lineargradient.addColorStop(0, this.properties["bgcolorTop"]); - this.lineargradient.addColorStop(1, this.properties["bgcolorBottom"]); - }; - - WidgetPanel.prototype.onDrawForeground = function(ctx) { - if (this.flags.collapsed) { - return; - } - - if (this.lineargradient == null) { - this.createGradient(ctx); - } - - if (!this.lineargradient) { - return; - } - - ctx.lineWidth = 1; - ctx.strokeStyle = this.properties["borderColor"]; - //ctx.fillStyle = "#ebebeb"; - ctx.fillStyle = this.lineargradient; - - if (this.properties["shadowSize"]) { - ctx.shadowColor = "#000"; - ctx.shadowOffsetX = 0; - ctx.shadowOffsetY = 0; - ctx.shadowBlur = this.properties["shadowSize"]; - } else { - ctx.shadowColor = "transparent"; - } - - ctx.roundRect( - 0, - 0, - this.size[0] - 1, - this.size[1] - 1, - this.properties["shadowSize"] - ); - ctx.fill(); - ctx.shadowColor = "transparent"; - ctx.stroke(); - }; - - LiteGraph.registerNodeType("widget/panel", WidgetPanel); -})(this); diff --git a/src/nodes/logic.js b/src/nodes/logic.js deleted file mode 100755 index 1a80a8ecf..000000000 --- a/src/nodes/logic.js +++ /dev/null @@ -1,201 +0,0 @@ -(function(global) { - var LiteGraph = global.LiteGraph; - - function Selector() { - this.addInput("sel", "number"); - this.addInput("A"); - this.addInput("B"); - this.addInput("C"); - this.addInput("D"); - this.addOutput("out"); - - this.selected = 0; - } - - Selector.title = "Selector"; - Selector.desc = "selects an output"; - - Selector.prototype.onDrawBackground = function(ctx) { - if (this.flags.collapsed) { - return; - } - ctx.fillStyle = "#AFB"; - var y = (this.selected + 1) * LiteGraph.NODE_SLOT_HEIGHT + 6; - ctx.beginPath(); - ctx.moveTo(50, y); - ctx.lineTo(50, y + LiteGraph.NODE_SLOT_HEIGHT); - ctx.lineTo(34, y + LiteGraph.NODE_SLOT_HEIGHT * 0.5); - ctx.fill(); - }; - - Selector.prototype.onExecute = function() { - var sel = this.getInputData(0); - if (sel == null || sel.constructor !== Number) - sel = 0; - this.selected = sel = Math.round(sel) % (this.inputs.length - 1); - var v = this.getInputData(sel + 1); - if (v !== undefined) { - this.setOutputData(0, v); - } - }; - - Selector.prototype.onGetInputs = function() { - return [["E", 0], ["F", 0], ["G", 0], ["H", 0]]; - }; - - LiteGraph.registerNodeType("logic/selector", Selector); - - function Sequence() { - this.properties = { - sequence: "A,B,C" - }; - this.addInput("index", "number"); - this.addInput("seq"); - this.addOutput("out"); - - this.index = 0; - this.values = this.properties.sequence.split(","); - } - - Sequence.title = "Sequence"; - Sequence.desc = "select one element from a sequence from a string"; - - Sequence.prototype.onPropertyChanged = function(name, value) { - if (name == "sequence") { - this.values = value.split(","); - } - }; - - Sequence.prototype.onExecute = function() { - var seq = this.getInputData(1); - if (seq && seq != this.current_sequence) { - this.values = seq.split(","); - this.current_sequence = seq; - } - var index = this.getInputData(0); - if (index == null) { - index = 0; - } - this.index = index = Math.round(index) % this.values.length; - - this.setOutputData(0, this.values[index]); - }; - - LiteGraph.registerNodeType("logic/sequence", Sequence); - - - function logicAnd(){ - this.properties = { }; - this.addInput("a", "boolean"); - this.addInput("b", "boolean"); - this.addOutput("out", "boolean"); - } - logicAnd.title = "AND"; - logicAnd.desc = "Return true if all inputs are true"; - logicAnd.prototype.onExecute = function() { - var ret = true; - for (var inX in this.inputs){ - if (!this.getInputData(inX)){ - var ret = false; - break; - } - } - this.setOutputData(0, ret); - }; - logicAnd.prototype.onGetInputs = function() { - return [ - ["and", "boolean"] - ]; - }; - LiteGraph.registerNodeType("logic/AND", logicAnd); - - - function logicOr(){ - this.properties = { }; - this.addInput("a", "boolean"); - this.addInput("b", "boolean"); - this.addOutput("out", "boolean"); - } - logicOr.title = "OR"; - logicOr.desc = "Return true if at least one input is true"; - logicOr.prototype.onExecute = function() { - var ret = false; - for (var inX in this.inputs){ - if (this.getInputData(inX)){ - ret = true; - break; - } - } - this.setOutputData(0, ret); - }; - logicOr.prototype.onGetInputs = function() { - return [ - ["or", "boolean"] - ]; - }; - LiteGraph.registerNodeType("logic/OR", logicOr); - - - function logicNot(){ - this.properties = { }; - this.addInput("in", "boolean"); - this.addOutput("out", "boolean"); - } - logicNot.title = "NOT"; - logicNot.desc = "Return the logical negation"; - logicNot.prototype.onExecute = function() { - var ret = !this.getInputData(0); - this.setOutputData(0, ret); - }; - LiteGraph.registerNodeType("logic/NOT", logicNot); - - - function logicCompare(){ - this.properties = { }; - this.addInput("a", "boolean"); - this.addInput("b", "boolean"); - this.addOutput("out", "boolean"); - } - logicCompare.title = "bool == bool"; - logicCompare.desc = "Compare for logical equality"; - logicCompare.prototype.onExecute = function() { - var last = null; - var ret = true; - for (var inX in this.inputs){ - if (last === null) last = this.getInputData(inX); - else - if (last != this.getInputData(inX)){ - ret = false; - break; - } - } - this.setOutputData(0, ret); - }; - logicCompare.prototype.onGetInputs = function() { - return [ - ["bool", "boolean"] - ]; - }; - LiteGraph.registerNodeType("logic/CompareBool", logicCompare); - - - function logicBranch(){ - this.properties = { }; - this.addInput("onTrigger", LiteGraph.ACTION); - this.addInput("condition", "boolean"); - this.addOutput("true", LiteGraph.EVENT); - this.addOutput("false", LiteGraph.EVENT); - this.mode = LiteGraph.ON_TRIGGER; - } - logicBranch.title = "Branch"; - logicBranch.desc = "Branch execution on condition"; - logicBranch.prototype.onExecute = function(param, options) { - var condtition = this.getInputData(1); - if (condtition){ - this.triggerSlot(0); - }else{ - this.triggerSlot(1); - } - }; - LiteGraph.registerNodeType("logic/IF", logicBranch); -})(this); diff --git a/src/nodes/math.js b/src/nodes/math.js deleted file mode 100755 index d60e3bd67..000000000 --- a/src/nodes/math.js +++ /dev/null @@ -1,1333 +0,0 @@ -(function(global) { - var LiteGraph = global.LiteGraph; - - //Converter - function Converter() { - this.addInput("in", 0); - this.addOutput("out", 0); - this.size = [80, 30]; - } - - Converter.title = "Converter"; - Converter.desc = "type A to type B"; - - Converter.prototype.onExecute = function() { - var v = this.getInputData(0); - if (v == null) { - return; - } - - if (this.outputs) { - for (var i = 0; i < this.outputs.length; i++) { - var output = this.outputs[i]; - if (!output.links || !output.links.length) { - continue; - } - - var result = null; - switch (output.name) { - case "number": - result = v.length ? v[0] : parseFloat(v); - break; - case "vec2": - case "vec3": - case "vec4": - var result = null; - var count = 1; - switch (output.name) { - case "vec2": - count = 2; - break; - case "vec3": - count = 3; - break; - case "vec4": - count = 4; - break; - } - - var result = new Float32Array(count); - if (v.length) { - for ( - var j = 0; - j < v.length && j < result.length; - j++ - ) { - result[j] = v[j]; - } - } else { - result[0] = parseFloat(v); - } - break; - } - this.setOutputData(i, result); - } - } - }; - - Converter.prototype.onGetOutputs = function() { - return [ - ["number", "number"], - ["vec2", "vec2"], - ["vec3", "vec3"], - ["vec4", "vec4"] - ]; - }; - - LiteGraph.registerNodeType("math/converter", Converter); - - //Bypass - function Bypass() { - this.addInput("in"); - this.addOutput("out"); - this.size = [80, 30]; - } - - Bypass.title = "Bypass"; - Bypass.desc = "removes the type"; - - Bypass.prototype.onExecute = function() { - var v = this.getInputData(0); - this.setOutputData(0, v); - }; - - LiteGraph.registerNodeType("math/bypass", Bypass); - - function ToNumber() { - this.addInput("in"); - this.addOutput("out"); - } - - ToNumber.title = "to Number"; - ToNumber.desc = "Cast to number"; - - ToNumber.prototype.onExecute = function() { - var v = this.getInputData(0); - this.setOutputData(0, Number(v)); - }; - - LiteGraph.registerNodeType("math/to_number", ToNumber); - - function MathRange() { - this.addInput("in", "number", { locked: true }); - this.addOutput("out", "number", { locked: true }); - this.addOutput("clamped", "number", { locked: true }); - - this.addProperty("in", 0); - this.addProperty("in_min", 0); - this.addProperty("in_max", 1); - this.addProperty("out_min", 0); - this.addProperty("out_max", 1); - - this.size = [120, 50]; - } - - MathRange.title = "Range"; - MathRange.desc = "Convert a number from one range to another"; - - MathRange.prototype.getTitle = function() { - if (this.flags.collapsed) { - return (this._last_v || 0).toFixed(2); - } - return this.title; - }; - - MathRange.prototype.onExecute = function() { - if (this.inputs) { - for (var i = 0; i < this.inputs.length; i++) { - var input = this.inputs[i]; - var v = this.getInputData(i); - if (v === undefined) { - continue; - } - this.properties[input.name] = v; - } - } - - var v = this.properties["in"]; - if (v === undefined || v === null || v.constructor !== Number) { - v = 0; - } - - var in_min = this.properties.in_min; - var in_max = this.properties.in_max; - var out_min = this.properties.out_min; - var out_max = this.properties.out_max; - /* - if( in_min > in_max ) - { - in_min = in_max; - in_max = this.properties.in_min; - } - if( out_min > out_max ) - { - out_min = out_max; - out_max = this.properties.out_min; - } - */ - - this._last_v = ((v - in_min) / (in_max - in_min)) * (out_max - out_min) + out_min; - this.setOutputData(0, this._last_v); - this.setOutputData(1, clamp( this._last_v, out_min, out_max )); - }; - - MathRange.prototype.onDrawBackground = function(ctx) { - //show the current value - if (this._last_v) { - this.outputs[0].label = this._last_v.toFixed(3); - } else { - this.outputs[0].label = "?"; - } - }; - - MathRange.prototype.onGetInputs = function() { - return [ - ["in_min", "number"], - ["in_max", "number"], - ["out_min", "number"], - ["out_max", "number"] - ]; - }; - - LiteGraph.registerNodeType("math/range", MathRange); - - function MathRand() { - this.addOutput("value", "number"); - this.addProperty("min", 0); - this.addProperty("max", 1); - this.size = [80, 30]; - } - - MathRand.title = "Rand"; - MathRand.desc = "Random number"; - - MathRand.prototype.onExecute = function() { - if (this.inputs) { - for (var i = 0; i < this.inputs.length; i++) { - var input = this.inputs[i]; - var v = this.getInputData(i); - if (v === undefined) { - continue; - } - this.properties[input.name] = v; - } - } - - var min = this.properties.min; - var max = this.properties.max; - this._last_v = Math.random() * (max - min) + min; - this.setOutputData(0, this._last_v); - }; - - MathRand.prototype.onDrawBackground = function(ctx) { - //show the current value - this.outputs[0].label = (this._last_v || 0).toFixed(3); - }; - - MathRand.prototype.onGetInputs = function() { - return [["min", "number"], ["max", "number"]]; - }; - - LiteGraph.registerNodeType("math/rand", MathRand); - - //basic continuous noise - function MathNoise() { - this.addInput("in", "number"); - this.addOutput("out", "number"); - this.addProperty("min", 0); - this.addProperty("max", 1); - this.addProperty("smooth", true); - this.addProperty("seed", 0); - this.addProperty("octaves", 1); - this.addProperty("persistence", 0.8); - this.addProperty("speed", 1); - this.size = [90, 30]; - } - - MathNoise.title = "Noise"; - MathNoise.desc = "Random number with temporal continuity"; - MathNoise.data = null; - - MathNoise.getValue = function(f, smooth) { - if (!MathNoise.data) { - MathNoise.data = new Float32Array(1024); - for (var i = 0; i < MathNoise.data.length; ++i) { - MathNoise.data[i] = Math.random(); - } - } - f = f % 1024; - if (f < 0) { - f += 1024; - } - var f_min = Math.floor(f); - var f = f - f_min; - var r1 = MathNoise.data[f_min]; - var r2 = MathNoise.data[f_min == 1023 ? 0 : f_min + 1]; - if (smooth) { - f = f * f * f * (f * (f * 6.0 - 15.0) + 10.0); - } - return r1 * (1 - f) + r2 * f; - }; - - MathNoise.prototype.onExecute = function() { - var f = this.getInputData(0) || 0; - var iterations = this.properties.octaves || 1; - var r = 0; - var amp = 1; - var seed = this.properties.seed || 0; - f += seed; - var speed = this.properties.speed || 1; - var total_amp = 0; - for(var i = 0; i < iterations; ++i) - { - r += MathNoise.getValue(f * (1+i) * speed, this.properties.smooth) * amp; - total_amp += amp; - amp *= this.properties.persistence; - if(amp < 0.001) - break; - } - r /= total_amp; - var min = this.properties.min; - var max = this.properties.max; - this._last_v = r * (max - min) + min; - this.setOutputData(0, this._last_v); - }; - - MathNoise.prototype.onDrawBackground = function(ctx) { - //show the current value - this.outputs[0].label = (this._last_v || 0).toFixed(3); - }; - - LiteGraph.registerNodeType("math/noise", MathNoise); - - //generates spikes every random time - function MathSpikes() { - this.addOutput("out", "number"); - this.addProperty("min_time", 1); - this.addProperty("max_time", 2); - this.addProperty("duration", 0.2); - this.size = [90, 30]; - this._remaining_time = 0; - this._blink_time = 0; - } - - MathSpikes.title = "Spikes"; - MathSpikes.desc = "spike every random time"; - - MathSpikes.prototype.onExecute = function() { - var dt = this.graph.elapsed_time; //in secs - - this._remaining_time -= dt; - this._blink_time -= dt; - - var v = 0; - if (this._blink_time > 0) { - var f = this._blink_time / this.properties.duration; - v = 1 / (Math.pow(f * 8 - 4, 4) + 1); - } - - if (this._remaining_time < 0) { - this._remaining_time = - Math.random() * - (this.properties.max_time - this.properties.min_time) + - this.properties.min_time; - this._blink_time = this.properties.duration; - this.boxcolor = "#FFF"; - } else { - this.boxcolor = "#000"; - } - this.setOutputData(0, v); - }; - - LiteGraph.registerNodeType("math/spikes", MathSpikes); - - //Math clamp - function MathClamp() { - this.addInput("in", "number"); - this.addOutput("out", "number"); - this.size = [80, 30]; - this.addProperty("min", 0); - this.addProperty("max", 1); - } - - MathClamp.title = "Clamp"; - MathClamp.desc = "Clamp number between min and max"; - //MathClamp.filter = "shader"; - - MathClamp.prototype.onExecute = function() { - var v = this.getInputData(0); - if (v == null) { - return; - } - v = Math.max(this.properties.min, v); - v = Math.min(this.properties.max, v); - this.setOutputData(0, v); - }; - - MathClamp.prototype.getCode = function(lang) { - var code = ""; - if (this.isInputConnected(0)) { - code += - "clamp({{0}}," + - this.properties.min + - "," + - this.properties.max + - ")"; - } - return code; - }; - - LiteGraph.registerNodeType("math/clamp", MathClamp); - - //Math ABS - function MathLerp() { - this.properties = { f: 0.5 }; - this.addInput("A", "number"); - this.addInput("B", "number"); - - this.addOutput("out", "number"); - } - - MathLerp.title = "Lerp"; - MathLerp.desc = "Linear Interpolation"; - - MathLerp.prototype.onExecute = function() { - var v1 = this.getInputData(0); - if (v1 == null) { - v1 = 0; - } - var v2 = this.getInputData(1); - if (v2 == null) { - v2 = 0; - } - - var f = this.properties.f; - - var _f = this.getInputData(2); - if (_f !== undefined) { - f = _f; - } - - this.setOutputData(0, v1 * (1 - f) + v2 * f); - }; - - MathLerp.prototype.onGetInputs = function() { - return [["f", "number"]]; - }; - - LiteGraph.registerNodeType("math/lerp", MathLerp); - - //Math ABS - function MathAbs() { - this.addInput("in", "number"); - this.addOutput("out", "number"); - this.size = [80, 30]; - } - - MathAbs.title = "Abs"; - MathAbs.desc = "Absolute"; - - MathAbs.prototype.onExecute = function() { - var v = this.getInputData(0); - if (v == null) { - return; - } - this.setOutputData(0, Math.abs(v)); - }; - - LiteGraph.registerNodeType("math/abs", MathAbs); - - //Math Floor - function MathFloor() { - this.addInput("in", "number"); - this.addOutput("out", "number"); - this.size = [80, 30]; - } - - MathFloor.title = "Floor"; - MathFloor.desc = "Floor number to remove fractional part"; - - MathFloor.prototype.onExecute = function() { - var v = this.getInputData(0); - if (v == null) { - return; - } - this.setOutputData(0, Math.floor(v)); - }; - - LiteGraph.registerNodeType("math/floor", MathFloor); - - //Math frac - function MathFrac() { - this.addInput("in", "number"); - this.addOutput("out", "number"); - this.size = [80, 30]; - } - - MathFrac.title = "Frac"; - MathFrac.desc = "Returns fractional part"; - - MathFrac.prototype.onExecute = function() { - var v = this.getInputData(0); - if (v == null) { - return; - } - this.setOutputData(0, v % 1); - }; - - LiteGraph.registerNodeType("math/frac", MathFrac); - - //Math Floor - function MathSmoothStep() { - this.addInput("in", "number"); - this.addOutput("out", "number"); - this.size = [80, 30]; - this.properties = { A: 0, B: 1 }; - } - - MathSmoothStep.title = "Smoothstep"; - MathSmoothStep.desc = "Smoothstep"; - - MathSmoothStep.prototype.onExecute = function() { - var v = this.getInputData(0); - if (v === undefined) { - return; - } - - var edge0 = this.properties.A; - var edge1 = this.properties.B; - - // Scale, bias and saturate x to 0..1 range - v = clamp((v - edge0) / (edge1 - edge0), 0.0, 1.0); - // Evaluate polynomial - v = v * v * (3 - 2 * v); - - this.setOutputData(0, v); - }; - - LiteGraph.registerNodeType("math/smoothstep", MathSmoothStep); - - //Math scale - function MathScale() { - this.addInput("in", "number", { label: "" }); - this.addOutput("out", "number", { label: "" }); - this.size = [80, 30]; - this.addProperty("factor", 1); - } - - MathScale.title = "Scale"; - MathScale.desc = "v * factor"; - - MathScale.prototype.onExecute = function() { - var value = this.getInputData(0); - if (value != null) { - this.setOutputData(0, value * this.properties.factor); - } - }; - - LiteGraph.registerNodeType("math/scale", MathScale); - - //Gate - function Gate() { - this.addInput("v","boolean"); - this.addInput("A"); - this.addInput("B"); - this.addOutput("out"); - } - - Gate.title = "Gate"; - Gate.desc = "if v is true, then outputs A, otherwise B"; - - Gate.prototype.onExecute = function() { - var v = this.getInputData(0); - this.setOutputData(0, this.getInputData( v ? 1 : 2 )); - }; - - LiteGraph.registerNodeType("math/gate", Gate); - - - //Math Average - function MathAverageFilter() { - this.addInput("in", "number"); - this.addOutput("out", "number"); - this.size = [80, 30]; - this.addProperty("samples", 10); - this._values = new Float32Array(10); - this._current = 0; - } - - MathAverageFilter.title = "Average"; - MathAverageFilter.desc = "Average Filter"; - - MathAverageFilter.prototype.onExecute = function() { - var v = this.getInputData(0); - if (v == null) { - v = 0; - } - - var num_samples = this._values.length; - - this._values[this._current % num_samples] = v; - this._current += 1; - if (this._current > num_samples) { - this._current = 0; - } - - var avr = 0; - for (var i = 0; i < num_samples; ++i) { - avr += this._values[i]; - } - - this.setOutputData(0, avr / num_samples); - }; - - MathAverageFilter.prototype.onPropertyChanged = function(name, value) { - if (value < 1) { - value = 1; - } - this.properties.samples = Math.round(value); - var old = this._values; - - this._values = new Float32Array(this.properties.samples); - if (old.length <= this._values.length) { - this._values.set(old); - } else { - this._values.set(old.subarray(0, this._values.length)); - } - }; - - LiteGraph.registerNodeType("math/average", MathAverageFilter); - - //Math - function MathTendTo() { - this.addInput("in", "number"); - this.addOutput("out", "number"); - this.addProperty("factor", 0.1); - this.size = [80, 30]; - this._value = null; - } - - MathTendTo.title = "TendTo"; - MathTendTo.desc = "moves the output value always closer to the input"; - - MathTendTo.prototype.onExecute = function() { - var v = this.getInputData(0); - if (v == null) { - v = 0; - } - var f = this.properties.factor; - if (this._value == null) { - this._value = v; - } else { - this._value = this._value * (1 - f) + v * f; - } - this.setOutputData(0, this._value); - }; - - LiteGraph.registerNodeType("math/tendTo", MathTendTo); - - //Math operation - function MathOperation() { - this.addInput("A", "number,array,object"); - this.addInput("B", "number"); - this.addOutput("=", "number"); - this.addProperty("A", 1); - this.addProperty("B", 1); - this.addProperty("OP", "+", "enum", { values: MathOperation.values }); - this._func = MathOperation.funcs[this.properties.OP]; - this._result = []; //only used for arrays - } - - MathOperation.values = ["+", "-", "*", "/", "%", "^", "max", "min"]; - MathOperation.funcs = { - "+": function(A,B) { return A + B; }, - "-": function(A,B) { return A - B; }, - "x": function(A,B) { return A * B; }, - "X": function(A,B) { return A * B; }, - "*": function(A,B) { return A * B; }, - "/": function(A,B) { return A / B; }, - "%": function(A,B) { return A % B; }, - "^": function(A,B) { return Math.pow(A, B); }, - "max": function(A,B) { return Math.max(A, B); }, - "min": function(A,B) { return Math.min(A, B); } - }; - - MathOperation.title = "Operation"; - MathOperation.desc = "Easy math operators"; - MathOperation["@OP"] = { - type: "enum", - title: "operation", - values: MathOperation.values - }; - MathOperation.size = [100, 60]; - - MathOperation.prototype.getTitle = function() { - if(this.properties.OP == "max" || this.properties.OP == "min") - return this.properties.OP + "(A,B)"; - return "A " + this.properties.OP + " B"; - }; - - MathOperation.prototype.setValue = function(v) { - if (typeof v == "string") { - v = parseFloat(v); - } - this.properties["value"] = v; - }; - - MathOperation.prototype.onPropertyChanged = function(name, value) - { - if (name != "OP") - return; - this._func = MathOperation.funcs[this.properties.OP]; - if(!this._func) - { - console.warn("Unknown operation: " + this.properties.OP); - this._func = function(A) { return A; }; - } - } - - MathOperation.prototype.onExecute = function() { - var A = this.getInputData(0); - var B = this.getInputData(1); - if ( A != null ) { - if( A.constructor === Number ) - this.properties["A"] = A; - } else { - A = this.properties["A"]; - } - - if (B != null) { - this.properties["B"] = B; - } else { - B = this.properties["B"]; - } - - var func = MathOperation.funcs[this.properties.OP]; - - var result; - if(A.constructor === Number) - { - result = 0; - result = func(A,B); - } - else if(A.constructor === Array) - { - result = this._result; - result.length = A.length; - for(var i = 0; i < A.length; ++i) - result[i] = func(A[i],B); - } - else - { - result = {}; - for(var i in A) - result[i] = func(A[i],B); - } - this.setOutputData(0, result); - }; - - MathOperation.prototype.onDrawBackground = function(ctx) { - if (this.flags.collapsed) { - return; - } - - ctx.font = "40px Arial"; - ctx.fillStyle = "#666"; - ctx.textAlign = "center"; - ctx.fillText( - this.properties.OP, - this.size[0] * 0.5, - (this.size[1] + LiteGraph.NODE_TITLE_HEIGHT) * 0.5 - ); - ctx.textAlign = "left"; - }; - - LiteGraph.registerNodeType("math/operation", MathOperation); - - LiteGraph.registerSearchboxExtra("math/operation", "MAX", { - properties: {OP:"max"}, - title: "MAX()" - }); - - LiteGraph.registerSearchboxExtra("math/operation", "MIN", { - properties: {OP:"min"}, - title: "MIN()" - }); - - - //Math compare - function MathCompare() { - this.addInput("A", "number"); - this.addInput("B", "number"); - this.addOutput("A==B", "boolean"); - this.addOutput("A!=B", "boolean"); - this.addProperty("A", 0); - this.addProperty("B", 0); - } - - MathCompare.title = "Compare"; - MathCompare.desc = "compares between two values"; - - MathCompare.prototype.onExecute = function() { - var A = this.getInputData(0); - var B = this.getInputData(1); - if (A !== undefined) { - this.properties["A"] = A; - } else { - A = this.properties["A"]; - } - - if (B !== undefined) { - this.properties["B"] = B; - } else { - B = this.properties["B"]; - } - - for (var i = 0, l = this.outputs.length; i < l; ++i) { - var output = this.outputs[i]; - if (!output.links || !output.links.length) { - continue; - } - var value; - switch (output.name) { - case "A==B": - value = A == B; - break; - case "A!=B": - value = A != B; - break; - case "A>B": - value = A > B; - break; - case "A=B": - value = A >= B; - break; - } - this.setOutputData(i, value); - } - }; - - MathCompare.prototype.onGetOutputs = function() { - return [ - ["A==B", "boolean"], - ["A!=B", "boolean"], - ["A>B", "boolean"], - ["A=B", "boolean"], - ["A<=B", "boolean"] - ]; - }; - - LiteGraph.registerNodeType("math/compare", MathCompare); - - LiteGraph.registerSearchboxExtra("math/compare", "==", { - outputs: [["A==B", "boolean"]], - title: "A==B" - }); - LiteGraph.registerSearchboxExtra("math/compare", "!=", { - outputs: [["A!=B", "boolean"]], - title: "A!=B" - }); - LiteGraph.registerSearchboxExtra("math/compare", ">", { - outputs: [["A>B", "boolean"]], - title: "A>B" - }); - LiteGraph.registerSearchboxExtra("math/compare", "<", { - outputs: [["A=", { - outputs: [["A>=B", "boolean"]], - title: "A>=B" - }); - LiteGraph.registerSearchboxExtra("math/compare", "<=", { - outputs: [["A<=B", "boolean"]], - title: "A<=B" - }); - - function MathCondition() { - this.addInput("A", "number"); - this.addInput("B", "number"); - this.addOutput("true", "boolean"); - this.addOutput("false", "boolean"); - this.addProperty("A", 1); - this.addProperty("B", 1); - this.addProperty("OP", ">", "enum", { values: MathCondition.values }); - this.addWidget("combo","Cond.",this.properties.OP,{ property: "OP", values: MathCondition.values } ); - - this.size = [80, 60]; - } - - MathCondition.values = [">", "<", "==", "!=", "<=", ">=", "||", "&&" ]; - MathCondition["@OP"] = { - type: "enum", - title: "operation", - values: MathCondition.values - }; - - MathCondition.title = "Condition"; - MathCondition.desc = "evaluates condition between A and B"; - - MathCondition.prototype.getTitle = function() { - return "A " + this.properties.OP + " B"; - }; - - MathCondition.prototype.onExecute = function() { - var A = this.getInputData(0); - if (A === undefined) { - A = this.properties.A; - } else { - this.properties.A = A; - } - - var B = this.getInputData(1); - if (B === undefined) { - B = this.properties.B; - } else { - this.properties.B = B; - } - - var result = true; - switch (this.properties.OP) { - case ">": - result = A > B; - break; - case "<": - result = A < B; - break; - case "==": - result = A == B; - break; - case "!=": - result = A != B; - break; - case "<=": - result = A <= B; - break; - case ">=": - result = A >= B; - break; - case "||": - result = A || B; - break; - case "&&": - result = A && B; - break; - } - - this.setOutputData(0, result); - this.setOutputData(1, !result); - }; - - LiteGraph.registerNodeType("math/condition", MathCondition); - - - function MathBranch() { - this.addInput("in", 0); - this.addInput("cond", "boolean"); - this.addOutput("true", 0); - this.addOutput("false", 0); - this.size = [80, 60]; - } - - MathBranch.title = "Branch"; - MathBranch.desc = "If condition is true, outputs IN in true, otherwise in false"; - - MathBranch.prototype.onExecute = function() { - var V = this.getInputData(0); - var cond = this.getInputData(1); - - if(cond) - { - this.setOutputData(0, V); - this.setOutputData(1, null); - } - else - { - this.setOutputData(0, null); - this.setOutputData(1, V); - } - } - - LiteGraph.registerNodeType("math/branch", MathBranch); - - - function MathAccumulate() { - this.addInput("inc", "number"); - this.addOutput("total", "number"); - this.addProperty("increment", 1); - this.addProperty("value", 0); - } - - MathAccumulate.title = "Accumulate"; - MathAccumulate.desc = "Increments a value every time"; - - MathAccumulate.prototype.onExecute = function() { - if (this.properties.value === null) { - this.properties.value = 0; - } - - var inc = this.getInputData(0); - if (inc !== null) { - this.properties.value += inc; - } else { - this.properties.value += this.properties.increment; - } - this.setOutputData(0, this.properties.value); - }; - - LiteGraph.registerNodeType("math/accumulate", MathAccumulate); - - //Math Trigonometry - function MathTrigonometry() { - this.addInput("v", "number"); - this.addOutput("sin", "number"); - - this.addProperty("amplitude", 1); - this.addProperty("offset", 0); - this.bgImageUrl = "nodes/imgs/icon-sin.png"; - } - - MathTrigonometry.title = "Trigonometry"; - MathTrigonometry.desc = "Sin Cos Tan"; - //MathTrigonometry.filter = "shader"; - - MathTrigonometry.prototype.onExecute = function() { - var v = this.getInputData(0); - if (v == null) { - v = 0; - } - var amplitude = this.properties["amplitude"]; - var slot = this.findInputSlot("amplitude"); - if (slot != -1) { - amplitude = this.getInputData(slot); - } - var offset = this.properties["offset"]; - slot = this.findInputSlot("offset"); - if (slot != -1) { - offset = this.getInputData(slot); - } - - for (var i = 0, l = this.outputs.length; i < l; ++i) { - var output = this.outputs[i]; - var value; - switch (output.name) { - case "sin": - value = Math.sin(v); - break; - case "cos": - value = Math.cos(v); - break; - case "tan": - value = Math.tan(v); - break; - case "asin": - value = Math.asin(v); - break; - case "acos": - value = Math.acos(v); - break; - case "atan": - value = Math.atan(v); - break; - } - this.setOutputData(i, amplitude * value + offset); - } - }; - - MathTrigonometry.prototype.onGetInputs = function() { - return [["v", "number"], ["amplitude", "number"], ["offset", "number"]]; - }; - - MathTrigonometry.prototype.onGetOutputs = function() { - return [ - ["sin", "number"], - ["cos", "number"], - ["tan", "number"], - ["asin", "number"], - ["acos", "number"], - ["atan", "number"] - ]; - }; - - LiteGraph.registerNodeType("math/trigonometry", MathTrigonometry); - - LiteGraph.registerSearchboxExtra("math/trigonometry", "SIN()", { - outputs: [["sin", "number"]], - title: "SIN()" - }); - LiteGraph.registerSearchboxExtra("math/trigonometry", "COS()", { - outputs: [["cos", "number"]], - title: "COS()" - }); - LiteGraph.registerSearchboxExtra("math/trigonometry", "TAN()", { - outputs: [["tan", "number"]], - title: "TAN()" - }); - - //math library for safe math operations without eval - function MathFormula() { - this.addInput("x", "number"); - this.addInput("y", "number"); - this.addOutput("", "number"); - this.properties = { x: 1.0, y: 1.0, formula: "x+y" }; - this.code_widget = this.addWidget( - "text", - "F(x,y)", - this.properties.formula, - function(v, canvas, node) { - node.properties.formula = v; - } - ); - this.addWidget("toggle", "allow", LiteGraph.allow_scripts, function(v) { - LiteGraph.allow_scripts = v; - }); - this._func = null; - } - - MathFormula.title = "Formula"; - MathFormula.desc = "Compute formula"; - MathFormula.size = [160, 100]; - - MathAverageFilter.prototype.onPropertyChanged = function(name, value) { - if (name == "formula") { - this.code_widget.value = value; - } - }; - - MathFormula.prototype.onExecute = function() { - if (!LiteGraph.allow_scripts) { - return; - } - - var x = this.getInputData(0); - var y = this.getInputData(1); - if (x != null) { - this.properties["x"] = x; - } else { - x = this.properties["x"]; - } - - if (y != null) { - this.properties["y"] = y; - } else { - y = this.properties["y"]; - } - - var f = this.properties["formula"]; - - var value; - try { - if (!this._func || this._func_code != this.properties.formula) { - this._func = new Function( - "x", - "y", - "TIME", - "return " + this.properties.formula - ); - this._func_code = this.properties.formula; - } - value = this._func(x, y, this.graph.globaltime); - this.boxcolor = null; - } catch (err) { - this.boxcolor = "red"; - } - this.setOutputData(0, value); - }; - - MathFormula.prototype.getTitle = function() { - return this._func_code || "Formula"; - }; - - MathFormula.prototype.onDrawBackground = function() { - var f = this.properties["formula"]; - if (this.outputs && this.outputs.length) { - this.outputs[0].label = f; - } - }; - - LiteGraph.registerNodeType("math/formula", MathFormula); - - function Math3DVec2ToXY() { - this.addInput("vec2", "vec2"); - this.addOutput("x", "number"); - this.addOutput("y", "number"); - } - - Math3DVec2ToXY.title = "Vec2->XY"; - Math3DVec2ToXY.desc = "vector 2 to components"; - - Math3DVec2ToXY.prototype.onExecute = function() { - var v = this.getInputData(0); - if (v == null) { - return; - } - - this.setOutputData(0, v[0]); - this.setOutputData(1, v[1]); - }; - - LiteGraph.registerNodeType("math3d/vec2-to-xy", Math3DVec2ToXY); - - function Math3DXYToVec2() { - this.addInputs([["x", "number"], ["y", "number"]]); - this.addOutput("vec2", "vec2"); - this.properties = { x: 0, y: 0 }; - this._data = new Float32Array(2); - } - - Math3DXYToVec2.title = "XY->Vec2"; - Math3DXYToVec2.desc = "components to vector2"; - - Math3DXYToVec2.prototype.onExecute = function() { - var x = this.getInputData(0); - if (x == null) { - x = this.properties.x; - } - var y = this.getInputData(1); - if (y == null) { - y = this.properties.y; - } - - var data = this._data; - data[0] = x; - data[1] = y; - - this.setOutputData(0, data); - }; - - LiteGraph.registerNodeType("math3d/xy-to-vec2", Math3DXYToVec2); - - function Math3DVec3ToXYZ() { - this.addInput("vec3", "vec3"); - this.addOutput("x", "number"); - this.addOutput("y", "number"); - this.addOutput("z", "number"); - } - - Math3DVec3ToXYZ.title = "Vec3->XYZ"; - Math3DVec3ToXYZ.desc = "vector 3 to components"; - - Math3DVec3ToXYZ.prototype.onExecute = function() { - var v = this.getInputData(0); - if (v == null) { - return; - } - - this.setOutputData(0, v[0]); - this.setOutputData(1, v[1]); - this.setOutputData(2, v[2]); - }; - - LiteGraph.registerNodeType("math3d/vec3-to-xyz", Math3DVec3ToXYZ); - - function Math3DXYZToVec3() { - this.addInputs([["x", "number"], ["y", "number"], ["z", "number"]]); - this.addOutput("vec3", "vec3"); - this.properties = { x: 0, y: 0, z: 0 }; - this._data = new Float32Array(3); - } - - Math3DXYZToVec3.title = "XYZ->Vec3"; - Math3DXYZToVec3.desc = "components to vector3"; - - Math3DXYZToVec3.prototype.onExecute = function() { - var x = this.getInputData(0); - if (x == null) { - x = this.properties.x; - } - var y = this.getInputData(1); - if (y == null) { - y = this.properties.y; - } - var z = this.getInputData(2); - if (z == null) { - z = this.properties.z; - } - - var data = this._data; - data[0] = x; - data[1] = y; - data[2] = z; - - this.setOutputData(0, data); - }; - - LiteGraph.registerNodeType("math3d/xyz-to-vec3", Math3DXYZToVec3); - - function Math3DVec4ToXYZW() { - this.addInput("vec4", "vec4"); - this.addOutput("x", "number"); - this.addOutput("y", "number"); - this.addOutput("z", "number"); - this.addOutput("w", "number"); - } - - Math3DVec4ToXYZW.title = "Vec4->XYZW"; - Math3DVec4ToXYZW.desc = "vector 4 to components"; - - Math3DVec4ToXYZW.prototype.onExecute = function() { - var v = this.getInputData(0); - if (v == null) { - return; - } - - this.setOutputData(0, v[0]); - this.setOutputData(1, v[1]); - this.setOutputData(2, v[2]); - this.setOutputData(3, v[3]); - }; - - LiteGraph.registerNodeType("math3d/vec4-to-xyzw", Math3DVec4ToXYZW); - - function Math3DXYZWToVec4() { - this.addInputs([ - ["x", "number"], - ["y", "number"], - ["z", "number"], - ["w", "number"] - ]); - this.addOutput("vec4", "vec4"); - this.properties = { x: 0, y: 0, z: 0, w: 0 }; - this._data = new Float32Array(4); - } - - Math3DXYZWToVec4.title = "XYZW->Vec4"; - Math3DXYZWToVec4.desc = "components to vector4"; - - Math3DXYZWToVec4.prototype.onExecute = function() { - var x = this.getInputData(0); - if (x == null) { - x = this.properties.x; - } - var y = this.getInputData(1); - if (y == null) { - y = this.properties.y; - } - var z = this.getInputData(2); - if (z == null) { - z = this.properties.z; - } - var w = this.getInputData(3); - if (w == null) { - w = this.properties.w; - } - - var data = this._data; - data[0] = x; - data[1] = y; - data[2] = z; - data[3] = w; - - this.setOutputData(0, data); - }; - - LiteGraph.registerNodeType("math3d/xyzw-to-vec4", Math3DXYZWToVec4); - -})(this); diff --git a/src/nodes/math3d.js b/src/nodes/math3d.js deleted file mode 100755 index cb3be5c4e..000000000 --- a/src/nodes/math3d.js +++ /dev/null @@ -1,573 +0,0 @@ -(function(global) { - var LiteGraph = global.LiteGraph; - - - function Math3DMat4() - { - this.addInput("T", "vec3"); - this.addInput("R", "vec3"); - this.addInput("S", "vec3"); - this.addOutput("mat4", "mat4"); - this.properties = { - "T":[0,0,0], - "R":[0,0,0], - "S":[1,1,1], - R_in_degrees: true - }; - this._result = mat4.create(); - this._must_update = true; - } - - Math3DMat4.title = "mat4"; - Math3DMat4.temp_quat = new Float32Array([0,0,0,1]); - Math3DMat4.temp_mat4 = new Float32Array(16); - Math3DMat4.temp_vec3 = new Float32Array(3); - - Math3DMat4.prototype.onPropertyChanged = function(name, value) - { - this._must_update = true; - } - - Math3DMat4.prototype.onExecute = function() - { - var M = this._result; - var Q = Math3DMat4.temp_quat; - var temp_mat4 = Math3DMat4.temp_mat4; - var temp_vec3 = Math3DMat4.temp_vec3; - - var T = this.getInputData(0); - var R = this.getInputData(1); - var S = this.getInputData(2); - - if( this._must_update || T || R || S ) - { - T = T || this.properties.T; - R = R || this.properties.R; - S = S || this.properties.S; - mat4.identity( M ); - mat4.translate( M, M, T ); - if(this.properties.R_in_degrees) - { - temp_vec3.set( R ); - vec3.scale(temp_vec3,temp_vec3,DEG2RAD); - quat.fromEuler( Q, temp_vec3 ); - } - else - quat.fromEuler( Q, R ); - mat4.fromQuat( temp_mat4, Q ); - mat4.multiply( M, M, temp_mat4 ); - mat4.scale( M, M, S ); - } - - this.setOutputData(0, M); - } - - LiteGraph.registerNodeType("math3d/mat4", Math3DMat4); - - //Math 3D operation - function Math3DOperation() { - this.addInput("A", "number,vec3"); - this.addInput("B", "number,vec3"); - this.addOutput("=", "number,vec3"); - this.addProperty("OP", "+", "enum", { values: Math3DOperation.values }); - this._result = vec3.create(); - } - - Math3DOperation.values = ["+", "-", "*", "/", "%", "^", "max", "min","dot","cross"]; - - LiteGraph.registerSearchboxExtra("math3d/operation", "CROSS()", { - properties: {"OP":"cross"}, - title: "CROSS()" - }); - - LiteGraph.registerSearchboxExtra("math3d/operation", "DOT()", { - properties: {"OP":"dot"}, - title: "DOT()" - }); - - Math3DOperation.title = "Operation"; - Math3DOperation.desc = "Easy math 3D operators"; - Math3DOperation["@OP"] = { - type: "enum", - title: "operation", - values: Math3DOperation.values - }; - Math3DOperation.size = [100, 60]; - - Math3DOperation.prototype.getTitle = function() { - if(this.properties.OP == "max" || this.properties.OP == "min" ) - return this.properties.OP + "(A,B)"; - return "A " + this.properties.OP + " B"; - }; - - Math3DOperation.prototype.onExecute = function() { - var A = this.getInputData(0); - var B = this.getInputData(1); - if(A == null || B == null) - return; - if(A.constructor === Number) - A = [A,A,A]; - if(B.constructor === Number) - B = [B,B,B]; - - var result = this._result; - switch (this.properties.OP) { - case "+": - result = vec3.add(result,A,B); - break; - case "-": - result = vec3.sub(result,A,B); - break; - case "x": - case "X": - case "*": - result = vec3.mul(result,A,B); - break; - case "/": - result = vec3.div(result,A,B); - break; - case "%": - result[0] = A[0]%B[0]; - result[1] = A[1]%B[1]; - result[2] = A[2]%B[2]; - break; - case "^": - result[0] = Math.pow(A[0],B[0]); - result[1] = Math.pow(A[1],B[1]); - result[2] = Math.pow(A[2],B[2]); - break; - case "max": - result[0] = Math.max(A[0],B[0]); - result[1] = Math.max(A[1],B[1]); - result[2] = Math.max(A[2],B[2]); - break; - case "min": - result[0] = Math.min(A[0],B[0]); - result[1] = Math.min(A[1],B[1]); - result[2] = Math.min(A[2],B[2]); - case "dot": - result = vec3.dot(A,B); - break; - case "cross": - vec3.cross(result,A,B); - break; - default: - console.warn("Unknown operation: " + this.properties.OP); - } - this.setOutputData(0, result); - }; - - Math3DOperation.prototype.onDrawBackground = function(ctx) { - if (this.flags.collapsed) { - return; - } - - ctx.font = "40px Arial"; - ctx.fillStyle = "#666"; - ctx.textAlign = "center"; - ctx.fillText( - this.properties.OP, - this.size[0] * 0.5, - (this.size[1] + LiteGraph.NODE_TITLE_HEIGHT) * 0.5 - ); - ctx.textAlign = "left"; - }; - - LiteGraph.registerNodeType("math3d/operation", Math3DOperation); - - function Math3DVec3Scale() { - this.addInput("in", "vec3"); - this.addInput("f", "number"); - this.addOutput("out", "vec3"); - this.properties = { f: 1 }; - this._data = new Float32Array(3); - } - - Math3DVec3Scale.title = "vec3_scale"; - Math3DVec3Scale.desc = "scales the components of a vec3"; - - Math3DVec3Scale.prototype.onExecute = function() { - var v = this.getInputData(0); - if (v == null) { - return; - } - var f = this.getInputData(1); - if (f == null) { - f = this.properties.f; - } - - var data = this._data; - data[0] = v[0] * f; - data[1] = v[1] * f; - data[2] = v[2] * f; - this.setOutputData(0, data); - }; - - LiteGraph.registerNodeType("math3d/vec3-scale", Math3DVec3Scale); - - function Math3DVec3Length() { - this.addInput("in", "vec3"); - this.addOutput("out", "number"); - } - - Math3DVec3Length.title = "vec3_length"; - Math3DVec3Length.desc = "returns the module of a vector"; - - Math3DVec3Length.prototype.onExecute = function() { - var v = this.getInputData(0); - if (v == null) { - return; - } - var dist = Math.sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]); - this.setOutputData(0, dist); - }; - - LiteGraph.registerNodeType("math3d/vec3-length", Math3DVec3Length); - - function Math3DVec3Normalize() { - this.addInput("in", "vec3"); - this.addOutput("out", "vec3"); - this._data = new Float32Array(3); - } - - Math3DVec3Normalize.title = "vec3_normalize"; - Math3DVec3Normalize.desc = "returns the vector normalized"; - - Math3DVec3Normalize.prototype.onExecute = function() { - var v = this.getInputData(0); - if (v == null) { - return; - } - var dist = Math.sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]); - var data = this._data; - data[0] = v[0] / dist; - data[1] = v[1] / dist; - data[2] = v[2] / dist; - - this.setOutputData(0, data); - }; - - LiteGraph.registerNodeType("math3d/vec3-normalize", Math3DVec3Normalize); - - function Math3DVec3Lerp() { - this.addInput("A", "vec3"); - this.addInput("B", "vec3"); - this.addInput("f", "vec3"); - this.addOutput("out", "vec3"); - this.properties = { f: 0.5 }; - this._data = new Float32Array(3); - } - - Math3DVec3Lerp.title = "vec3_lerp"; - Math3DVec3Lerp.desc = "returns the interpolated vector"; - - Math3DVec3Lerp.prototype.onExecute = function() { - var A = this.getInputData(0); - if (A == null) { - return; - } - var B = this.getInputData(1); - if (B == null) { - return; - } - var f = this.getInputOrProperty("f"); - - var data = this._data; - data[0] = A[0] * (1 - f) + B[0] * f; - data[1] = A[1] * (1 - f) + B[1] * f; - data[2] = A[2] * (1 - f) + B[2] * f; - - this.setOutputData(0, data); - }; - - LiteGraph.registerNodeType("math3d/vec3-lerp", Math3DVec3Lerp); - - function Math3DVec3Dot() { - this.addInput("A", "vec3"); - this.addInput("B", "vec3"); - this.addOutput("out", "number"); - } - - Math3DVec3Dot.title = "vec3_dot"; - Math3DVec3Dot.desc = "returns the dot product"; - - Math3DVec3Dot.prototype.onExecute = function() { - var A = this.getInputData(0); - if (A == null) { - return; - } - var B = this.getInputData(1); - if (B == null) { - return; - } - - var dot = A[0] * B[0] + A[1] * B[1] + A[2] * B[2]; - this.setOutputData(0, dot); - }; - - LiteGraph.registerNodeType("math3d/vec3-dot", Math3DVec3Dot); - - //if glMatrix is installed... - if (global.glMatrix) { - function Math3DQuaternion() { - this.addOutput("quat", "quat"); - this.properties = { x: 0, y: 0, z: 0, w: 1, normalize: false }; - this._value = quat.create(); - } - - Math3DQuaternion.title = "Quaternion"; - Math3DQuaternion.desc = "quaternion"; - - Math3DQuaternion.prototype.onExecute = function() { - this._value[0] = this.getInputOrProperty("x"); - this._value[1] = this.getInputOrProperty("y"); - this._value[2] = this.getInputOrProperty("z"); - this._value[3] = this.getInputOrProperty("w"); - if (this.properties.normalize) { - quat.normalize(this._value, this._value); - } - this.setOutputData(0, this._value); - }; - - Math3DQuaternion.prototype.onGetInputs = function() { - return [ - ["x", "number"], - ["y", "number"], - ["z", "number"], - ["w", "number"] - ]; - }; - - LiteGraph.registerNodeType("math3d/quaternion", Math3DQuaternion); - - function Math3DRotation() { - this.addInputs([["degrees", "number"], ["axis", "vec3"]]); - this.addOutput("quat", "quat"); - this.properties = { angle: 90.0, axis: vec3.fromValues(0, 1, 0) }; - - this._value = quat.create(); - } - - Math3DRotation.title = "Rotation"; - Math3DRotation.desc = "quaternion rotation"; - - Math3DRotation.prototype.onExecute = function() { - var angle = this.getInputData(0); - if (angle == null) { - angle = this.properties.angle; - } - var axis = this.getInputData(1); - if (axis == null) { - axis = this.properties.axis; - } - - var R = quat.setAxisAngle(this._value, axis, angle * 0.0174532925); - this.setOutputData(0, R); - }; - - LiteGraph.registerNodeType("math3d/rotation", Math3DRotation); - - - function MathEulerToQuat() { - this.addInput("euler", "vec3"); - this.addOutput("quat", "quat"); - this.properties = { euler:[0,0,0], use_yaw_pitch_roll: false }; - this._degs = vec3.create(); - this._value = quat.create(); - } - - MathEulerToQuat.title = "Euler->Quat"; - MathEulerToQuat.desc = "Converts euler angles (in degrees) to quaternion"; - - MathEulerToQuat.prototype.onExecute = function() { - var euler = this.getInputData(0); - if (euler == null) { - euler = this.properties.euler; - } - vec3.scale( this._degs, euler, DEG2RAD ); - if(this.properties.use_yaw_pitch_roll) - this._degs = [this._degs[2],this._degs[0],this._degs[1]]; - var R = quat.fromEuler(this._value, this._degs); - this.setOutputData(0, R); - }; - - LiteGraph.registerNodeType("math3d/euler_to_quat", MathEulerToQuat); - - function MathQuatToEuler() { - this.addInput(["quat", "quat"]); - this.addOutput("euler", "vec3"); - this._value = vec3.create(); - } - - MathQuatToEuler.title = "Euler->Quat"; - MathQuatToEuler.desc = "Converts rotX,rotY,rotZ in degrees to quat"; - - MathQuatToEuler.prototype.onExecute = function() { - var q = this.getInputData(0); - if(!q) - return; - var R = quat.toEuler(this._value, q); - vec3.scale( this._value, this._value, DEG2RAD ); - this.setOutputData(0, this._value); - }; - - LiteGraph.registerNodeType("math3d/quat_to_euler", MathQuatToEuler); - - - //Math3D rotate vec3 - function Math3DRotateVec3() { - this.addInputs([["vec3", "vec3"], ["quat", "quat"]]); - this.addOutput("result", "vec3"); - this.properties = { vec: [0, 0, 1] }; - } - - Math3DRotateVec3.title = "Rot. Vec3"; - Math3DRotateVec3.desc = "rotate a point"; - - Math3DRotateVec3.prototype.onExecute = function() { - var vec = this.getInputData(0); - if (vec == null) { - vec = this.properties.vec; - } - var quat = this.getInputData(1); - if (quat == null) { - this.setOutputData(vec); - } else { - this.setOutputData( - 0, - vec3.transformQuat(vec3.create(), vec, quat) - ); - } - }; - - LiteGraph.registerNodeType("math3d/rotate_vec3", Math3DRotateVec3); - - function Math3DMultQuat() { - this.addInputs([["A", "quat"], ["B", "quat"]]); - this.addOutput("A*B", "quat"); - - this._value = quat.create(); - } - - Math3DMultQuat.title = "Mult. Quat"; - Math3DMultQuat.desc = "rotate quaternion"; - - Math3DMultQuat.prototype.onExecute = function() { - var A = this.getInputData(0); - if (A == null) { - return; - } - var B = this.getInputData(1); - if (B == null) { - return; - } - - var R = quat.multiply(this._value, A, B); - this.setOutputData(0, R); - }; - - LiteGraph.registerNodeType("math3d/mult-quat", Math3DMultQuat); - - function Math3DQuatSlerp() { - this.addInputs([ - ["A", "quat"], - ["B", "quat"], - ["factor", "number"] - ]); - this.addOutput("slerp", "quat"); - this.addProperty("factor", 0.5); - - this._value = quat.create(); - } - - Math3DQuatSlerp.title = "Quat Slerp"; - Math3DQuatSlerp.desc = "quaternion spherical interpolation"; - - Math3DQuatSlerp.prototype.onExecute = function() { - var A = this.getInputData(0); - if (A == null) { - return; - } - var B = this.getInputData(1); - if (B == null) { - return; - } - var factor = this.properties.factor; - if (this.getInputData(2) != null) { - factor = this.getInputData(2); - } - - var R = quat.slerp(this._value, A, B, factor); - this.setOutputData(0, R); - }; - - LiteGraph.registerNodeType("math3d/quat-slerp", Math3DQuatSlerp); - - - //Math3D rotate vec3 - function Math3DRemapRange() { - this.addInput("vec3", "vec3"); - this.addOutput("remap", "vec3"); - this.addOutput("clamped", "vec3"); - this.properties = { clamp: true, range_min: [-1, -1, 0], range_max: [1, 1, 0], target_min: [-1,-1,0], target_max:[1,1,0] }; - this._value = vec3.create(); - this._clamped = vec3.create(); - } - - Math3DRemapRange.title = "Remap Range"; - Math3DRemapRange.desc = "remap a 3D range"; - - Math3DRemapRange.prototype.onExecute = function() { - var vec = this.getInputData(0); - if(vec) - this._value.set(vec); - var range_min = this.properties.range_min; - var range_max = this.properties.range_max; - var target_min = this.properties.target_min; - var target_max = this.properties.target_max; - - //swap to avoid errors - /* - if(range_min > range_max) - { - range_min = range_max; - range_max = this.properties.range_min; - } - - if(target_min > target_max) - { - target_min = target_max; - target_max = this.properties.target_min; - } - */ - - for(var i = 0; i < 3; ++i) - { - var r = range_max[i] - range_min[i]; - this._clamped[i] = clamp( this._value[i], range_min[i], range_max[i] ); - if(r == 0) - { - this._value[i] = (target_min[i] + target_max[i]) * 0.5; - continue; - } - - var n = (this._value[i] - range_min[i]) / r; - if(this.properties.clamp) - n = clamp(n,0,1); - var t = target_max[i] - target_min[i]; - this._value[i] = target_min[i] + n * t; - } - - this.setOutputData(0,this._value); - this.setOutputData(1,this._clamped); - }; - - LiteGraph.registerNodeType("math3d/remap_range", Math3DRemapRange); - - - - } //glMatrix - else if (LiteGraph.debug) - console.warn("No glmatrix found, some Math3D nodes may not work"); - -})(this); diff --git a/src/nodes/midi.js b/src/nodes/midi.js deleted file mode 100755 index 8cacc7e17..000000000 --- a/src/nodes/midi.js +++ /dev/null @@ -1,1586 +0,0 @@ -(function(global) { - var LiteGraph = global.LiteGraph; - var MIDI_COLOR = "#243"; - - function MIDIEvent(data) { - this.channel = 0; - this.cmd = 0; - this.data = new Uint32Array(3); - - if (data) { - this.setup(data); - } - } - - LiteGraph.MIDIEvent = MIDIEvent; - - MIDIEvent.prototype.fromJSON = function(o) { - this.setup(o.data); - }; - - MIDIEvent.prototype.setup = function(data) { - var raw_data = data; - if (data.constructor === Object) { - raw_data = data.data; - } - - this.data.set(raw_data); - - var midiStatus = raw_data[0]; - this.status = midiStatus; - - var midiCommand = midiStatus & 0xf0; - - if (midiStatus >= 0xf0) { - this.cmd = midiStatus; - } else { - this.cmd = midiCommand; - } - - if (this.cmd == MIDIEvent.NOTEON && this.velocity == 0) { - this.cmd = MIDIEvent.NOTEOFF; - } - - this.cmd_str = MIDIEvent.commands[this.cmd] || ""; - - if ( - midiCommand >= MIDIEvent.NOTEON || - midiCommand <= MIDIEvent.NOTEOFF - ) { - this.channel = midiStatus & 0x0f; - } - }; - - Object.defineProperty(MIDIEvent.prototype, "velocity", { - get: function() { - if (this.cmd == MIDIEvent.NOTEON) { - return this.data[2]; - } - return -1; - }, - set: function(v) { - this.data[2] = v; // v / 127; - }, - enumerable: true - }); - - MIDIEvent.notes = [ - "A", - "A#", - "B", - "C", - "C#", - "D", - "D#", - "E", - "F", - "F#", - "G", - "G#" - ]; - MIDIEvent.note_to_index = { - A: 0, - "A#": 1, - B: 2, - C: 3, - "C#": 4, - D: 5, - "D#": 6, - E: 7, - F: 8, - "F#": 9, - G: 10, - "G#": 11 - }; - - Object.defineProperty(MIDIEvent.prototype, "note", { - get: function() { - if (this.cmd != MIDIEvent.NOTEON) { - return -1; - } - return MIDIEvent.toNoteString(this.data[1], true); - }, - set: function(v) { - throw "notes cannot be assigned this way, must modify the data[1]"; - }, - enumerable: true - }); - - Object.defineProperty(MIDIEvent.prototype, "octave", { - get: function() { - if (this.cmd != MIDIEvent.NOTEON) { - return -1; - } - var octave = this.data[1] - 24; - return Math.floor(octave / 12 + 1); - }, - set: function(v) { - throw "octave cannot be assigned this way, must modify the data[1]"; - }, - enumerable: true - }); - - //returns HZs - MIDIEvent.prototype.getPitch = function() { - return Math.pow(2, (this.data[1] - 69) / 12) * 440; - }; - - MIDIEvent.computePitch = function(note) { - return Math.pow(2, (note - 69) / 12) * 440; - }; - - MIDIEvent.prototype.getCC = function() { - return this.data[1]; - }; - - MIDIEvent.prototype.getCCValue = function() { - return this.data[2]; - }; - - //not tested, there is a formula missing here - MIDIEvent.prototype.getPitchBend = function() { - return this.data[1] + (this.data[2] << 7) - 8192; - }; - - MIDIEvent.computePitchBend = function(v1, v2) { - return v1 + (v2 << 7) - 8192; - }; - - MIDIEvent.prototype.setCommandFromString = function(str) { - this.cmd = MIDIEvent.computeCommandFromString(str); - }; - - MIDIEvent.computeCommandFromString = function(str) { - if (!str) { - return 0; - } - - if (str && str.constructor === Number) { - return str; - } - - str = str.toUpperCase(); - switch (str) { - case "NOTE ON": - case "NOTEON": - return MIDIEvent.NOTEON; - break; - case "NOTE OFF": - case "NOTEOFF": - return MIDIEvent.NOTEON; - break; - case "KEY PRESSURE": - case "KEYPRESSURE": - return MIDIEvent.KEYPRESSURE; - break; - case "CONTROLLER CHANGE": - case "CONTROLLERCHANGE": - case "CC": - return MIDIEvent.CONTROLLERCHANGE; - break; - case "PROGRAM CHANGE": - case "PROGRAMCHANGE": - case "PC": - return MIDIEvent.PROGRAMCHANGE; - break; - case "CHANNEL PRESSURE": - case "CHANNELPRESSURE": - return MIDIEvent.CHANNELPRESSURE; - break; - case "PITCH BEND": - case "PITCHBEND": - return MIDIEvent.PITCHBEND; - break; - case "TIME TICK": - case "TIMETICK": - return MIDIEvent.TIMETICK; - break; - default: - return Number(str); //assume its a hex code - } - }; - - //transform from a pitch number to string like "C4" - MIDIEvent.toNoteString = function(d, skip_octave) { - d = Math.round(d); //in case it has decimals - var note = d - 21; - var octave = Math.floor((d - 24) / 12 + 1); - note = note % 12; - if (note < 0) { - note = 12 + note; - } - return MIDIEvent.notes[note] + (skip_octave ? "" : octave); - }; - - MIDIEvent.NoteStringToPitch = function(str) { - str = str.toUpperCase(); - var note = str[0]; - var octave = 4; - - if (str[1] == "#") { - note += "#"; - if (str.length > 2) { - octave = Number(str[2]); - } - } else { - if (str.length > 1) { - octave = Number(str[1]); - } - } - var pitch = MIDIEvent.note_to_index[note]; - if (pitch == null) { - return null; - } - return (octave - 1) * 12 + pitch + 21; - }; - - MIDIEvent.prototype.toString = function() { - var str = "" + this.channel + ". "; - switch (this.cmd) { - case MIDIEvent.NOTEON: - str += "NOTEON " + MIDIEvent.toNoteString(this.data[1]); - break; - case MIDIEvent.NOTEOFF: - str += "NOTEOFF " + MIDIEvent.toNoteString(this.data[1]); - break; - case MIDIEvent.CONTROLLERCHANGE: - str += "CC " + this.data[1] + " " + this.data[2]; - break; - case MIDIEvent.PROGRAMCHANGE: - str += "PC " + this.data[1]; - break; - case MIDIEvent.PITCHBEND: - str += "PITCHBEND " + this.getPitchBend(); - break; - case MIDIEvent.KEYPRESSURE: - str += "KEYPRESS " + this.data[1]; - break; - } - - return str; - }; - - MIDIEvent.prototype.toHexString = function() { - var str = ""; - for (var i = 0; i < this.data.length; i++) { - str += this.data[i].toString(16) + " "; - } - }; - - MIDIEvent.prototype.toJSON = function() { - return { - data: [this.data[0], this.data[1], this.data[2]], - object_class: "MIDIEvent" - }; - }; - - MIDIEvent.NOTEOFF = 0x80; - MIDIEvent.NOTEON = 0x90; - MIDIEvent.KEYPRESSURE = 0xa0; - MIDIEvent.CONTROLLERCHANGE = 0xb0; - MIDIEvent.PROGRAMCHANGE = 0xc0; - MIDIEvent.CHANNELPRESSURE = 0xd0; - MIDIEvent.PITCHBEND = 0xe0; - MIDIEvent.TIMETICK = 0xf8; - - MIDIEvent.commands = { - 0x80: "note off", - 0x90: "note on", - 0xa0: "key pressure", - 0xb0: "controller change", - 0xc0: "program change", - 0xd0: "channel pressure", - 0xe0: "pitch bend", - 0xf0: "system", - 0xf2: "Song pos", - 0xf3: "Song select", - 0xf6: "Tune request", - 0xf8: "time tick", - 0xfa: "Start Song", - 0xfb: "Continue Song", - 0xfc: "Stop Song", - 0xfe: "Sensing", - 0xff: "Reset" - }; - - MIDIEvent.commands_short = { - 0x80: "NOTEOFF", - 0x90: "NOTEOFF", - 0xa0: "KEYP", - 0xb0: "CC", - 0xc0: "PC", - 0xd0: "CP", - 0xe0: "PB", - 0xf0: "SYS", - 0xf2: "POS", - 0xf3: "SELECT", - 0xf6: "TUNEREQ", - 0xf8: "TT", - 0xfa: "START", - 0xfb: "CONTINUE", - 0xfc: "STOP", - 0xfe: "SENS", - 0xff: "RESET" - }; - - MIDIEvent.commands_reversed = {}; - for (var i in MIDIEvent.commands) { - MIDIEvent.commands_reversed[MIDIEvent.commands[i]] = i; - } - - //MIDI wrapper, instantiate by MIDIIn and MIDIOut - function MIDIInterface(on_ready, on_error) { - if (!navigator.requestMIDIAccess) { - this.error = "not suppoorted"; - if (on_error) { - on_error("Not supported"); - } else { - console.error("MIDI NOT SUPPORTED, enable by chrome://flags"); - } - return; - } - - this.on_ready = on_ready; - - this.state = { - note: [], - cc: [] - }; - - this.input_ports = null; - this.input_ports_info = []; - this.output_ports = null; - this.output_ports_info = []; - - navigator.requestMIDIAccess().then(this.onMIDISuccess.bind(this), this.onMIDIFailure.bind(this)); - } - - MIDIInterface.input = null; - - MIDIInterface.MIDIEvent = MIDIEvent; - - MIDIInterface.prototype.onMIDISuccess = function(midiAccess) { - console.log("MIDI ready!"); - console.log(midiAccess); - this.midi = midiAccess; // store in the global (in real usage, would probably keep in an object instance) - this.updatePorts(); - - if (this.on_ready) { - this.on_ready(this); - } - }; - - MIDIInterface.prototype.updatePorts = function() { - var midi = this.midi; - this.input_ports = midi.inputs; - this.input_ports_info = []; - this.output_ports = midi.outputs; - this.output_ports_info = []; - - var num = 0; - - var it = this.input_ports.values(); - var it_value = it.next(); - while (it_value && it_value.done === false) { - var port_info = it_value.value; - this.input_ports_info.push(port_info); - console.log( "Input port [type:'" + port_info.type + "'] id:'" + port_info.id + "' manufacturer:'" + port_info.manufacturer + "' name:'" + port_info.name + "' version:'" + port_info.version + "'" ); - num++; - it_value = it.next(); - } - this.num_input_ports = num; - - num = 0; - var it = this.output_ports.values(); - var it_value = it.next(); - while (it_value && it_value.done === false) { - var port_info = it_value.value; - this.output_ports_info.push(port_info); - console.log( "Output port [type:'" + port_info.type + "'] id:'" + port_info.id + "' manufacturer:'" + port_info.manufacturer + "' name:'" + port_info.name + "' version:'" + port_info.version + "'" ); - num++; - it_value = it.next(); - } - this.num_output_ports = num; - }; - - MIDIInterface.prototype.onMIDIFailure = function(msg) { - console.error("Failed to get MIDI access - " + msg); - }; - - MIDIInterface.prototype.openInputPort = function(port, callback) { - var input_port = this.input_ports.get("input-" + port); - if (!input_port) { - return false; - } - MIDIInterface.input = this; - var that = this; - - input_port.onmidimessage = function(a) { - var midi_event = new MIDIEvent(a.data); - that.updateState(midi_event); - if (callback) { - callback(a.data, midi_event); - } - if (MIDIInterface.on_message) { - MIDIInterface.on_message(a.data, midi_event); - } - }; - console.log("port open: ", input_port); - return true; - }; - - MIDIInterface.parseMsg = function(data) {}; - - MIDIInterface.prototype.updateState = function(midi_event) { - switch (midi_event.cmd) { - case MIDIEvent.NOTEON: - this.state.note[midi_event.value1 | 0] = midi_event.value2; - break; - case MIDIEvent.NOTEOFF: - this.state.note[midi_event.value1 | 0] = 0; - break; - case MIDIEvent.CONTROLLERCHANGE: - this.state.cc[midi_event.getCC()] = midi_event.getCCValue(); - break; - } - }; - - MIDIInterface.prototype.sendMIDI = function(port, midi_data) { - if (!midi_data) { - return; - } - - var output_port = this.output_ports_info[port];//this.output_ports.get("output-" + port); - if (!output_port) { - return; - } - - MIDIInterface.output = this; - - if (midi_data.constructor === MIDIEvent) { - output_port.send(midi_data.data); - } else { - output_port.send(midi_data); - } - }; - - function LGMIDIIn() { - this.addOutput("on_midi", LiteGraph.EVENT); - this.addOutput("out", "midi"); - this.properties = { port: 0 }; - this._last_midi_event = null; - this._current_midi_event = null; - this.boxcolor = "#AAA"; - this._last_time = 0; - - var that = this; - new MIDIInterface(function(midi) { - //open - that._midi = midi; - if (that._waiting) { - that.onStart(); - } - that._waiting = false; - }); - } - - LGMIDIIn.MIDIInterface = MIDIInterface; - - LGMIDIIn.title = "MIDI Input"; - LGMIDIIn.desc = "Reads MIDI from a input port"; - LGMIDIIn.color = MIDI_COLOR; - - LGMIDIIn.prototype.getPropertyInfo = function(name) { - if (!this._midi) { - return; - } - - if (name == "port") { - var values = {}; - for (var i = 0; i < this._midi.input_ports_info.length; ++i) { - var input = this._midi.input_ports_info[i]; - values[i] = i + ".- " + input.name + " version:" + input.version; - } - return { type: "enum", values: values }; - } - }; - - LGMIDIIn.prototype.onStart = function() { - if (this._midi) { - this._midi.openInputPort( - this.properties.port, - this.onMIDIEvent.bind(this) - ); - } else { - this._waiting = true; - } - }; - - LGMIDIIn.prototype.onMIDIEvent = function(data, midi_event) { - this._last_midi_event = midi_event; - this.boxcolor = "#AFA"; - this._last_time = LiteGraph.getTime(); - this.trigger("on_midi", midi_event); - if (midi_event.cmd == MIDIEvent.NOTEON) { - this.trigger("on_noteon", midi_event); - } else if (midi_event.cmd == MIDIEvent.NOTEOFF) { - this.trigger("on_noteoff", midi_event); - } else if (midi_event.cmd == MIDIEvent.CONTROLLERCHANGE) { - this.trigger("on_cc", midi_event); - } else if (midi_event.cmd == MIDIEvent.PROGRAMCHANGE) { - this.trigger("on_pc", midi_event); - } else if (midi_event.cmd == MIDIEvent.PITCHBEND) { - this.trigger("on_pitchbend", midi_event); - } - }; - - LGMIDIIn.prototype.onDrawBackground = function(ctx) { - this.boxcolor = "#AAA"; - if (!this.flags.collapsed && this._last_midi_event) { - ctx.fillStyle = "white"; - var now = LiteGraph.getTime(); - var f = 1.0 - Math.max(0, (now - this._last_time) * 0.001); - if (f > 0) { - var t = ctx.globalAlpha; - ctx.globalAlpha *= f; - ctx.font = "12px Tahoma"; - ctx.fillText( - this._last_midi_event.toString(), - 2, - this.size[1] * 0.5 + 3 - ); - //ctx.fillRect(0,0,this.size[0],this.size[1]); - ctx.globalAlpha = t; - } - } - }; - - LGMIDIIn.prototype.onExecute = function() { - if (this.outputs) { - var last = this._last_midi_event; - for (var i = 0; i < this.outputs.length; ++i) { - var output = this.outputs[i]; - var v = null; - switch (output.name) { - case "midi": - v = this._midi; - break; - case "last_midi": - v = last; - break; - default: - continue; - } - this.setOutputData(i, v); - } - } - }; - - LGMIDIIn.prototype.onGetOutputs = function() { - return [ - ["last_midi", "midi"], - ["on_midi", LiteGraph.EVENT], - ["on_noteon", LiteGraph.EVENT], - ["on_noteoff", LiteGraph.EVENT], - ["on_cc", LiteGraph.EVENT], - ["on_pc", LiteGraph.EVENT], - ["on_pitchbend", LiteGraph.EVENT] - ]; - }; - - LiteGraph.registerNodeType("midi/input", LGMIDIIn); - - function LGMIDIOut() { - this.addInput("send", LiteGraph.EVENT); - this.properties = { port: 0 }; - - var that = this; - new MIDIInterface(function(midi) { - that._midi = midi; - that.widget.options.values = that.getMIDIOutputs(); - }); - this.widget = this.addWidget("combo","Device",this.properties.port,{ property: "port", values: this.getMIDIOutputs.bind(this) }); - this.size = [340,60]; - } - - LGMIDIOut.MIDIInterface = MIDIInterface; - - LGMIDIOut.title = "MIDI Output"; - LGMIDIOut.desc = "Sends MIDI to output channel"; - LGMIDIOut.color = MIDI_COLOR; - - LGMIDIOut.prototype.onGetPropertyInfo = function(name) { - if (!this._midi) { - return; - } - - if (name == "port") { - var values = this.getMIDIOutputs(); - return { type: "enum", values: values }; - } - }; - LGMIDIOut.default_ports = {0:"unknown"}; - - LGMIDIOut.prototype.getMIDIOutputs = function() - { - var values = {}; - if(!this._midi) - return LGMIDIOut.default_ports; - if(this._midi.output_ports_info) - for (var i = 0; i < this._midi.output_ports_info.length; ++i) { - var output = this._midi.output_ports_info[i]; - if(!output) - continue; - var name = i + ".- " + output.name + " version:" + output.version; - values[i] = name; - } - return values; - } - - LGMIDIOut.prototype.onAction = function(event, midi_event) { - //console.log(midi_event); - if (!this._midi) { - return; - } - if (event == "send") { - this._midi.sendMIDI(this.properties.port, midi_event); - } - this.trigger("midi", midi_event); - }; - - LGMIDIOut.prototype.onGetInputs = function() { - return [["send", LiteGraph.ACTION]]; - }; - - LGMIDIOut.prototype.onGetOutputs = function() { - return [["on_midi", LiteGraph.EVENT]]; - }; - - LiteGraph.registerNodeType("midi/output", LGMIDIOut); - - - function LGMIDIShow() { - this.addInput("on_midi", LiteGraph.EVENT); - this._str = ""; - this.size = [200, 40]; - } - - LGMIDIShow.title = "MIDI Show"; - LGMIDIShow.desc = "Shows MIDI in the graph"; - LGMIDIShow.color = MIDI_COLOR; - - LGMIDIShow.prototype.getTitle = function() { - if (this.flags.collapsed) { - return this._str; - } - return this.title; - }; - - LGMIDIShow.prototype.onAction = function(event, midi_event) { - if (!midi_event) { - return; - } - if (midi_event.constructor === MIDIEvent) { - this._str = midi_event.toString(); - } else { - this._str = "???"; - } - }; - - LGMIDIShow.prototype.onDrawForeground = function(ctx) { - if (!this._str || this.flags.collapsed) { - return; - } - - ctx.font = "30px Arial"; - ctx.fillText(this._str, 10, this.size[1] * 0.8); - }; - - LGMIDIShow.prototype.onGetInputs = function() { - return [["in", LiteGraph.ACTION]]; - }; - - LGMIDIShow.prototype.onGetOutputs = function() { - return [["on_midi", LiteGraph.EVENT]]; - }; - - LiteGraph.registerNodeType("midi/show", LGMIDIShow); - - function LGMIDIFilter() { - this.properties = { - channel: -1, - cmd: -1, - min_value: -1, - max_value: -1 - }; - - var that = this; - this._learning = false; - this.addWidget("button", "Learn", "", function() { - that._learning = true; - that.boxcolor = "#FA3"; - }); - - this.addInput("in", LiteGraph.EVENT); - this.addOutput("on_midi", LiteGraph.EVENT); - this.boxcolor = "#AAA"; - } - - LGMIDIFilter.title = "MIDI Filter"; - LGMIDIFilter.desc = "Filters MIDI messages"; - LGMIDIFilter.color = MIDI_COLOR; - - LGMIDIFilter["@cmd"] = { - type: "enum", - title: "Command", - values: MIDIEvent.commands_reversed - }; - - LGMIDIFilter.prototype.getTitle = function() { - var str = null; - if (this.properties.cmd == -1) { - str = "Nothing"; - } else { - str = MIDIEvent.commands_short[this.properties.cmd] || "Unknown"; - } - - if ( - this.properties.min_value != -1 && - this.properties.max_value != -1 - ) { - str += - " " + - (this.properties.min_value == this.properties.max_value - ? this.properties.max_value - : this.properties.min_value + - ".." + - this.properties.max_value); - } - - return "Filter: " + str; - }; - - LGMIDIFilter.prototype.onPropertyChanged = function(name, value) { - if (name == "cmd") { - var num = Number(value); - if (isNaN(num)) { - num = MIDIEvent.commands[value] || 0; - } - this.properties.cmd = num; - } - }; - - LGMIDIFilter.prototype.onAction = function(event, midi_event) { - if (!midi_event || midi_event.constructor !== MIDIEvent) { - return; - } - - if (this._learning) { - this._learning = false; - this.boxcolor = "#AAA"; - this.properties.channel = midi_event.channel; - this.properties.cmd = midi_event.cmd; - this.properties.min_value = this.properties.max_value = - midi_event.data[1]; - } else { - if ( - this.properties.channel != -1 && - midi_event.channel != this.properties.channel - ) { - return; - } - if ( - this.properties.cmd != -1 && - midi_event.cmd != this.properties.cmd - ) { - return; - } - if ( - this.properties.min_value != -1 && - midi_event.data[1] < this.properties.min_value - ) { - return; - } - if ( - this.properties.max_value != -1 && - midi_event.data[1] > this.properties.max_value - ) { - return; - } - } - - this.trigger("on_midi", midi_event); - }; - - LiteGraph.registerNodeType("midi/filter", LGMIDIFilter); - - function LGMIDIEvent() { - this.properties = { - channel: 0, - cmd: 144, //0x90 - value1: 1, - value2: 1 - }; - - this.addInput("send", LiteGraph.EVENT); - this.addInput("assign", LiteGraph.EVENT); - this.addOutput("on_midi", LiteGraph.EVENT); - - this.midi_event = new MIDIEvent(); - this.gate = false; - } - - LGMIDIEvent.title = "MIDIEvent"; - LGMIDIEvent.desc = "Create a MIDI Event"; - LGMIDIEvent.color = MIDI_COLOR; - - LGMIDIEvent.prototype.onAction = function(event, midi_event) { - if (event == "assign") { - this.properties.channel = midi_event.channel; - this.properties.cmd = midi_event.cmd; - this.properties.value1 = midi_event.data[1]; - this.properties.value2 = midi_event.data[2]; - if (midi_event.cmd == MIDIEvent.NOTEON) { - this.gate = true; - } else if (midi_event.cmd == MIDIEvent.NOTEOFF) { - this.gate = false; - } - return; - } - - //send - var midi_event = this.midi_event; - midi_event.channel = this.properties.channel; - if (this.properties.cmd && this.properties.cmd.constructor === String) { - midi_event.setCommandFromString(this.properties.cmd); - } else { - midi_event.cmd = this.properties.cmd; - } - midi_event.data[0] = midi_event.cmd | midi_event.channel; - midi_event.data[1] = Number(this.properties.value1); - midi_event.data[2] = Number(this.properties.value2); - - this.trigger("on_midi", midi_event); - }; - - LGMIDIEvent.prototype.onExecute = function() { - var props = this.properties; - - if (this.inputs) { - for (var i = 0; i < this.inputs.length; ++i) { - var input = this.inputs[i]; - if (input.link == -1) { - continue; - } - switch (input.name) { - case "note": - var v = this.getInputData(i); - if (v != null) { - if (v.constructor === String) { - v = MIDIEvent.NoteStringToPitch(v); - } - this.properties.value1 = (v | 0) % 255; - } - break; - case "cmd": - var v = this.getInputData(i); - if (v != null) { - this.properties.cmd = v; - } - break; - case "value1": - var v = this.getInputData(i); - if (v != null) { - this.properties.value1 = clamp(v|0,0,127); - } - break; - case "value2": - var v = this.getInputData(i); - if (v != null) { - this.properties.value2 = clamp(v|0,0,127); - } - break; - } - } - } - - if (this.outputs) { - for (var i = 0; i < this.outputs.length; ++i) { - var output = this.outputs[i]; - var v = null; - switch (output.name) { - case "midi": - v = new MIDIEvent(); - v.setup([props.cmd, props.value1, props.value2]); - v.channel = props.channel; - break; - case "command": - v = props.cmd; - break; - case "cc": - v = props.value1; - break; - case "cc_value": - v = props.value2; - break; - case "note": - v = - props.cmd == MIDIEvent.NOTEON || - props.cmd == MIDIEvent.NOTEOFF - ? props.value1 - : null; - break; - case "velocity": - v = props.cmd == MIDIEvent.NOTEON ? props.value2 : null; - break; - case "pitch": - v = - props.cmd == MIDIEvent.NOTEON - ? MIDIEvent.computePitch(props.value1) - : null; - break; - case "pitchbend": - v = - props.cmd == MIDIEvent.PITCHBEND - ? MIDIEvent.computePitchBend( - props.value1, - props.value2 - ) - : null; - break; - case "gate": - v = this.gate; - break; - default: - continue; - } - if (v !== null) { - this.setOutputData(i, v); - } - } - } - }; - - LGMIDIEvent.prototype.onPropertyChanged = function(name, value) { - if (name == "cmd") { - this.properties.cmd = MIDIEvent.computeCommandFromString(value); - } - }; - - LGMIDIEvent.prototype.onGetInputs = function() { - return [["cmd", "number"],["note", "number"],["value1", "number"],["value2", "number"]]; - }; - - LGMIDIEvent.prototype.onGetOutputs = function() { - return [ - ["midi", "midi"], - ["on_midi", LiteGraph.EVENT], - ["command", "number"], - ["note", "number"], - ["velocity", "number"], - ["cc", "number"], - ["cc_value", "number"], - ["pitch", "number"], - ["gate", "bool"], - ["pitchbend", "number"] - ]; - }; - - LiteGraph.registerNodeType("midi/event", LGMIDIEvent); - - function LGMIDICC() { - this.properties = { - // channel: 0, - cc: 1, - value: 0 - }; - - this.addOutput("value", "number"); - } - - LGMIDICC.title = "MIDICC"; - LGMIDICC.desc = "gets a Controller Change"; - LGMIDICC.color = MIDI_COLOR; - - LGMIDICC.prototype.onExecute = function() { - var props = this.properties; - if (MIDIInterface.input) { - this.properties.value = - MIDIInterface.input.state.cc[this.properties.cc]; - } - this.setOutputData(0, this.properties.value); - }; - - LiteGraph.registerNodeType("midi/cc", LGMIDICC); - - function LGMIDIGenerator() { - this.addInput("generate", LiteGraph.ACTION); - this.addInput("scale", "string"); - this.addInput("octave", "number"); - this.addOutput("note", LiteGraph.EVENT); - this.properties = { - notes: "A,A#,B,C,C#,D,D#,E,F,F#,G,G#", - octave: 2, - duration: 0.5, - mode: "sequence" - }; - - this.notes_pitches = LGMIDIGenerator.processScale( - this.properties.notes - ); - this.sequence_index = 0; - } - - LGMIDIGenerator.title = "MIDI Generator"; - LGMIDIGenerator.desc = "Generates a random MIDI note"; - LGMIDIGenerator.color = MIDI_COLOR; - - LGMIDIGenerator.processScale = function(scale) { - var notes = scale.split(","); - for (var i = 0; i < notes.length; ++i) { - var n = notes[i]; - if ((n.length == 2 && n[1] != "#") || n.length > 2) { - notes[i] = -LiteGraph.MIDIEvent.NoteStringToPitch(n); - } else { - notes[i] = MIDIEvent.note_to_index[n] || 0; - } - } - return notes; - }; - - LGMIDIGenerator.prototype.onPropertyChanged = function(name, value) { - if (name == "notes") { - this.notes_pitches = LGMIDIGenerator.processScale(value); - } - }; - - LGMIDIGenerator.prototype.onExecute = function() { - var octave = this.getInputData(2); - if (octave != null) { - this.properties.octave = octave; - } - - var scale = this.getInputData(1); - if (scale) { - this.notes_pitches = LGMIDIGenerator.processScale(scale); - } - }; - - LGMIDIGenerator.prototype.onAction = function(event, midi_event) { - //var range = this.properties.max - this.properties.min; - //var pitch = this.properties.min + ((Math.random() * range)|0); - var pitch = 0; - var range = this.notes_pitches.length; - var index = 0; - - if (this.properties.mode == "sequence") { - index = this.sequence_index = (this.sequence_index + 1) % range; - } else if (this.properties.mode == "random") { - index = Math.floor(Math.random() * range); - } - - var note = this.notes_pitches[index]; - if (note >= 0) { - pitch = note + (this.properties.octave - 1) * 12 + 33; - } else { - pitch = -note; - } - - var midi_event = new MIDIEvent(); - midi_event.setup([MIDIEvent.NOTEON, pitch, 10]); - var duration = this.properties.duration || 1; - this.trigger("note", midi_event); - - //noteoff - setTimeout( - function() { - var midi_event = new MIDIEvent(); - midi_event.setup([MIDIEvent.NOTEOFF, pitch, 0]); - this.trigger("note", midi_event); - }.bind(this), - duration * 1000 - ); - }; - - LiteGraph.registerNodeType("midi/generator", LGMIDIGenerator); - - function LGMIDITranspose() { - this.properties = { - amount: 0 - }; - this.addInput("in", LiteGraph.ACTION); - this.addInput("amount", "number"); - this.addOutput("out", LiteGraph.EVENT); - - this.midi_event = new MIDIEvent(); - } - - LGMIDITranspose.title = "MIDI Transpose"; - LGMIDITranspose.desc = "Transpose a MIDI note"; - LGMIDITranspose.color = MIDI_COLOR; - - LGMIDITranspose.prototype.onAction = function(event, midi_event) { - if (!midi_event || midi_event.constructor !== MIDIEvent) { - return; - } - - if ( - midi_event.data[0] == MIDIEvent.NOTEON || - midi_event.data[0] == MIDIEvent.NOTEOFF - ) { - this.midi_event = new MIDIEvent(); - this.midi_event.setup(midi_event.data); - this.midi_event.data[1] = Math.round( - this.midi_event.data[1] + this.properties.amount - ); - this.trigger("out", this.midi_event); - } else { - this.trigger("out", midi_event); - } - }; - - LGMIDITranspose.prototype.onExecute = function() { - var amount = this.getInputData(1); - if (amount != null) { - this.properties.amount = amount; - } - }; - - LiteGraph.registerNodeType("midi/transpose", LGMIDITranspose); - - function LGMIDIQuantize() { - this.properties = { - scale: "A,A#,B,C,C#,D,D#,E,F,F#,G,G#" - }; - this.addInput("note", LiteGraph.ACTION); - this.addInput("scale", "string"); - this.addOutput("out", LiteGraph.EVENT); - - this.valid_notes = new Array(12); - this.offset_notes = new Array(12); - this.processScale(this.properties.scale); - } - - LGMIDIQuantize.title = "MIDI Quantize Pitch"; - LGMIDIQuantize.desc = "Transpose a MIDI note tp fit an scale"; - LGMIDIQuantize.color = MIDI_COLOR; - - LGMIDIQuantize.prototype.onPropertyChanged = function(name, value) { - if (name == "scale") { - this.processScale(value); - } - }; - - LGMIDIQuantize.prototype.processScale = function(scale) { - this._current_scale = scale; - this.notes_pitches = LGMIDIGenerator.processScale(scale); - for (var i = 0; i < 12; ++i) { - this.valid_notes[i] = this.notes_pitches.indexOf(i) != -1; - } - for (var i = 0; i < 12; ++i) { - if (this.valid_notes[i]) { - this.offset_notes[i] = 0; - continue; - } - for (var j = 1; j < 12; ++j) { - if (this.valid_notes[(i - j) % 12]) { - this.offset_notes[i] = -j; - break; - } - if (this.valid_notes[(i + j) % 12]) { - this.offset_notes[i] = j; - break; - } - } - } - }; - - LGMIDIQuantize.prototype.onAction = function(event, midi_event) { - if (!midi_event || midi_event.constructor !== MIDIEvent) { - return; - } - - if ( - midi_event.data[0] == MIDIEvent.NOTEON || - midi_event.data[0] == MIDIEvent.NOTEOFF - ) { - this.midi_event = new MIDIEvent(); - this.midi_event.setup(midi_event.data); - var note = midi_event.note; - var index = MIDIEvent.note_to_index[note]; - var offset = this.offset_notes[index]; - this.midi_event.data[1] += offset; - this.trigger("out", this.midi_event); - } else { - this.trigger("out", midi_event); - } - }; - - LGMIDIQuantize.prototype.onExecute = function() { - var scale = this.getInputData(1); - if (scale != null && scale != this._current_scale) { - this.processScale(scale); - } - }; - - LiteGraph.registerNodeType("midi/quantize", LGMIDIQuantize); - - function LGMIDIFromFile() { - this.properties = { - url: "", - autoplay: true - }; - - this.addInput("play", LiteGraph.ACTION); - this.addInput("pause", LiteGraph.ACTION); - this.addOutput("note", LiteGraph.EVENT); - this._midi = null; - this._current_time = 0; - this._playing = false; - - if (typeof MidiParser == "undefined") { - console.error( - "midi-parser.js not included, LGMidiPlay requires that library: https://raw.githubusercontent.com/colxi/midi-parser-js/master/src/main.js" - ); - this.boxcolor = "red"; - } - - } - - LGMIDIFromFile.title = "MIDI fromFile"; - LGMIDIFromFile.desc = "Plays a MIDI file"; - LGMIDIFromFile.color = MIDI_COLOR; - - LGMIDIFromFile.prototype.onAction = function( name ) - { - if(name == "play") - this.play(); - else if(name == "pause") - this._playing = !this._playing; - } - - LGMIDIFromFile.prototype.onPropertyChanged = function(name,value) - { - if(name == "url") - this.loadMIDIFile(value); - } - - LGMIDIFromFile.prototype.onExecute = function() { - if(!this._midi) - return; - - if(!this._playing) - return; - - this._current_time += this.graph.elapsed_time; - var current_time = this._current_time * 100; - - for(var i = 0; i < this._midi.tracks; ++i) - { - var track = this._midi.track[i]; - if(!track._last_pos) - { - track._last_pos = 0; - track._time = 0; - } - - var elem = track.event[ track._last_pos ]; - if(elem && (track._time + elem.deltaTime) <= current_time ) - { - track._last_pos++; - track._time += elem.deltaTime; - - if(elem.data) - { - var midi_cmd = elem.type << 4 + elem.channel; - var midi_event = new MIDIEvent(); - midi_event.setup([midi_cmd, elem.data[0], elem.data[1]]); - this.trigger("note", midi_event); - } - } - - } - }; - - LGMIDIFromFile.prototype.play = function() - { - this._playing = true; - this._current_time = 0; - if(!this._midi) - return; - - for(var i = 0; i < this._midi.tracks; ++i) - { - var track = this._midi.track[i]; - track._last_pos = 0; - track._time = 0; - } - } - - LGMIDIFromFile.prototype.loadMIDIFile = function(url) - { - var that = this; - LiteGraph.fetchFile( url, "arraybuffer", function(data) - { - that.boxcolor = "#AFA"; - that._midi = MidiParser.parse( new Uint8Array(data) ); - if(that.properties.autoplay) - that.play(); - }, function(err){ - that.boxcolor = "#FAA"; - that._midi = null; - }); - } - - LGMIDIFromFile.prototype.onDropFile = function(file) - { - this.properties.url = ""; - this.loadMIDIFile( file ); - } - - LiteGraph.registerNodeType("midi/fromFile", LGMIDIFromFile); - - - function LGMIDIPlay() { - this.properties = { - volume: 0.5, - duration: 1 - }; - this.addInput("note", LiteGraph.ACTION); - this.addInput("volume", "number"); - this.addInput("duration", "number"); - this.addOutput("note", LiteGraph.EVENT); - - if (typeof AudioSynth == "undefined") { - console.error( - "Audiosynth.js not included, LGMidiPlay requires that library" - ); - this.boxcolor = "red"; - } else { - var Synth = (this.synth = new AudioSynth()); - this.instrument = Synth.createInstrument("piano"); - } - } - - LGMIDIPlay.title = "MIDI Play"; - LGMIDIPlay.desc = "Plays a MIDI note"; - LGMIDIPlay.color = MIDI_COLOR; - - LGMIDIPlay.prototype.onAction = function(event, midi_event) { - if (!midi_event || midi_event.constructor !== MIDIEvent) { - return; - } - - if (this.instrument && midi_event.data[0] == MIDIEvent.NOTEON) { - var note = midi_event.note; //C# - if (!note || note == "undefined" || note.constructor !== String) { - return; - } - this.instrument.play( - note, - midi_event.octave, - this.properties.duration, - this.properties.volume - ); - } - this.trigger("note", midi_event); - }; - - LGMIDIPlay.prototype.onExecute = function() { - var volume = this.getInputData(1); - if (volume != null) { - this.properties.volume = volume; - } - - var duration = this.getInputData(2); - if (duration != null) { - this.properties.duration = duration; - } - }; - - LiteGraph.registerNodeType("midi/play", LGMIDIPlay); - - function LGMIDIKeys() { - this.properties = { - num_octaves: 2, - start_octave: 2 - }; - this.addInput("note", LiteGraph.ACTION); - this.addInput("reset", LiteGraph.ACTION); - this.addOutput("note", LiteGraph.EVENT); - this.size = [400, 100]; - this.keys = []; - this._last_key = -1; - } - - LGMIDIKeys.title = "MIDI Keys"; - LGMIDIKeys.desc = "Keyboard to play notes"; - LGMIDIKeys.color = MIDI_COLOR; - - LGMIDIKeys.keys = [ - { x: 0, w: 1, h: 1, t: 0 }, - { x: 0.75, w: 0.5, h: 0.6, t: 1 }, - { x: 1, w: 1, h: 1, t: 0 }, - { x: 1.75, w: 0.5, h: 0.6, t: 1 }, - { x: 2, w: 1, h: 1, t: 0 }, - { x: 2.75, w: 0.5, h: 0.6, t: 1 }, - { x: 3, w: 1, h: 1, t: 0 }, - { x: 4, w: 1, h: 1, t: 0 }, - { x: 4.75, w: 0.5, h: 0.6, t: 1 }, - { x: 5, w: 1, h: 1, t: 0 }, - { x: 5.75, w: 0.5, h: 0.6, t: 1 }, - { x: 6, w: 1, h: 1, t: 0 } - ]; - - LGMIDIKeys.prototype.onDrawForeground = function(ctx) { - if (this.flags.collapsed) { - return; - } - - var num_keys = this.properties.num_octaves * 12; - this.keys.length = num_keys; - var key_width = this.size[0] / (this.properties.num_octaves * 7); - var key_height = this.size[1]; - - ctx.globalAlpha = 1; - - for ( - var k = 0; - k < 2; - k++ //draw first whites (0) then blacks (1) - ) { - for (var i = 0; i < num_keys; ++i) { - var key_info = LGMIDIKeys.keys[i % 12]; - if (key_info.t != k) { - continue; - } - var octave = Math.floor(i / 12); - var x = octave * 7 * key_width + key_info.x * key_width; - if (k == 0) { - ctx.fillStyle = this.keys[i] ? "#CCC" : "white"; - } else { - ctx.fillStyle = this.keys[i] ? "#333" : "black"; - } - ctx.fillRect( - x + 1, - 0, - key_width * key_info.w - 2, - key_height * key_info.h - ); - } - } - }; - - LGMIDIKeys.prototype.getKeyIndex = function(pos) { - var num_keys = this.properties.num_octaves * 12; - var key_width = this.size[0] / (this.properties.num_octaves * 7); - var key_height = this.size[1]; - - for ( - var k = 1; - k >= 0; - k-- //test blacks first (1) then whites (0) - ) { - for (var i = 0; i < this.keys.length; ++i) { - var key_info = LGMIDIKeys.keys[i % 12]; - if (key_info.t != k) { - continue; - } - var octave = Math.floor(i / 12); - var x = octave * 7 * key_width + key_info.x * key_width; - var w = key_width * key_info.w; - var h = key_height * key_info.h; - if (pos[0] < x || pos[0] > x + w || pos[1] > h) { - continue; - } - return i; - } - } - return -1; - }; - - LGMIDIKeys.prototype.onAction = function(event, params) { - if (event == "reset") { - for (var i = 0; i < this.keys.length; ++i) { - this.keys[i] = false; - } - return; - } - - if (!params || params.constructor !== MIDIEvent) { - return; - } - var midi_event = params; - var start_note = (this.properties.start_octave - 1) * 12 + 29; - var index = midi_event.data[1] - start_note; - if (index >= 0 && index < this.keys.length) { - if (midi_event.data[0] == MIDIEvent.NOTEON) { - this.keys[index] = true; - } else if (midi_event.data[0] == MIDIEvent.NOTEOFF) { - this.keys[index] = false; - } - } - - this.trigger("note", midi_event); - }; - - LGMIDIKeys.prototype.onMouseDown = function(e, pos) { - if (pos[1] < 0) { - return; - } - var index = this.getKeyIndex(pos); - this.keys[index] = true; - this._last_key = index; - var pitch = (this.properties.start_octave - 1) * 12 + 29 + index; - var midi_event = new MIDIEvent(); - midi_event.setup([MIDIEvent.NOTEON, pitch, 100]); - this.trigger("note", midi_event); - return true; - }; - - LGMIDIKeys.prototype.onMouseMove = function(e, pos) { - if (pos[1] < 0 || this._last_key == -1) { - return; - } - this.setDirtyCanvas(true); - var index = this.getKeyIndex(pos); - if (this._last_key == index) { - return true; - } - this.keys[this._last_key] = false; - var pitch = - (this.properties.start_octave - 1) * 12 + 29 + this._last_key; - var midi_event = new MIDIEvent(); - midi_event.setup([MIDIEvent.NOTEOFF, pitch, 100]); - this.trigger("note", midi_event); - - this.keys[index] = true; - var pitch = (this.properties.start_octave - 1) * 12 + 29 + index; - var midi_event = new MIDIEvent(); - midi_event.setup([MIDIEvent.NOTEON, pitch, 100]); - this.trigger("note", midi_event); - - this._last_key = index; - return true; - }; - - LGMIDIKeys.prototype.onMouseUp = function(e, pos) { - if (pos[1] < 0) { - return; - } - var index = this.getKeyIndex(pos); - this.keys[index] = false; - this._last_key = -1; - var pitch = (this.properties.start_octave - 1) * 12 + 29 + index; - var midi_event = new MIDIEvent(); - midi_event.setup([MIDIEvent.NOTEOFF, pitch, 100]); - this.trigger("note", midi_event); - return true; - }; - - LiteGraph.registerNodeType("midi/keys", LGMIDIKeys); - - function now() { - return window.performance.now(); - } -})(this); diff --git a/src/nodes/network.js b/src/nodes/network.js deleted file mode 100755 index 340016bc4..000000000 --- a/src/nodes/network.js +++ /dev/null @@ -1,428 +0,0 @@ -//event related nodes -(function(global) { - var LiteGraph = global.LiteGraph; - - function LGWebSocket() { - this.size = [60, 20]; - this.addInput("send", LiteGraph.ACTION); - this.addOutput("received", LiteGraph.EVENT); - this.addInput("in", 0); - this.addOutput("out", 0); - this.properties = { - url: "", - room: "lgraph", //allows to filter messages, - only_send_changes: true - }; - this._ws = null; - this._last_sent_data = []; - this._last_received_data = []; - } - - LGWebSocket.title = "WebSocket"; - LGWebSocket.desc = "Send data through a websocket"; - - LGWebSocket.prototype.onPropertyChanged = function(name, value) { - if (name == "url") { - this.connectSocket(); - } - }; - - LGWebSocket.prototype.onExecute = function() { - if (!this._ws && this.properties.url) { - this.connectSocket(); - } - - if (!this._ws || this._ws.readyState != WebSocket.OPEN) { - return; - } - - var room = this.properties.room; - var only_changes = this.properties.only_send_changes; - - for (var i = 1; i < this.inputs.length; ++i) { - var data = this.getInputData(i); - if (data == null) { - continue; - } - var json; - try { - json = JSON.stringify({ - type: 0, - room: room, - channel: i, - data: data - }); - } catch (err) { - continue; - } - if (only_changes && this._last_sent_data[i] == json) { - continue; - } - - this._last_sent_data[i] = json; - this._ws.send(json); - } - - for (var i = 1; i < this.outputs.length; ++i) { - this.setOutputData(i, this._last_received_data[i]); - } - - if (this.boxcolor == "#AFA") { - this.boxcolor = "#6C6"; - } - }; - - LGWebSocket.prototype.connectSocket = function() { - var that = this; - var url = this.properties.url; - if (url.substr(0, 2) != "ws") { - url = "ws://" + url; - } - this._ws = new WebSocket(url); - this._ws.onopen = function() { - console.log("ready"); - that.boxcolor = "#6C6"; - }; - this._ws.onmessage = function(e) { - that.boxcolor = "#AFA"; - var data = JSON.parse(e.data); - if (data.room && data.room != that.properties.room) { - return; - } - if (data.type == 1) { - if ( - data.data.object_class && - LiteGraph[data.data.object_class] - ) { - var obj = null; - try { - obj = new LiteGraph[data.data.object_class](data.data); - that.triggerSlot(0, obj); - } catch (err) { - return; - } - } else { - that.triggerSlot(0, data.data); - } - } else { - that._last_received_data[data.channel || 0] = data.data; - } - }; - this._ws.onerror = function(e) { - console.log("couldnt connect to websocket"); - that.boxcolor = "#E88"; - }; - this._ws.onclose = function(e) { - console.log("connection closed"); - that.boxcolor = "#000"; - }; - }; - - LGWebSocket.prototype.send = function(data) { - if (!this._ws || this._ws.readyState != WebSocket.OPEN) { - return; - } - this._ws.send(JSON.stringify({ type: 1, msg: data })); - }; - - LGWebSocket.prototype.onAction = function(action, param) { - if (!this._ws || this._ws.readyState != WebSocket.OPEN) { - return; - } - this._ws.send({ - type: 1, - room: this.properties.room, - action: action, - data: param - }); - }; - - LGWebSocket.prototype.onGetInputs = function() { - return [["in", 0]]; - }; - - LGWebSocket.prototype.onGetOutputs = function() { - return [["out", 0]]; - }; - - LiteGraph.registerNodeType("network/websocket", LGWebSocket); - - //It is like a websocket but using the SillyServer.js server that bounces packets back to all clients connected: - //For more information: https://github.com/jagenjo/SillyServer.js - - function LGSillyClient() { - //this.size = [60,20]; - this.room_widget = this.addWidget( - "text", - "Room", - "lgraph", - this.setRoom.bind(this) - ); - this.addWidget( - "button", - "Reconnect", - null, - this.connectSocket.bind(this) - ); - - this.addInput("send", LiteGraph.ACTION); - this.addOutput("received", LiteGraph.EVENT); - this.addInput("in", 0); - this.addOutput("out", 0); - this.properties = { - url: "tamats.com:55000", - room: "lgraph", - only_send_changes: true - }; - - this._server = null; - this.connectSocket(); - this._last_sent_data = []; - this._last_received_data = []; - - if(typeof(SillyClient) == "undefined") - console.warn("remember to add SillyClient.js to your project: https://tamats.com/projects/sillyserver/src/sillyclient.js"); - } - - LGSillyClient.title = "SillyClient"; - LGSillyClient.desc = "Connects to SillyServer to broadcast messages"; - - LGSillyClient.prototype.onPropertyChanged = function(name, value) { - if (name == "room") { - this.room_widget.value = value; - } - this.connectSocket(); - }; - - LGSillyClient.prototype.setRoom = function(room_name) { - this.properties.room = room_name; - this.room_widget.value = room_name; - this.connectSocket(); - }; - - //force label names - LGSillyClient.prototype.onDrawForeground = function() { - for (var i = 1; i < this.inputs.length; ++i) { - var slot = this.inputs[i]; - slot.label = "in_" + i; - } - for (var i = 1; i < this.outputs.length; ++i) { - var slot = this.outputs[i]; - slot.label = "out_" + i; - } - }; - - LGSillyClient.prototype.onExecute = function() { - if (!this._server || !this._server.is_connected) { - return; - } - - var only_send_changes = this.properties.only_send_changes; - - for (var i = 1; i < this.inputs.length; ++i) { - var data = this.getInputData(i); - var prev_data = this._last_sent_data[i]; - if (data != null) { - if (only_send_changes) - { - var is_equal = true; - if( data && data.length && prev_data && prev_data.length == data.length && data.constructor !== String) - { - for(var j = 0; j < data.length; ++j) - if( prev_data[j] != data[j] ) - { - is_equal = false; - break; - } - } - else if(this._last_sent_data[i] != data) - is_equal = false; - if(is_equal) - continue; - } - this._server.sendMessage({ type: 0, channel: i, data: data }); - if( data.length && data.constructor !== String ) - { - if( this._last_sent_data[i] ) - { - this._last_sent_data[i].length = data.length; - for(var j = 0; j < data.length; ++j) - this._last_sent_data[i][j] = data[j]; - } - else //create - { - if(data.constructor === Array) - this._last_sent_data[i] = data.concat(); - else - this._last_sent_data[i] = new data.constructor( data ); - } - } - else - this._last_sent_data[i] = data; //should be cloned - } - } - - for (var i = 1; i < this.outputs.length; ++i) { - this.setOutputData(i, this._last_received_data[i]); - } - - if (this.boxcolor == "#AFA") { - this.boxcolor = "#6C6"; - } - }; - - LGSillyClient.prototype.connectSocket = function() { - var that = this; - if (typeof SillyClient == "undefined") { - if (!this._error) { - console.error( - "SillyClient node cannot be used, you must include SillyServer.js" - ); - } - this._error = true; - return; - } - - this._server = new SillyClient(); - this._server.on_ready = function() { - console.log("ready"); - that.boxcolor = "#6C6"; - }; - this._server.on_message = function(id, msg) { - var data = null; - try { - data = JSON.parse(msg); - } catch (err) { - return; - } - - if (data.type == 1) { - //EVENT slot - if ( - data.data.object_class && - LiteGraph[data.data.object_class] - ) { - var obj = null; - try { - obj = new LiteGraph[data.data.object_class](data.data); - that.triggerSlot(0, obj); - } catch (err) { - return; - } - } else { - that.triggerSlot(0, data.data); - } - } //for FLOW slots - else { - that._last_received_data[data.channel || 0] = data.data; - } - that.boxcolor = "#AFA"; - }; - this._server.on_error = function(e) { - console.log("couldnt connect to websocket"); - that.boxcolor = "#E88"; - }; - this._server.on_close = function(e) { - console.log("connection closed"); - that.boxcolor = "#000"; - }; - - if (this.properties.url && this.properties.room) { - try { - this._server.connect(this.properties.url, this.properties.room); - } catch (err) { - console.error("SillyServer error: " + err); - this._server = null; - return; - } - this._final_url = this.properties.url + "/" + this.properties.room; - } - }; - - LGSillyClient.prototype.send = function(data) { - if (!this._server || !this._server.is_connected) { - return; - } - this._server.sendMessage({ type: 1, data: data }); - }; - - LGSillyClient.prototype.onAction = function(action, param) { - if (!this._server || !this._server.is_connected) { - return; - } - this._server.sendMessage({ type: 1, action: action, data: param }); - }; - - LGSillyClient.prototype.onGetInputs = function() { - return [["in", 0]]; - }; - - LGSillyClient.prototype.onGetOutputs = function() { - return [["out", 0]]; - }; - - LiteGraph.registerNodeType("network/sillyclient", LGSillyClient); - -//HTTP Request -function HTTPRequestNode() { - var that = this; - this.addInput("request", LiteGraph.ACTION); - this.addInput("url", "string"); - this.addProperty("url", ""); - this.addOutput("ready", LiteGraph.EVENT); - this.addOutput("data", "string"); - this.addWidget("button", "Fetch", null, this.fetch.bind(this)); - this._data = null; - this._fetching = null; -} - -HTTPRequestNode.title = "HTTP Request"; -HTTPRequestNode.desc = "Fetch data through HTTP"; - -HTTPRequestNode.prototype.fetch = function() -{ - var url = this.properties.url; - if(!url) - return; - - this.boxcolor = "#FF0"; - var that = this; - this._fetching = fetch(url) - .then(resp=>{ - if(!resp.ok) - { - this.boxcolor = "#F00"; - that.trigger("error"); - } - else - { - this.boxcolor = "#0F0"; - return resp.text(); - } - }) - .then(data=>{ - that._data = data; - that._fetching = null; - that.trigger("ready"); - }); -} - -HTTPRequestNode.prototype.onAction = function(evt) -{ - if(evt == "request") - this.fetch(); -} - -HTTPRequestNode.prototype.onExecute = function() { - this.setOutputData(1, this._data); -}; - -HTTPRequestNode.prototype.onGetOutputs = function() { - return [["error",LiteGraph.EVENT]]; -} - -LiteGraph.registerNodeType("network/httprequest", HTTPRequestNode); - - - -})(this); diff --git a/src/nodes/others.js b/src/nodes/others.js deleted file mode 100755 index cca0e6f2b..000000000 --- a/src/nodes/others.js +++ /dev/null @@ -1,37 +0,0 @@ -(function(global) { - var LiteGraph = global.LiteGraph; - - /* in types :: run in console :: var s=""; LiteGraph.slot_types_in.forEach(function(el){s+=el+"\n";}); console.log(s); */ - - if(typeof LiteGraph.slot_types_default_in == "undefined") LiteGraph.slot_types_default_in = {}; //[]; - LiteGraph.slot_types_default_in["_event_"] = "widget/button"; - LiteGraph.slot_types_default_in["array"] = "basic/array"; - LiteGraph.slot_types_default_in["boolean"] = "basic/boolean"; - LiteGraph.slot_types_default_in["number"] = "widget/number"; - LiteGraph.slot_types_default_in["object"] = "basic/data"; - LiteGraph.slot_types_default_in["string"] = ["basic/string","string/concatenate"]; - LiteGraph.slot_types_default_in["vec2"] = "math3d/xy-to-vec2"; - LiteGraph.slot_types_default_in["vec3"] = "math3d/xyz-to-vec3"; - LiteGraph.slot_types_default_in["vec4"] = "math3d/xyzw-to-vec4"; - - /* out types :: run in console :: var s=""; LiteGraph.slot_types_out.forEach(function(el){s+=el+"\n";}); console.log(s); */ - if(typeof LiteGraph.slot_types_default_out == "undefined") LiteGraph.slot_types_default_out = {}; - LiteGraph.slot_types_default_out["_event_"] = ["logic/IF","events/sequencer","events/log","events/counter"]; - LiteGraph.slot_types_default_out["array"] = ["basic/watch","basic/set_array","basic/array[]"]; - LiteGraph.slot_types_default_out["boolean"] = ["logic/IF","basic/watch","math/branch","math/gate"]; - LiteGraph.slot_types_default_out["number"] = ["basic/watch" - ,{node:"math/operation",properties:{OP:"*"},title:"A*B"} - ,{node:"math/operation",properties:{OP:"/"},title:"A/B"} - ,{node:"math/operation",properties:{OP:"+"},title:"A+B"} - ,{node:"math/operation",properties:{OP:"-"},title:"A-B"} - ,{node:"math/compare",outputs:[["A==B", "boolean"]],title:"A==B"} - ,{node:"math/compare",outputs:[["A>B", "boolean"]],title:"A>B"} - ,{node:"math/compare",outputs:[["A console.log('Example app listening on http://127.0.0.1:8000')) diff --git a/utils/temp.js b/utils/temp.js deleted file mode 100755 index 833ccaada..000000000 --- a/utils/temp.js +++ /dev/null @@ -1,24 +0,0 @@ -LiteGraph.registerNodeType("color/palette",{title:"Palette",desc:"Generates a color",inputs:[["f","number"]],outputs:[["Color","color"]],properties:{colorA:"#444444",colorB:"#44AAFF",colorC:"#44FFAA",colorD:"#FFFFFF"},onExecute:function(){var a=[];null!=this.properties.colorA&&a.push(hex2num(this.properties.colorA));null!=this.properties.colorB&&a.push(hex2num(this.properties.colorB));null!=this.properties.colorC&&a.push(hex2num(this.properties.colorC));null!=this.properties.colorD&&a.push(hex2num(this.properties.colorD)); -var b=this.getInputData(0);null==b&&(b=0.5);1b&&(b=0);if(0!=a.length){var c=[0,0,0];if(0==b)c=a[0];else if(1==b)c=a[a.length-1];else{var d=(a.length-1)*b,b=a[Math.floor(d)],a=a[Math.floor(d)+1],d=d-Math.floor(d);c[0]=b[0]*(1-d)+a[0]*d;c[1]=b[1]*(1-d)+a[1]*d;c[2]=b[2]*(1-d)+a[2]*d}for(var e in c)c[e]/=255;this.boxcolor=colorToString(c);this.setOutputData(0,c)}}}); -LiteGraph.registerNodeType("graphics/frame",{title:"Frame",desc:"Frame viewerew",inputs:[["","image"]],size:[200,200],widgets:[{name:"resize",text:"Resize box",type:"button"},{name:"view",text:"View Image",type:"button"}],onDrawBackground:function(a){this.frame&&a.drawImage(this.frame,0,0,this.size[0],this.size[1])},onExecute:function(){this.frame=this.getInputData(0);this.setDirtyCanvas(!0)},onWidget:function(a,b){if("resize"==b.name&&this.frame){var c=this.frame.width,d=this.frame.height;c||null== -this.frame.videoWidth||(c=this.frame.videoWidth,d=this.frame.videoHeight);c&&d&&(this.size=[c,d]);this.setDirtyCanvas(!0,!0)}else"view"==b.name&&this.show()},show:function(){showElement&&this.frame&&showElement(this.frame)}}); -LiteGraph.registerNodeType("visualization/graph",{desc:"Shows a graph of the inputs",inputs:[["",0],["",0],["",0],["",0]],size:[200,200],properties:{min:-1,max:1,bgColor:"#000"},onDrawBackground:function(a){var b=["#FFF","#FAA","#AFA","#AAF"];null!=this.properties.bgColor&&""!=this.properties.bgColor&&(a.fillStyle="#000",a.fillRect(2,2,this.size[0]-4,this.size[1]-4));if(this.data){var c=this.properties.min,d=this.properties.max,e;for(e in this.data){var h=this.data[e];if(h&&null!=this.getInputInfo(e)){a.strokeStyle= -b[e];a.beginPath();for(var k=h.length/this.size[0],g=0;gf&&(f=0);0==g?a.moveTo(g/k,this.size[1]-5-(this.size[1]-10)*f):a.lineTo(g/k,this.size[1]-5-(this.size[1]-10)*f)}a.stroke()}}}},onExecute:function(){this.data||(this.data=[]);for(var a in this.inputs){var b=this.getInputData(a);"number"==typeof b?(b=b?b:0,this.data[a]||(this.data[a]=[]),this.data[a].push(b),this.data[a].length>this.size[1]-4&&(this.data[a]=this.data[a].slice(1,this.data[a].length))): -this.data[a]=b}this.data.length&&this.setDirtyCanvas(!0)}}); -LiteGraph.registerNodeType("graphics/supergraph",{title:"Supergraph",desc:"Shows a nice circular graph",inputs:[["x","number"],["y","number"],["c","color"]],outputs:[["","image"]],widgets:[{name:"clear_alpha",text:"Clear Alpha",type:"minibutton"},{name:"clear_color",text:"Clear color",type:"minibutton"}],properties:{size:256,bgcolor:"#000",lineWidth:1},bgcolor:"#000",flags:{allow_fastrender:!0},onLoad:function(){this.createCanvas()},createCanvas:function(){this.canvas=document.createElement("canvas"); -this.canvas.width=this.properties.size;this.canvas.height=this.properties.size;this.oldpos=null;this.clearCanvas(!0)},onExecute:function(){var a=this.getInputData(0),b=this.getInputData(1),c=this.getInputData(2);if(null!=a||null!=b){a||(a=0);b||(b=0);var a=0.95*a,b=0.95*b,d=this.properties.size;d==this.canvas.width&&d==this.canvas.height||this.createCanvas();if(this.oldpos){var e=this.canvas.getContext("2d");null==c?c="rgba(255,255,255,0.5)":"object"==typeof c&&(c=colorToString(c));e.strokeStyle= -c;e.beginPath();e.moveTo(this.oldpos[0],this.oldpos[1]);this.oldpos=[(0.5*a+0.5)*d,(0.5*b+0.5)*d];e.lineTo(this.oldpos[0],this.oldpos[1]);e.stroke();this.canvas.dirty=!0;this.setOutputData(0,this.canvas)}else this.oldpos=[(0.5*a+0.5)*d,(0.5*b+0.5)*d]}},clearCanvas:function(a){var b=this.canvas.getContext("2d");a?(b.clearRect(0,0,this.canvas.width,this.canvas.height),this.trace("Clearing alpha")):(b.fillStyle=this.properties.bgcolor,b.fillRect(0,0,this.canvas.width,this.canvas.height))},onWidget:function(a, -b){"clear_color"==b.name?this.clearCanvas(!1):"clear_alpha"==b.name&&this.clearCanvas(!0)},onPropertyChange:function(a,b){if("size"==a)this.properties.size=parseInt(b),this.createCanvas();else if("bgcolor"==a)this.properties.bgcolor=b,this.createCanvas();else if("lineWidth"==a)this.properties.lineWidth=parseInt(b),this.canvas.getContext("2d").lineWidth=this.properties.lineWidth;else return!1;return!0}}); -LiteGraph.registerNodeType("graphics/imagefade",{title:"Image fade",desc:"Fades between images",inputs:[["img1","image"],["img2","image"],["fade","number"]],outputs:[["","image"]],properties:{fade:0.5,width:512,height:512},widgets:[{name:"resizeA",text:"Resize to A",type:"button"},{name:"resizeB",text:"Resize to B",type:"button"}],onLoad:function(){this.createCanvas();var a=this.canvas.getContext("2d");a.fillStyle="#000";a.fillRect(0,0,this.properties.width,this.properties.height)},createCanvas:function(){this.canvas= -document.createElement("canvas");this.canvas.width=this.properties.width;this.canvas.height=this.properties.height},onExecute:function(){var a=this.canvas.getContext("2d");this.canvas.width=this.canvas.width;var b=this.getInputData(0);null!=b&&a.drawImage(b,0,0,this.canvas.width,this.canvas.height);b=this.getInputData(2);null==b?b=this.properties.fade:this.properties.fade=b;a.globalAlpha=b;b=this.getInputData(1);null!=b&&a.drawImage(b,0,0,this.canvas.width,this.canvas.height);a.globalAlpha=1;this.setOutputData(0, -this.canvas);this.setDirtyCanvas(!0)}}); -LiteGraph.registerNodeType("graphics/image",{title:"Image",desc:"Image loader",inputs:[],outputs:[["frame","image"]],properties:{url:""},widgets:[{name:"load",text:"Load",type:"button"}],onLoad:function(){""!=this.properties.url&&null==this.img&&this.loadImage(this.properties.url)},onStart:function(){},onExecute:function(){this.img||(this.boxcolor="#000");this.img&&this.img.width?this.setOutputData(0,this.img):this.setOutputData(0,null);this.img.dirty&&(this.img.dirty=!1)},onPropertyChange:function(a, -b){this.properties[a]=b;"url"==a&&""!=b&&this.loadImage(b);return!0},loadImage:function(a){if(""==a)this.img=null;else{this.trace("loading image...");this.img=document.createElement("img");this.img.src="miniproxy.php?url="+a;this.boxcolor="#F95";var b=this;this.img.onload=function(){b.trace("Image loaded, size: "+b.img.width+"x"+b.img.height);this.dirty=!0;b.boxcolor="#9F9";b.setDirtyCanvas(!0)}}},onWidget:function(a,b){"load"==b.name&&this.loadImage(this.properties.url)}}); -LiteGraph.registerNodeType("graphics/cropImage",{title:"Crop",desc:"Crop Image",inputs:[["","image"]],outputs:[["","image"]],properties:{width:256,height:256,x:0,y:0,scale:1},size:[50,20],onLoad:function(){this.createCanvas()},createCanvas:function(){this.canvas=document.createElement("canvas");this.canvas.width=this.properties.width;this.canvas.height=this.properties.height},onExecute:function(){var a=this.getInputData(0);a&&(a.width?(this.canvas.getContext("2d").drawImage(a,-this.properties.x,-this.properties.y, -a.width*this.properties.scale,a.height*this.properties.scale),this.setOutputData(0,this.canvas)):this.setOutputData(0,null))},onPropertyChange:function(a,b){this.properties[a]=b;"scale"==a?(this.properties[a]=parseFloat(b),0==this.properties[a]&&(this.trace("Error in scale"),this.properties[a]=1)):this.properties[a]=parseInt(b);this.createCanvas();return!0}}); -LiteGraph.registerNodeType("graphics/video",{title:"Video",desc:"Video playback",inputs:[["t","number"]],outputs:[["frame","image"],["t","number"],["d","number"]],properties:{url:""},widgets:[{name:"play",text:"PLAY",type:"minibutton"},{name:"stop",text:"STOP",type:"minibutton"},{name:"demo",text:"Demo video",type:"button"},{name:"mute",text:"Mute video",type:"button"}],onClick:function(a){if(this.video&&20>distance([a.canvasX,a.canvasY],[this.pos[0]+55,this.pos[1]+40]))return this.play(),!0},onKeyDown:function(a){32== -a.keyCode&&this.playPause()},onLoad:function(){""!=this.properties.url&&this.loadVideo(this.properties.url)},play:function(){this.video&&(this.trace("Video playing"),this.video.play())},playPause:function(){this.video&&(this.video.paused?this.play():this.pause())},stop:function(){this.video&&(this.trace("Video stopped"),this.video.pause(),this.video.currentTime=0)},pause:function(){this.video&&(this.trace("Video paused"),this.video.pause())},onExecute:function(){if(this.video){var a=this.getInputData(0); -a&&0<=a&&1>=a&&(this.video.currentTime=a*this.video.duration,this.video.pause());this.video.dirty=!0;this.setOutputData(0,this.video);this.setOutputData(1,this.video.currentTime);this.setOutputData(2,this.video.duration);this.setDirtyCanvas(!0)}},onStart:function(){},onStop:function(){this.pause()},loadVideo:function(a){this.video=document.createElement("video");a?this.video.src=a:(this.video.src="modules/data/video.webm",this.properties.url=this.video.src);this.video.type="type=video/mp4";this.video.muted= -!0;this.video.autoplay=!1;var b=this;this.video.addEventListener("loadedmetadata",function(a){b.trace("Duration: "+b.video.duration+" seconds");b.trace("Size: "+b.video.videoWidth+","+b.video.videoHeight);b.setDirtyCanvas(!0);this.width=this.videoWidth;this.height=this.videoHeight});this.video.addEventListener("progress",function(a){});this.video.addEventListener("error",function(a){b.trace("Error loading video: "+this.src);if(this.error)switch(this.error.code){case this.error.MEDIA_ERR_ABORTED:b.trace("You stopped the video."); -break;case this.error.MEDIA_ERR_NETWORK:b.trace("Network error - please try again later.");break;case this.error.MEDIA_ERR_DECODE:b.trace("Video is broken..");break;case this.error.MEDIA_ERR_SRC_NOT_SUPPORTED:b.trace("Sorry, your browser can't play this video.")}});this.video.addEventListener("ended",function(a){b.trace("Ended.");this.play()})},onPropertyChange:function(a,b){this.properties[a]=b;"url"==a&&""!=b&&this.loadVideo(b);return!0},onWidget:function(a,b){"demo"==b.name?this.loadVideo():"play"== -b.name&&this.video&&this.playPause();"stop"==b.name?this.stop():"mute"==b.name&&this.video&&(this.video.muted=!this.video.muted)}}); diff --git a/utils/test.sh b/utils/test.sh deleted file mode 100755 index 9794b81ea..000000000 --- a/utils/test.sh +++ /dev/null @@ -1,16 +0,0 @@ -#!/bin/bash - -set -eo pipefail -cd "$(dirname "$(readlink -f "${BASH_SOURCE[0]}")")" - -export NVM_DIR=$HOME/.nvm -source "$NVM_DIR/nvm.sh" - -# This are versions 12, 14, 16, 18 -NODE_VERSIONS=("lts/erbium" "lts/fermium" "lts/gallium" "lts/hydrogen") - -for NODE_VERSION in "${NODE_VERSIONS[@]}"; do - nvm install "$NODE_VERSION" - nvm exec "$NODE_VERSION" npm install - nvm exec "$NODE_VERSION" npm test -done