From a121fda158cc38c29a1667a4ddd6d3aade98b13b Mon Sep 17 00:00:00 2001 From: Carlos Roig Date: Wed, 3 Nov 2021 18:06:20 +0100 Subject: [PATCH 01/18] Major changes. Revamped UI to modern standards. Added support for stages (should help us to figure out what we want) Added support for glyphed fonts to help `those users` Added proto support for importing/exporting selected elements --- css/problemtype-editor.css | 319 ++++++++++++++++++ style.css => css/style.css | 5 +- index.html | 142 ++++---- js/code.js | 54 ++- js/nodes/parsed_model_part.js | 2 +- .../fluid_monolithic_solver.js | 2 +- js/nodes/stages/modeler.js | 20 ++ js/nodes/stages/post_process.js | 20 ++ js/nodes/stages/pre_process.js | 21 ++ js/nodes/stages/stage.js | 22 ++ js/problemtype-editor.js | 245 ++++++++++++++ resources/selection.json | 1 + suggestions.md | 16 + 13 files changed, 793 insertions(+), 76 deletions(-) create mode 100755 css/problemtype-editor.css rename style.css => css/style.css (92%) create mode 100644 js/nodes/stages/modeler.js create mode 100644 js/nodes/stages/post_process.js create mode 100644 js/nodes/stages/pre_process.js create mode 100644 js/nodes/stages/stage.js create mode 100755 js/problemtype-editor.js create mode 100644 resources/selection.json create mode 100644 suggestions.md diff --git a/css/problemtype-editor.css b/css/problemtype-editor.css new file mode 100755 index 0000000..6005065 --- /dev/null +++ b/css/problemtype-editor.css @@ -0,0 +1,319 @@ +@font-face { + font-family:"Font Awesome 5 Free"; + src:local("DroidSans"); + font-style:normal; + font-weight:900; + font-display:block; + unicode-range: U+0d-100d; +} + +body, html { + font-family: "Font Awesome 5 Free"; + font-style:normal; + font-weight:900; +} + +.litegraph.litecontextmenu { + font-family: "Font Awesome 5 Free"; + font-style:normal; + font-weight:900; +} + +.litegraph-editor { + width: 100%; + height: 100%; + margin: 0; + padding: 0; + + background-color: #333; + color: #eee; + /* font: 14px Tahoma; */ + + position: relative; +} + +.litegraph-editor h1 { + color: #ddd; + font-size: 28px; + padding-left: 10px; + margin: 0; + font-weight: normal; +} + +.litegraph-editor h1 span { + font-size: 14px; + font-weight: normal; + color: #aaa; +} + +.litegraph-editor h2 { + 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% - 44px); */ + 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; + /* width: 100%; */ + position: absolute; + bottom: 0px; + /*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-size: 14px; + color: #aaa; + cursor: pointer; +} + +/* BUTTONS **********************/ + +.litegraph-editor .float-btn-container { + width: 100%; + text-align: right; +} + +.litegraph-editor .float-btn-container-align { + width: 100px; + float: right; + margin-right: 12px; +} + +.litegraph-editor .float-btn { + appearance: none; + background-color: #2ea44f; + border: 1px solid rgba(27, 31, 35, .15); + border-radius: 6px; + box-shadow: rgba(27, 31, 35, .1) 0 1px 0; + box-sizing: border-box; + color: #fff; + cursor: pointer; + display: inline-block; + font-family: -apple-system,system-ui,"Segoe UI",Helvetica,Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji"; + font-size: 14px; + font-weight: 600; + line-height: 20px; + padding: 6px 16px; + position: relative; + text-align: center; + text-decoration: none; + user-select: none; + -webkit-user-select: none; + touch-action: manipulation; + vertical-align: middle; + white-space: nowrap; + margin: 6px 0px 6px 0px; + width: 100%; +} + +.litegraph-editor .btn-blue { + background-color: #2e6ba4; +} + +.litegraph-editor .float-btn:focus:not(:focus-visible):not(.focus-visible) { + box-shadow: none; + outline: none; +} + +.litegraph-editor .float-btn:hover { + background-color: #2c974b; +} + +.litegraph-editor .btn-blue:hover { + background-color: #225686; + } + +.litegraph-editor .float-btn:focus { + box-shadow: rgba(46, 164, 79, .4) 0 0 0 3px; + outline: none; +} + +.litegraph-editor .btn-blue:focus { + box-shadow: rgba(46, 87, 164, 0.4) 0 0 0 3px; + outline: none; + } + +.litegraph-editor .float-btn:disabled { + background-color: #94d3a2; + border-color: rgba(27, 31, 35, .1); + color: rgba(255, 255, 255, .8); + cursor: default; +} + +.litegraph-editor .btn-blue:disabled { + background-color: #94afd3; + border-color: rgba(27, 31, 35, .1); + color: rgba(255, 255, 255, .8); + cursor: default; + } + +.litegraph-editor .float-btn:active { + background-color: #298e46; + box-shadow: rgba(20, 70, 32, .2) 0 1px 0 inset; +} + +.litegraph-editor .btn-blue:active { + background-color: #29518e; + box-shadow: rgba(20, 70, 32, .2) 0 1px 0 inset; + } + +.litegraph-editor .btn { + 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 { + position: fixed; + width: 100%; + height: calc(100% - 40px); +} + +/* METER *********************/ + +.litegraph-editor .loadmeter { + color: #aaa; + font-size: 12px; + border-radius: 2px; + width: 130px; + vertical-align: top; +} + +.litegraph-editor .strong { + vertical-align: top; + padding: 3px; + width: 30px; + display: inline-block; + line-height: 8px; +} + +.litegraph-editor .cpuload .bgload, +.litegraph-editor .gpuload .bgload { + display: inline-block; + width: 90px; + height: 15px; + background-image: url("../editor/imgs/load-progress-empty.png"); +} + +.litegraph-editor .cpuload .fgload, +.litegraph-editor .gpuload .fgload { + display: inline-block; + width: 4px; + height: 15px; + max-width: 90px; + background-image: url("../editor/imgs/load-progress-full.png"); +} + +.litegraph-editor textarea.code, .litegraph-editor div.code { + height: 100%; + width: 100%; + background-color: black; + padding: 4px; + font: 16px monospace; + overflow: auto; + resize: none; + outline: none; +} + +.litegraph-editor .codeflask { + background-color: #2a2a2a; +} + +.litegraph-editor .codeflask textarea { + opacity: 0; +} \ No newline at end of file diff --git a/style.css b/css/style.css similarity index 92% rename from style.css rename to css/style.css index 00f105a..35c32ae 100644 --- a/style.css +++ b/css/style.css @@ -1,4 +1,4 @@ -html,body { +html, body { width: 100%; height: 100%; margin: 0; @@ -131,7 +131,7 @@ input,textarea { /*border-radius: 4px;*/ padding: 2px; /*box-shadow: inset 0 0 3px #333; */ - font-family: Verdana; + /* font-family: Verdana; */ width: 250px; } @@ -216,6 +216,7 @@ textarea { #worldcanvas { background-color: #343; + font-weight: 600; } .popup { diff --git a/index.html b/index.html index 22b92dc..40fbd79 100644 --- a/index.html +++ b/index.html @@ -1,83 +1,91 @@ - - - LiteGraph - - - - + + + Flowgraph + + + + - - - - + + + + + + - -
-
- - + +
.
+
+ + + - - - - - + + + + + - + - - - - - - - - - - - - - + + + + + + + + + + + + + - - - - - - - - + + + + + + + + - - + + + + + - - + + - - - - - - + + - - - - + + + + + + - - - - - - + + + + - - - + + + + + + - + + + + diff --git a/js/code.js b/js/code.js index 8ffb36a..365cb8a 100644 --- a/js/code.js +++ b/js/code.js @@ -4,6 +4,7 @@ LiteGraph.node_images_path = "../nodes_data/"; var editor = new LiteGraph.Editor("main"); window.graphcanvas = editor.graphcanvas; window.graph = editor.graph; +window.graphcanvas.title_text_font = '900 16px "Font Awesome 5 Free"'; window.addEventListener("resize", function() { editor.graphcanvas.resize(); } ); //window.addEventListener("keydown", editor.graphcanvas.processKey.bind(editor.graphcanvas) ); window.onbeforeunload = function(){ @@ -15,28 +16,37 @@ window.onbeforeunload = function(){ var elem = document.createElement("span"); elem.className = "selector" -elem.innerHTML = " Filename:"; +elem.innerHTML = "Kratos FlowGraph \uf35a"; $('.loadmeter').remove(); editor.tools.appendChild(elem); -const fileSelector = document.getElementById('file-selector'); -fileSelector.addEventListener('change', (event) => { +document.querySelector("#play-graph").addEventListener("click", function() { + if (graph.status == LGraph.STATUS_STOPPED) { + this.innerHTML = "Stop"; + graph.start(); + } else { + this.innerHTML = "Generate"; + graph.stop(); + } +}); + +document.querySelector("#load-graph").addEventListener('change', (event) => { const fileList = event.target.files; graph.load(fileList[0]) name = document.getElementById('download-name').value = event.target.files[0].name }); -elem.querySelector("#download").addEventListener("click",function(){ +document.querySelector("#save-graph").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); - name = document.getElementById('download-name').value + '.json'; + var name = document.getElementById('download-name').value + '.json'; element.setAttribute('download', name ); element.style.display = 'none'; @@ -44,4 +54,38 @@ elem.querySelector("#download").addEventListener("click",function(){ element.click(); document.body.removeChild(element); setTimeout( function(){ URL.revokeObjectURL( url ); }, 1000*60 ); //wait one minute to revoke url +}); + +document.querySelector('#expt-group').addEventListener("click", function() { + var serialization_data = [] + + for(var node in graphcanvas.selected_nodes) { + console.log(node) + serialization_data.push(graphcanvas.selected_nodes[node].serialize()); + }; + + var data = JSON.stringify(serialization_data); + var element = document.createElement("a"); + + element.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(data)); + element.setAttribute('download', "selection.json" ); + element.style.display = 'none'; + + document.body.appendChild(element); + element.click(); + document.body.removeChild(element); +}); + +document.querySelector('#impt-group').addEventListener('change', (event) => { + const fileData = event.target.files; + const reader = new FileReader(); + reader.onload = function(event) { + const data = { + nodes: JSON.parse( event.target.result ), + version: LiteGraph.VERSION + }; + + graph.configure(data, true); + }; + reader.readAsText(fileData[0]); }); \ No newline at end of file diff --git a/js/nodes/parsed_model_part.js b/js/nodes/parsed_model_part.js index ba28e1a..dde01d8 100644 --- a/js/nodes/parsed_model_part.js +++ b/js/nodes/parsed_model_part.js @@ -136,7 +136,7 @@ class ParsedModelPart { // } } -ParsedModelPart.title = "ParsedModelPart"; +ParsedModelPart.title = "\uf6d1 ParsedModelPart"; ParsedModelPart.desc = "Parses a ModelPart"; LiteGraph.registerNodeType("model_part/ParsedModelPart", ParsedModelPart); diff --git a/js/nodes/solver_settings/fluid_monolithic_solver.js b/js/nodes/solver_settings/fluid_monolithic_solver.js index 47892a2..2f43376 100644 --- a/js/nodes/solver_settings/fluid_monolithic_solver.js +++ b/js/nodes/solver_settings/fluid_monolithic_solver.js @@ -78,7 +78,7 @@ class FluidMonolithicSolver { } } -FluidMonolithicSolver.title = "Fluid monolithic solver"; +FluidMonolithicSolver.title = "\uf085 Fluid monolithic solver"; FluidMonolithicSolver.desc = "Properties for the monolthic fluid solver"; LiteGraph.registerNodeType("solver_settings/FluidMonolithicSolver", FluidMonolithicSolver); diff --git a/js/nodes/stages/modeler.js b/js/nodes/stages/modeler.js new file mode 100644 index 0000000..c54b591 --- /dev/null +++ b/js/nodes/stages/modeler.js @@ -0,0 +1,20 @@ +class Modeler { + constructor() { + this.addInput("Input", "stage_data_model"); + + this.addOutput("Modeler", "modeler") + } + + onExecute() { + } + + onSelection(e) { + } +} + +Modeler.title = "\uf6cf Modeler"; +Modeler.desc = "Select different ModelParts and access their submodelparts directly"; + +LiteGraph.registerNodeType("Stages/Modeler", Modeler); + +console.log("Modeler node created"); //helps to debug \ No newline at end of file diff --git a/js/nodes/stages/post_process.js b/js/nodes/stages/post_process.js new file mode 100644 index 0000000..cd271f8 --- /dev/null +++ b/js/nodes/stages/post_process.js @@ -0,0 +1,20 @@ +class StagePostProcess { + constructor() { + this.addInput("Process List", "process_array"); + + this.addOutput("Post Process", "stage_post_process"); + } + + onExecute() { + } + + onSelection(e) { + } +} + +StagePostProcess.title = "\uf35a Post Process"; +StagePostProcess.desc = "Select different ModelParts and access their submodelparts directly"; + +LiteGraph.registerNodeType("Stages/PostProcess", StagePostProcess); + +console.log("Modeler node created"); //helps to debug \ No newline at end of file diff --git a/js/nodes/stages/pre_process.js b/js/nodes/stages/pre_process.js new file mode 100644 index 0000000..8c69dda --- /dev/null +++ b/js/nodes/stages/pre_process.js @@ -0,0 +1,21 @@ +class StagePreProcess { + constructor() { + this.addInput("Modeler", "modeler"); + this.addInput("Process List", "process_array"); + + this.addOutput("Pre Process", "stage_pre_process"); + } + + onExecute() { + } + + onSelection(e) { + } +} + +StagePreProcess.title = "\uf359 Pre Process"; +StagePreProcess.desc = "Select different ModelParts and access their submodelparts directly"; + +LiteGraph.registerNodeType("Stages/PreProcess", StagePreProcess); + +console.log("Modeler node created"); //helps to debug \ No newline at end of file diff --git a/js/nodes/stages/stage.js b/js/nodes/stages/stage.js new file mode 100644 index 0000000..954a3cf --- /dev/null +++ b/js/nodes/stages/stage.js @@ -0,0 +1,22 @@ +class Stage { + constructor() { + this.addInput("Pre", "stage_pre_process"); // 0 + this.addInput("Solver", "map"); // 1 To be changed to solver_settings + this.addInput("Post", "stage_post_process"); // 2 + + this.addOutput("Stage Output", "stage_data_model") + } + + onExecute() { + } + + onSelection(e) { + } +} + +Stage.title = "\uf121 Stage"; +Stage.desc = "Select different ModelParts and access their submodelparts directly"; + +LiteGraph.registerNodeType("Stages/Stage", Stage); + +console.log("Stage node created"); //helps to debug \ No newline at end of file diff --git a/js/problemtype-editor.js b/js/problemtype-editor.js new file mode 100755 index 0000000..682b7ae --- /dev/null +++ b/js/problemtype-editor.js @@ -0,0 +1,245 @@ +//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 += "
"; + + var button_test = "
" + button_test += "
" + button_test += "
"; + button_test += "
"; + button_test += "
"; + button_test += "
"; + button_test += "
"; + button_test += "
" + button_test += "
" + + html += button_test + + 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 = 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(); + + 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.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); +}; + +LiteGraph.Editor = Editor; diff --git a/resources/selection.json b/resources/selection.json new file mode 100644 index 0000000..b6ffac7 --- /dev/null +++ b/resources/selection.json @@ -0,0 +1 @@ +[{"id":2,"type":"Stages/Stage","pos":[569,464],"size":{"0":161.1999969482422,"1":66},"flags":{},"order":1,"mode":0,"inputs":[{"name":"Pre","type":"stage_pre_process","link":1},{"name":"Solver","type":"map","link":null},{"name":"Post","type":"stage_post_process","link":null}],"outputs":[{"name":"Stage Output","type":"stage_data_model","links":null}],"properties":{}},{"id":3,"type":"Stages/PreProcess","pos":[308,431],"size":{"0":203.1999969482422,"1":46},"flags":{},"order":0,"mode":0,"inputs":[{"name":"Modeler","type":"modeler","link":null},{"name":"Process List","type":"array","link":null}],"outputs":[{"name":"Pre Process","type":"stage_pre_process","links":[1]}],"properties":{}}] \ No newline at end of file diff --git a/suggestions.md b/suggestions.md new file mode 100644 index 0000000..1b3f0b9 --- /dev/null +++ b/suggestions.md @@ -0,0 +1,16 @@ +- Load & Save: + Parece que se refiera a el ProjectParameters o al Mdpa. + +- Tener algunos elementos cargados directamente en la pantalla + - Modelpart + - Stages + - Solvers + - Output + - Viewer + - Selector de procesos + - Project Parameters + +- Materials: + - Crearlos o leerlos de un json de Kratos. + +- Cabiar Parsed Modelpart -> Input Modelpart \ No newline at end of file From 99937ab6d99189f4f016f32930cb81e557aebae2 Mon Sep 17 00:00:00 2001 From: Carlos Date: Sun, 7 Nov 2021 18:43:02 +0100 Subject: [PATCH 02/18] Removing unused imports, adding glyph support in titles/slots --- css/problemtype-editor.css | 26 +- index.html | 15 +- js/injections/extended_menu.js | 122 +++ js/injections/glyphs.js | 777 ++++++++++++++++++ .../fluid_monolithic_solver.js | 7 +- 5 files changed, 932 insertions(+), 15 deletions(-) create mode 100644 js/injections/extended_menu.js create mode 100644 js/injections/glyphs.js diff --git a/css/problemtype-editor.css b/css/problemtype-editor.css index 6005065..7028c12 100755 --- a/css/problemtype-editor.css +++ b/css/problemtype-editor.css @@ -1,18 +1,30 @@ +/* latin */ @font-face { - font-family:"Font Awesome 5 Free"; - src:local("DroidSans"); - font-style:normal; - font-weight:900; - font-display:block; - unicode-range: U+0d-100d; + font-family: 'Font Awesome 5 Free'; + font-style: normal; + font-weight: 400; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v29/KFOmCnqEu92Fr1Mu4mxK.woff2) format('woff2'); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} + +/* latin */ +@font-face { + font-family: 'Font Awesome 5 Free'; + font-style: normal; + font-weight: 900; + font-display: swap; + src: url(https://fonts.gstatic.com/s/roboto/v29/KFOmCnqEu92Fr1Mu4mxK.woff2) format('woff2'); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; } body, html { font-family: "Font Awesome 5 Free"; font-style:normal; - font-weight:900; + font-weight:400; } +/* This one is for the context menu */ .litegraph.litecontextmenu { font-family: "Font Awesome 5 Free"; font-style:normal; diff --git a/index.html b/index.html index 40fbd79..e17986f 100644 --- a/index.html +++ b/index.html @@ -2,11 +2,15 @@ Flowgraph + + + + @@ -33,16 +37,11 @@ - - - - - @@ -86,6 +85,10 @@ - + + + + + diff --git a/js/injections/extended_menu.js b/js/injections/extended_menu.js new file mode 100644 index 0000000..8950bf6 --- /dev/null +++ b/js/injections/extended_menu.js @@ -0,0 +1,122 @@ +// Override the Litegrapgh menu generation + +LGraphCanvas.onMenuAdd = function(node, options, e, prev_menu, callback) { + var canvas = LGraphCanvas.active_canvas; + var ref_window = canvas.getCanvasWindow(); + var graph = canvas.graph; + + if(!graph) + return; + + var values = LiteGraph.getNodeTypesCategories( canvas.filter || graph.filter ); + var entries_levels = {}; + + for (var i=0; i < values.length; i++) { + if (values[i]) { + var name = values[i]; + var name_route; + var acc_route = ""; + + // Namespace prune. I don't fully understand this use case but was in the base. + if(name.indexOf("::") != -1) + name = name.split("::")[1]; + + // Submenu parse + if(name.indexOf("/") != -1) + name_route = values[i].split("/"); + else + name_route = name + + if(Array.isArray(name_route)) { + acc_route += name_route[0]; + + if(entries_levels[name_route[0]] == undefined) + entries_levels[name_route[0]] = {value: acc_route, content: name_route[0], has_submenu: true, sub: {} } + + entry_point = entries_levels[name_route[0]].sub; + + for(var j = 1; j < name_route.length; j++) { + acc_route += "/"+name_route[j]; + if(entry_point[name_route[j]] == undefined) + entry_point[name_route[j]] = {value: acc_route, content: name_route[j], has_submenu: true, sub: {} } + entry_point = entry_point[name_route[j]].sub; + } + } else { + if(entries_levels[name_route] == undefined) + entries_levels[name_route] = {value: values[i], content: name, has_submenu: true, sub: {} }; + } + } + } + + var entries = []; + + Object.entries(entries_levels).forEach(([key, value]) => { + entries.push(value); + }); + + // Show Initial submenu. Probably can be merged. Menu_level is used to correctly assign the parentMenu at each level. + var menu = new LiteGraph.ContextMenu( entries, { event: e, callback: handle_menu_click, parentMenu: prev_menu }, ref_window ); + var menu_level = {'': menu}; + + // Select callback depending on the entry attributes. "Submode" key seems to handle submenus natively, but I am unable to make it work. + function handle_menu_click(v, option, e) { + if(v.sub == undefined || v.sub.size == 0) { + handle_leave(v, option, e); + } else { + handle_submenu(v, option, e); + } + } + + function handle_submenu(v, option, e) { + var category = v.value; + var node_types = LiteGraph.getNodeTypesInCategory( category, canvas.filter || graph.filter ); + var values = []; + var category_path = category.split("/"); + + // Build the entries belonging to submenus + Object.entries(v.sub).forEach(([key, value]) => { + values.push(value); + }); + + // Build the entries belonging end nodes + for (var i=0; i < node_types.length; i++) { + if (!node_types[i].skip_list) { + values.push({ + content: node_types[i].title, + value: node_types[i].type, + has_submenu: false + }); + } + } + + var prev = ""; + if (category_path.length > 1 ) { + category_path.splice(-1,1) + prev = category_path.join("/"); + } + + new_menu = new LiteGraph.ContextMenu( values, { event: e, callback: handle_menu_click, parentMenu: menu_level[prev] }, ref_window ); + menu_level[category] = new_menu; + + return false; + } + + function handle_leave(v, e) { + var first_event = prev_menu.getFirstEvent(); + canvas.graph.beforeChange(); + var node = LiteGraph.createNode(v.value); + + if (node) { + node.pos = canvas.convertEventToCanvasOffset(first_event); + canvas.graph.add(node); + } + + if(callback) { + callback(node); + } + + canvas.graph.afterChange(); + } + + return false; +}; \ No newline at end of file diff --git a/js/injections/glyphs.js b/js/injections/glyphs.js new file mode 100644 index 0000000..8f781ed --- /dev/null +++ b/js/injections/glyphs.js @@ -0,0 +1,777 @@ +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; + } + + var low_quality = this.ds.scale < 0.6; //zoomed out + + //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 && !low_quality) { + 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.onDrawCollapsed && + 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 = !low_quality; + + var out_slot = this.connecting_output; + var in_slot = this.connecting_input; + 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]; + + var slot_type = slot.type; + var slot_shape = slot.shape; + + ctx.globalAlpha = editor_alpha; + //change opacity of incompatible slots when dragging a connection + if ( this.connecting_output && !LiteGraph.isValidConnection( slot.type , out_slot.type) ) { + ctx.globalAlpha = 0.4 * editor_alpha; + } + + ctx.fillStyle = + slot.link != null + ? slot.color_on || + this.default_connection_color_byType[slot_type] || + this.default_connection_color.input_on + : slot.color_off || + this.default_connection_color_byTypeOff[slot_type] || + this.default_connection_color_byType[slot_type] || + this.default_connection_color.input_off; + + var pos = node.getConnectionPos(true, i, slot_pos); + 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 == "array"){ + slot_shape = LiteGraph.GRID_SHAPE; // place in addInput? addOutput instead? + } + + var doStroke = true; + + 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 if (slot_shape === LiteGraph.GRID_SHAPE) { + ctx.rect(pos[0] - 4, pos[1] - 4, 2, 2); + ctx.rect(pos[0] - 1, pos[1] - 4, 2, 2); + ctx.rect(pos[0] + 2, pos[1] - 4, 2, 2); + ctx.rect(pos[0] - 4, pos[1] - 1, 2, 2); + ctx.rect(pos[0] - 1, pos[1] - 1, 2, 2); + ctx.rect(pos[0] + 2, pos[1] - 1, 2, 2); + ctx.rect(pos[0] - 4, pos[1] + 2, 2, 2); + ctx.rect(pos[0] - 1, pos[1] + 2, 2, 2); + ctx.rect(pos[0] + 2, pos[1] + 2, 2, 2); + doStroke = false; + } else { + if(low_quality) // Faster + ctx.rect(pos[0] - 4, pos[1] - 4, 8, 8 ); + else // Custom shape based on font. + if (slot.glyph) { + ctx.moveTo(pos[0] + 8, pos[1] + 0.5); + ctx.font = '900 '+10+'px "Font Awesome 5 Free"'; + ctx.fillText( + slot.glyph, + pos[0] - 4.5, + pos[1] + 5 + ); + } 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 + + ctx.textAlign = horizontal ? "center" : "right"; + ctx.strokeStyle = "black"; + if (node.outputs) { + for (var i = 0; i < node.outputs.length; i++) { + var slot = node.outputs[i]; + + var slot_type = slot.type; + var slot_shape = slot.shape; + + //change opacity of incompatible slots when dragging a connection + if (this.connecting_input && !LiteGraph.isValidConnection( slot_type , in_slot.type) ) { + ctx.globalAlpha = 0.4 * editor_alpha; + } + + var pos = node.getConnectionPos(false, i, slot_pos); + pos[0] -= node.pos[0]; + pos[1] -= node.pos[1]; + 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_byType[slot_type] || + this.default_connection_color.output_on + : slot.color_off || + this.default_connection_color_byTypeOff[slot_type] || + this.default_connection_color_byType[slot_type] || + this.default_connection_color.output_off; + ctx.beginPath(); + //ctx.rect( node.size[0] - 14,i*14,10,10); + + if (slot_type == "array"){ + slot_shape = LiteGraph.GRID_SHAPE; + } + + var doStroke = true; + + if ( + slot_type === LiteGraph.EVENT || + slot_shape === LiteGraph.BOX_SHAPE + ) { + 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 if (slot_shape === LiteGraph.GRID_SHAPE) { + ctx.rect(pos[0] - 4, pos[1] - 4, 2, 2); + ctx.rect(pos[0] - 1, pos[1] - 4, 2, 2); + ctx.rect(pos[0] + 2, pos[1] - 4, 2, 2); + ctx.rect(pos[0] - 4, pos[1] - 1, 2, 2); + ctx.rect(pos[0] - 1, pos[1] - 1, 2, 2); + ctx.rect(pos[0] + 2, pos[1] - 1, 2, 2); + ctx.rect(pos[0] - 4, pos[1] + 2, 2, 2); + ctx.rect(pos[0] - 1, pos[1] + 2, 2, 2); + ctx.rect(pos[0] + 2, pos[1] + 2, 2, 2); + doStroke = false; + } else { + if(low_quality) + ctx.rect(pos[0] - 4, pos[1] - 4, 8, 8 ); + 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(); + if(!low_quality && doStroke) + 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) { + var widgets_y = max_y; + if (horizontal || node.widgets_up) { + widgets_y = 2; + } + if( node.widgets_start_y != null ) + widgets_y = node.widgets_start_y; + this.drawNodeWidgets( + node, + widgets_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 || title_mode == LiteGraph.NO_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], + shape == LiteGraph.CARD_SHAPE ? [this.round_radius,this.round_radius,0,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(); + + //separator + if(!node.flags.collapsed && render_title) + { + ctx.shadowColor = "transparent"; + ctx.fillStyle = "rgba(0,0,0,0.2)"; + ctx.fillRect(0, -1, area[2], 2); + } + } + ctx.shadowColor = "transparent"; + + if (node.onDrawBackground) { + node.onDrawBackground(ctx, this, this.canvas, this.graph_mouse ); + } + + //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); // TODO refactor: validate color !! prevent DOMException + 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, + node.flags.collapsed ? [this.round_radius] : [this.round_radius,this.round_radius,0,0] + ); + } + ctx.fill(); + ctx.shadowColor = "transparent"; + } + + var colState = false; + if (LiteGraph.node_box_coloured_by_mode){ + if(LiteGraph.NODE_MODES_COLORS[node.mode]){ + colState = LiteGraph.NODE_MODES_COLORS[node.mode]; + } + } + if (LiteGraph.node_box_coloured_when_on){ + colState = node.action_triggered ? "#FFF" : (node.execute_triggered ? "#AAA" : colState); + } + + //title box + var box_size = 10; + if (node.onDrawTitleBox) { + 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 || colState || LiteGraph.NODE_DEFAULT_BOXCOLOR; + if(low_quality) { + ctx.fillRect( title_height * 0.5 - box_size *0.5, title_height * -0.5 - box_size *0.5, box_size , box_size ); + } else if (node.glyph) { + ctx.font = '900 '+14+'px "Font Awesome 5 Free"'; + ctx.fillText( + node.glyph.shape, + title_height * 0.5 - node.glyph.width / 2, + title_height * -0.5 + node.glyph.height / 2 + ); + } else { + ctx.beginPath(); + ctx.arc( + title_height * 0.5, + title_height * -0.5, + box_size * 0.5, + 0, + Math.PI * 2 + ); + ctx.fill(); + } + } else { + if (low_quality) { + ctx.fillStyle = "black"; + 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 || colState || 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 = String(node.getTitle()); + if (title) { + if (selected) { + ctx.fillStyle = LiteGraph.NODE_SELECTED_TITLE_COLOR; + } else { + ctx.fillStyle = + node.constructor.title_text_color || + this.node_title_color; + } + if (node.flags.collapsed) { + ctx.textAlign = "left"; + var measure = ctx.measureText(title); + ctx.fillText( + title.substr(0,20), //avoid urls too long + 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 + ); + } + } + } + + //subgraph box + if (!node.flags.collapsed && node.subgraph && !node.skip_subgraph_button) { + var w = LiteGraph.NODE_TITLE_HEIGHT; + var x = node.size[0] - w; + var over = LiteGraph.isInsideRectangle( this.graph_mouse[0] - node.pos[0], this.graph_mouse[1] - node.pos[1], x+2, -w+2, w-4, w-4 ); + ctx.fillStyle = over ? "#888" : "#555"; + if( shape == LiteGraph.BOX_SHAPE || low_quality) + ctx.fillRect(x+2, -w+2, w-4, w-4); + else + { + ctx.beginPath(); + ctx.roundRect(x+2, -w+2, w-4, w-4,[4]); + ctx.fill(); + } + ctx.fillStyle = "#333"; + ctx.beginPath(); + ctx.moveTo(x + w * 0.2, -w * 0.6); + ctx.lineTo(x + w * 0.8, -w * 0.6); + ctx.lineTo(x + w * 0.5, -w * 0.3); + ctx.fill(); + } + + //custom title render + 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,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 = LiteGraph.NODE_BOX_OUTLINE_COLOR; + ctx.stroke(); + ctx.strokeStyle = fgcolor; + ctx.globalAlpha = 1; + } + + // these counter helps in conditioning drawing based on if the node has been executed or an action occurred + if (node.execute_triggered>0) node.execute_triggered--; + if (node.action_triggered>0) node.action_triggered--; +}; \ No newline at end of file diff --git a/js/nodes/solver_settings/fluid_monolithic_solver.js b/js/nodes/solver_settings/fluid_monolithic_solver.js index 2f43376..5a6fba0 100644 --- a/js/nodes/solver_settings/fluid_monolithic_solver.js +++ b/js/nodes/solver_settings/fluid_monolithic_solver.js @@ -1,8 +1,11 @@ class FluidMonolithicSolver { constructor() { + // Add a identifier glyph + this.glyph = {shape: "\uf085", width: 16, height: 9}; + // List of inputs and outputs ("name", "type") - this.addInput("model_import_settings", "map"); // 0 + this.addInput("model_import_settings", "map", {"glyph": "\uf6cf"}); // 0 this.addInput("model_part_name", "string"); // 1 this.addInput("volume_model_part_name", "string"); // 2 this.addInput("skin_parts", "array"); // 3 @@ -78,7 +81,7 @@ class FluidMonolithicSolver { } } -FluidMonolithicSolver.title = "\uf085 Fluid monolithic solver"; +FluidMonolithicSolver.title = "Fluid monolithic solver"; FluidMonolithicSolver.desc = "Properties for the monolthic fluid solver"; LiteGraph.registerNodeType("solver_settings/FluidMonolithicSolver", FluidMonolithicSolver); From d353de649e12f3bef0527b4ae1d909714009faba Mon Sep 17 00:00:00 2001 From: Carlos Date: Sun, 7 Nov 2021 18:54:38 +0100 Subject: [PATCH 03/18] Optional glyph offsets and duplicated file --- js/extended_menu.js | 122 ------------------ js/injections/glyphs.js | 10 +- .../fluid_monolithic_solver.js | 2 +- 3 files changed, 8 insertions(+), 126 deletions(-) delete mode 100644 js/extended_menu.js diff --git a/js/extended_menu.js b/js/extended_menu.js deleted file mode 100644 index 8950bf6..0000000 --- a/js/extended_menu.js +++ /dev/null @@ -1,122 +0,0 @@ -// Override the Litegrapgh menu generation - -LGraphCanvas.onMenuAdd = function(node, options, e, prev_menu, callback) { - var canvas = LGraphCanvas.active_canvas; - var ref_window = canvas.getCanvasWindow(); - var graph = canvas.graph; - - if(!graph) - return; - - var values = LiteGraph.getNodeTypesCategories( canvas.filter || graph.filter ); - var entries_levels = {}; - - for (var i=0; i < values.length; i++) { - if (values[i]) { - var name = values[i]; - var name_route; - var acc_route = ""; - - // Namespace prune. I don't fully understand this use case but was in the base. - if(name.indexOf("::") != -1) - name = name.split("::")[1]; - - // Submenu parse - if(name.indexOf("/") != -1) - name_route = values[i].split("/"); - else - name_route = name - - if(Array.isArray(name_route)) { - acc_route += name_route[0]; - - if(entries_levels[name_route[0]] == undefined) - entries_levels[name_route[0]] = {value: acc_route, content: name_route[0], has_submenu: true, sub: {} } - - entry_point = entries_levels[name_route[0]].sub; - - for(var j = 1; j < name_route.length; j++) { - acc_route += "/"+name_route[j]; - if(entry_point[name_route[j]] == undefined) - entry_point[name_route[j]] = {value: acc_route, content: name_route[j], has_submenu: true, sub: {} } - entry_point = entry_point[name_route[j]].sub; - } - } else { - if(entries_levels[name_route] == undefined) - entries_levels[name_route] = {value: values[i], content: name, has_submenu: true, sub: {} }; - } - } - } - - var entries = []; - - Object.entries(entries_levels).forEach(([key, value]) => { - entries.push(value); - }); - - // Show Initial submenu. Probably can be merged. Menu_level is used to correctly assign the parentMenu at each level. - var menu = new LiteGraph.ContextMenu( entries, { event: e, callback: handle_menu_click, parentMenu: prev_menu }, ref_window ); - var menu_level = {'': menu}; - - // Select callback depending on the entry attributes. "Submode" key seems to handle submenus natively, but I am unable to make it work. - function handle_menu_click(v, option, e) { - if(v.sub == undefined || v.sub.size == 0) { - handle_leave(v, option, e); - } else { - handle_submenu(v, option, e); - } - } - - function handle_submenu(v, option, e) { - var category = v.value; - var node_types = LiteGraph.getNodeTypesInCategory( category, canvas.filter || graph.filter ); - var values = []; - var category_path = category.split("/"); - - // Build the entries belonging to submenus - Object.entries(v.sub).forEach(([key, value]) => { - values.push(value); - }); - - // Build the entries belonging end nodes - for (var i=0; i < node_types.length; i++) { - if (!node_types[i].skip_list) { - values.push({ - content: node_types[i].title, - value: node_types[i].type, - has_submenu: false - }); - } - } - - var prev = ""; - if (category_path.length > 1 ) { - category_path.splice(-1,1) - prev = category_path.join("/"); - } - - new_menu = new LiteGraph.ContextMenu( values, { event: e, callback: handle_menu_click, parentMenu: menu_level[prev] }, ref_window ); - menu_level[category] = new_menu; - - return false; - } - - function handle_leave(v, e) { - var first_event = prev_menu.getFirstEvent(); - canvas.graph.beforeChange(); - var node = LiteGraph.createNode(v.value); - - if (node) { - node.pos = canvas.convertEventToCanvasOffset(first_event); - canvas.graph.add(node); - } - - if(callback) { - callback(node); - } - - canvas.graph.afterChange(); - } - - return false; -}; \ No newline at end of file diff --git a/js/injections/glyphs.js b/js/injections/glyphs.js index 8f781ed..552a886 100644 --- a/js/injections/glyphs.js +++ b/js/injections/glyphs.js @@ -1,3 +1,5 @@ +LiteGraph.NODE_DEFAULT_TITLE_FONT = "" + LiteGraph.NODE_TEXT_SIZE + "px Arial"; + var temp_vec2 = new Float32Array(2); /** @@ -610,11 +612,13 @@ LGraphCanvas.prototype.drawNodeShape = function( if(low_quality) { ctx.fillRect( title_height * 0.5 - box_size *0.5, title_height * -0.5 - box_size *0.5, box_size , box_size ); } else if (node.glyph) { - ctx.font = '900 '+14+'px "Font Awesome 5 Free"'; + ctx.font = node.glyph.font || LiteGraph.NODE_DEFAULT_TITLE_FONT; + const offset_width = node.glyph.width || 0; + const offset_height = node.glyph.height || 0; ctx.fillText( node.glyph.shape, - title_height * 0.5 - node.glyph.width / 2, - title_height * -0.5 + node.glyph.height / 2 + title_height * 0.5 - offset_width / 2, + title_height * -0.5 + offset_height / 2 ); } else { ctx.beginPath(); diff --git a/js/nodes/solver_settings/fluid_monolithic_solver.js b/js/nodes/solver_settings/fluid_monolithic_solver.js index 5a6fba0..0e14064 100644 --- a/js/nodes/solver_settings/fluid_monolithic_solver.js +++ b/js/nodes/solver_settings/fluid_monolithic_solver.js @@ -2,7 +2,7 @@ class FluidMonolithicSolver { constructor() { // Add a identifier glyph - this.glyph = {shape: "\uf085", width: 16, height: 9}; + this.glyph = {shape: '\uf085', font:'900 14px "Font Awesome 5 Free"', width: 16, height: 9}; // List of inputs and outputs ("name", "type") this.addInput("model_import_settings", "map", {"glyph": "\uf6cf"}); // 0 From 8a75eb01539b243eb3cfd49deb3ba02bed60ce52 Mon Sep 17 00:00:00 2001 From: Carlos Date: Sun, 7 Nov 2021 19:05:24 +0100 Subject: [PATCH 04/18] offset support for slot glyphs --- js/injections/glyphs.js | 11 ++++++----- js/nodes/solver_settings/fluid_monolithic_solver.js | 2 +- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/js/injections/glyphs.js b/js/injections/glyphs.js index 552a886..5a91921 100644 --- a/js/injections/glyphs.js +++ b/js/injections/glyphs.js @@ -206,12 +206,13 @@ LGraphCanvas.prototype.drawNode = function(node, ctx) { ctx.rect(pos[0] - 4, pos[1] - 4, 8, 8 ); else // Custom shape based on font. if (slot.glyph) { - ctx.moveTo(pos[0] + 8, pos[1] + 0.5); - ctx.font = '900 '+10+'px "Font Awesome 5 Free"'; + ctx.font = slot.glyph.font || LiteGraph.NODE_DEFAULT_TITLE_FONT; + const offset_width = slot.glyph.width || 0; + const offset_height = slot.glyph.height || 0; ctx.fillText( - slot.glyph, - pos[0] - 4.5, - pos[1] + 5 + slot.glyph.shape, + pos[0] - offset_width / 2, + pos[1] + offset_height / 2 ); } else { ctx.arc(pos[0], pos[1], 4, 0, Math.PI * 2); diff --git a/js/nodes/solver_settings/fluid_monolithic_solver.js b/js/nodes/solver_settings/fluid_monolithic_solver.js index 0e14064..5d6c5ad 100644 --- a/js/nodes/solver_settings/fluid_monolithic_solver.js +++ b/js/nodes/solver_settings/fluid_monolithic_solver.js @@ -5,7 +5,7 @@ class FluidMonolithicSolver { this.glyph = {shape: '\uf085', font:'900 14px "Font Awesome 5 Free"', width: 16, height: 9}; // List of inputs and outputs ("name", "type") - this.addInput("model_import_settings", "map", {"glyph": "\uf6cf"}); // 0 + this.addInput("model_import_settings", "map", {"glyph": {shape:"\uf6cf", font:'900 10px "Font Awesome 5 Free"', width:9, height:10}}); // 0 this.addInput("model_part_name", "string"); // 1 this.addInput("volume_model_part_name", "string"); // 2 this.addInput("skin_parts", "array"); // 3 From 7823d9184be5b3f8a6f823876c7f229275ddceb3 Mon Sep 17 00:00:00 2001 From: Carlos Date: Sun, 7 Nov 2021 19:13:53 +0100 Subject: [PATCH 05/18] Updating first implementation --- css/problemtype-editor.css | 2 +- js/nodes/parsed_model_part.js | 6 +++++- js/nodes/solver_settings/fluid_monolithic_solver.js | 5 ++--- js/nodes/stages/modeler.js | 6 +++++- js/nodes/stages/post_process.js | 6 +++++- js/nodes/stages/pre_process.js | 6 +++++- js/nodes/stages/stage.js | 6 +++++- 7 files changed, 28 insertions(+), 9 deletions(-) diff --git a/css/problemtype-editor.css b/css/problemtype-editor.css index 7028c12..a78b299 100755 --- a/css/problemtype-editor.css +++ b/css/problemtype-editor.css @@ -96,7 +96,7 @@ body, html { .litegraph-editor .tools-left { right: auto; - left: 4px; + padding: 6px; } .litegraph-editor .footer { diff --git a/js/nodes/parsed_model_part.js b/js/nodes/parsed_model_part.js index dde01d8..7de52b4 100644 --- a/js/nodes/parsed_model_part.js +++ b/js/nodes/parsed_model_part.js @@ -1,5 +1,9 @@ class ParsedModelPart { constructor() { + // Identifier Glyph + this.glyph = {shape: '\uf6d1', font:'900 14px "Font Awesome 5 Free"', width: 16, height: 9}; + + // List of inputs and outputs ("name", "type") this.input_manager = document.createElement('input'); this.input_manager.type = 'file'; this.input_manager.addEventListener('change', this.onSelection.bind(this)); @@ -136,7 +140,7 @@ class ParsedModelPart { // } } -ParsedModelPart.title = "\uf6d1 ParsedModelPart"; +ParsedModelPart.title = "ParsedModelPart"; ParsedModelPart.desc = "Parses a ModelPart"; LiteGraph.registerNodeType("model_part/ParsedModelPart", ParsedModelPart); diff --git a/js/nodes/solver_settings/fluid_monolithic_solver.js b/js/nodes/solver_settings/fluid_monolithic_solver.js index 5d6c5ad..511bc40 100644 --- a/js/nodes/solver_settings/fluid_monolithic_solver.js +++ b/js/nodes/solver_settings/fluid_monolithic_solver.js @@ -1,11 +1,10 @@ class FluidMonolithicSolver { constructor() { - - // Add a identifier glyph + // Identifier Glyph this.glyph = {shape: '\uf085', font:'900 14px "Font Awesome 5 Free"', width: 16, height: 9}; // List of inputs and outputs ("name", "type") - this.addInput("model_import_settings", "map", {"glyph": {shape:"\uf6cf", font:'900 10px "Font Awesome 5 Free"', width:9, height:10}}); // 0 + this.addInput("model_import_settings", "map", {"glyph": {shape:"\uf6cf", font:'900 10px "Font Awesome 5 Free"', width:9, height:10}}); // 0 this.addInput("model_part_name", "string"); // 1 this.addInput("volume_model_part_name", "string"); // 2 this.addInput("skin_parts", "array"); // 3 diff --git a/js/nodes/stages/modeler.js b/js/nodes/stages/modeler.js index c54b591..22da43a 100644 --- a/js/nodes/stages/modeler.js +++ b/js/nodes/stages/modeler.js @@ -1,5 +1,9 @@ class Modeler { constructor() { + // Identifier Glyph + this.glyph = {shape: '\uf6cf', font:'900 14px "Font Awesome 5 Free"', width: 16, height: 9}; + + // List of inputs and outputs ("name", "type") this.addInput("Input", "stage_data_model"); this.addOutput("Modeler", "modeler") @@ -12,7 +16,7 @@ class Modeler { } } -Modeler.title = "\uf6cf Modeler"; +Modeler.title = "Modeler"; Modeler.desc = "Select different ModelParts and access their submodelparts directly"; LiteGraph.registerNodeType("Stages/Modeler", Modeler); diff --git a/js/nodes/stages/post_process.js b/js/nodes/stages/post_process.js index cd271f8..33e98f9 100644 --- a/js/nodes/stages/post_process.js +++ b/js/nodes/stages/post_process.js @@ -1,5 +1,9 @@ class StagePostProcess { constructor() { + // Identifier Glyph + this.glyph = {shape: '\uf35a', font:'900 14px "Font Awesome 5 Free"', width: 16, height: 9}; + + // List of inputs and outputs ("name", "type") this.addInput("Process List", "process_array"); this.addOutput("Post Process", "stage_post_process"); @@ -12,7 +16,7 @@ class StagePostProcess { } } -StagePostProcess.title = "\uf35a Post Process"; +StagePostProcess.title = "Post Process"; StagePostProcess.desc = "Select different ModelParts and access their submodelparts directly"; LiteGraph.registerNodeType("Stages/PostProcess", StagePostProcess); diff --git a/js/nodes/stages/pre_process.js b/js/nodes/stages/pre_process.js index 8c69dda..654ec30 100644 --- a/js/nodes/stages/pre_process.js +++ b/js/nodes/stages/pre_process.js @@ -1,5 +1,9 @@ class StagePreProcess { constructor() { + // Identifier Glyph + this.glyph = {shape: '\uf359', font:'900 14px "Font Awesome 5 Free"', width: 16, height: 9}; + + // List of inputs and outputs ("name", "type") this.addInput("Modeler", "modeler"); this.addInput("Process List", "process_array"); @@ -13,7 +17,7 @@ class StagePreProcess { } } -StagePreProcess.title = "\uf359 Pre Process"; +StagePreProcess.title = "Pre Process"; StagePreProcess.desc = "Select different ModelParts and access their submodelparts directly"; LiteGraph.registerNodeType("Stages/PreProcess", StagePreProcess); diff --git a/js/nodes/stages/stage.js b/js/nodes/stages/stage.js index 954a3cf..17d3405 100644 --- a/js/nodes/stages/stage.js +++ b/js/nodes/stages/stage.js @@ -1,5 +1,9 @@ class Stage { constructor() { + // Identifier Glyph + this.glyph = {shape: '\uf121', font:'900 14px "Font Awesome 5 Free"', width: 16, height: 9}; + + // List of inputs and outputs ("name", "type") this.addInput("Pre", "stage_pre_process"); // 0 this.addInput("Solver", "map"); // 1 To be changed to solver_settings this.addInput("Post", "stage_post_process"); // 2 @@ -14,7 +18,7 @@ class Stage { } } -Stage.title = "\uf121 Stage"; +Stage.title = "Stage"; Stage.desc = "Select different ModelParts and access their submodelparts directly"; LiteGraph.registerNodeType("Stages/Stage", Stage); From 6b67b8aa7ee50b5474010066b143c78f689015fa Mon Sep 17 00:00:00 2001 From: Carlos Date: Sun, 7 Nov 2021 19:20:14 +0100 Subject: [PATCH 06/18] Removing Css hacks --- css/problemtype-editor.css | 33 +++++++++------------------------ js/code.js | 4 ++-- 2 files changed, 11 insertions(+), 26 deletions(-) diff --git a/css/problemtype-editor.css b/css/problemtype-editor.css index a78b299..eddf8cf 100755 --- a/css/problemtype-editor.css +++ b/css/problemtype-editor.css @@ -1,34 +1,12 @@ -/* latin */ -@font-face { - font-family: 'Font Awesome 5 Free'; - font-style: normal; - font-weight: 400; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v29/KFOmCnqEu92Fr1Mu4mxK.woff2) format('woff2'); - unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; -} - -/* latin */ -@font-face { - font-family: 'Font Awesome 5 Free'; - font-style: normal; - font-weight: 900; - font-display: swap; - src: url(https://fonts.gstatic.com/s/roboto/v29/KFOmCnqEu92Fr1Mu4mxK.woff2) format('woff2'); - unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; -} - body, html { - font-family: "Font Awesome 5 Free"; + font-family: "Roboto"; font-style:normal; - font-weight:400; } /* This one is for the context menu */ .litegraph.litecontextmenu { - font-family: "Font Awesome 5 Free"; + font-family: "Roboto"; font-style:normal; - font-weight:900; } .litegraph-editor { @@ -44,6 +22,12 @@ body, html { position: relative; } +.litegraph-editor .glyph { + font-family: "Font Awesome 5 Free"; + font-style:normal; + font-weight: 400; +} + .litegraph-editor h1 { color: #ddd; font-size: 28px; @@ -97,6 +81,7 @@ body, html { .litegraph-editor .tools-left { right: auto; padding: 6px; + left: 2px; } .litegraph-editor .footer { diff --git a/js/code.js b/js/code.js index 365cb8a..4d48b16 100644 --- a/js/code.js +++ b/js/code.js @@ -4,7 +4,7 @@ LiteGraph.node_images_path = "../nodes_data/"; var editor = new LiteGraph.Editor("main"); window.graphcanvas = editor.graphcanvas; window.graph = editor.graph; -window.graphcanvas.title_text_font = '900 16px "Font Awesome 5 Free"'; +window.graphcanvas.title_text_font = '400 16px "Roboto"'; window.addEventListener("resize", function() { editor.graphcanvas.resize(); } ); //window.addEventListener("keydown", editor.graphcanvas.processKey.bind(editor.graphcanvas) ); window.onbeforeunload = function(){ @@ -16,7 +16,7 @@ window.onbeforeunload = function(){ var elem = document.createElement("span"); elem.className = "selector" -elem.innerHTML = "Kratos FlowGraph \uf35a"; +elem.innerHTML = "Kratos FlowGraph \uf35a"; $('.loadmeter').remove(); From 27761cb2b1a9abde388cd421476b260a0878d0c8 Mon Sep 17 00:00:00 2001 From: roigcarlo Date: Thu, 3 Feb 2022 16:25:08 +0000 Subject: [PATCH 07/18] Adding support for Id manipulation in selection imports --- js/code.js | 77 +++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 71 insertions(+), 6 deletions(-) diff --git a/js/code.js b/js/code.js index 4d48b16..987dea2 100644 --- a/js/code.js +++ b/js/code.js @@ -46,7 +46,7 @@ document.querySelector("#save-graph").addEventListener("click",function(){ var element = document.createElement("a"); element.setAttribute('href', url); - var name = document.getElementById('download-name').value + '.json'; + var name = 'graph.json'; element.setAttribute('download', name ); element.style.display = 'none'; @@ -57,11 +57,18 @@ document.querySelector("#save-graph").addEventListener("click",function(){ }); document.querySelector('#expt-group').addEventListener("click", function() { - var serialization_data = [] + var serialization_data = {"nodes":[], "links":[]}; + var nodes_id = []; for(var node in graphcanvas.selected_nodes) { - console.log(node) - serialization_data.push(graphcanvas.selected_nodes[node].serialize()); + serialization_data["nodes"].push(graphcanvas.selected_nodes[node].serialize()); + nodes_id.push(graphcanvas.selected_nodes[node].id); + }; + + for(var link in graph.links) { + if (nodes_id.includes(graph.links[link].origin_id) && nodes_id.includes(graph.links[link].target_id)) { + serialization_data["links"].push(graph.links[link].serialize()); + } }; var data = JSON.stringify(serialization_data); @@ -77,15 +84,73 @@ document.querySelector('#expt-group').addEventListener("click", function() { }); document.querySelector('#impt-group').addEventListener('change', (event) => { + const LINK_OBJECT_ID = 0; + const LINK_ORIGIN_ID = 1; + const LINK_TARGET_ID = 3; + const fileData = event.target.files; const reader = new FileReader(); reader.onload = function(event) { + import_group = JSON.parse(event.target.result); + + // Modify node and links id's as we cannot assume the same entities or other entities + // with the same id are used in the grapgh + var last_node_id = graph.last_node_id; + var last_link_id = graph.last_link_id; + var new_node_ids_map = {}; + var new_link_ids_map = {}; + + // Nodes + for(var node in import_group["nodes"]) { + const old_id = import_group["nodes"][node]["id"] + const new_id = ++last_node_id; + new_node_ids_map[old_id] = new_id; + import_group["nodes"][node]["id"] = new_id; + } + + for(var link in import_group["links"]) { + if (import_group["links"][link][LINK_ORIGIN_ID] in new_node_ids_map) { + import_group["links"][link][LINK_ORIGIN_ID] = new_node_ids_map[import_group["links"][link][LINK_ORIGIN_ID]]; + } + if (import_group["links"][link][LINK_TARGET_ID] in new_node_ids_map) { + import_group["links"][link][LINK_TARGET_ID] = new_node_ids_map[import_group["links"][link][LINK_TARGET_ID]]; + } + } + + graph.last_node_id = last_node_id; + + // Links + + for(var link in import_group["links"]) { + const old_id = import_group["links"][link][LINK_OBJECT_ID] + const new_id = ++last_link_id; + new_link_ids_map[old_id] = new_id; + import_group["links"][link][LINK_OBJECT_ID] = new_id; + } + + for(var node in import_group["nodes"]) { + for(var input in import_group["nodes"][node]["inputs"]) { + if( import_group["nodes"][node]["inputs"][input]["link"] in new_link_ids_map) { + import_group["nodes"][node]["inputs"][input]["link"] = new_link_ids_map[import_group["nodes"][node]["inputs"][input]["link"]]; + } + } + + for(var output in import_group["nodes"][node]["outputs"]) { + if( import_group["nodes"][node]["outputs"][output]["link"] in new_link_ids_map) { + import_group["nodes"][node]["outputs"][output]["link"] = new_link_ids_map[import_group["nodes"][node]["output"][output]["link"]]; + } + } + } + + graph.last_link_id = last_link_id; + const data = { - nodes: JSON.parse( event.target.result ), + nodes: import_group["nodes"], + links: import_group["links"], version: LiteGraph.VERSION }; - graph.configure(data, true); + graph.configure_selection(data, true); }; reader.readAsText(fileData[0]); }); \ No newline at end of file From 6d9cadcf4feb4bbae6ef2ae8994b29b8066dff74 Mon Sep 17 00:00:00 2001 From: roigcarlo Date: Thu, 3 Feb 2022 16:26:03 +0000 Subject: [PATCH 08/18] Adding custom configure function into LGraph Default function has some behaviour I don't understand: - this._nodes gets deleted regardless of the 'keep' variable - links are not added, but replaced directly into the class Until I figure out why is this done this way import node selection need to be done with my function --- index.html | 1 + js/injections/selection_import.js | 99 +++++++++++++++++++++++++++++++ 2 files changed, 100 insertions(+) create mode 100644 js/injections/selection_import.js diff --git a/index.html b/index.html index e17986f..05ce3b3 100644 --- a/index.html +++ b/index.html @@ -88,6 +88,7 @@ + diff --git a/js/injections/selection_import.js b/js/injections/selection_import.js new file mode 100644 index 0000000..c5b9cfb --- /dev/null +++ b/js/injections/selection_import.js @@ -0,0 +1,99 @@ + /** + * 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_selection = 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]; + if(!link_data) //weird bug + { + console.warn("serialized graph link data contains errors, skipping."); + continue; + } + var link = new LiteGraph.LLink(); + link.configure(link_data); + this.links[link.id] = link; + } + // data.links = links; + } + + //copy all stored fields + for (var i in data) { + if(i == "nodes" || i == "groups" || i == "links") //links must be accepted + continue; + 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.extra = data.extra || {}; + + if(this.onConfigure) + this.onConfigure(data); + + this._version++; + this.setDirtyCanvas(true, true); + return error; + }; \ No newline at end of file From 38b3052568714fdb92dedaf897e1939685c2b9ad Mon Sep 17 00:00:00 2001 From: roigcarlo Date: Thu, 3 Feb 2022 16:28:09 +0000 Subject: [PATCH 09/18] Adding Selection Example --- resources/selections/selection.json | 1 + 1 file changed, 1 insertion(+) create mode 100644 resources/selections/selection.json diff --git a/resources/selections/selection.json b/resources/selections/selection.json new file mode 100644 index 0000000..3b4a69e --- /dev/null +++ b/resources/selections/selection.json @@ -0,0 +1 @@ +{"nodes":[{"id":3,"type":"Stages/Stage","pos":[484,152],"size":{"0":161.1999969482422,"1":66},"flags":{},"order":3,"mode":0,"inputs":[{"name":"Pre","type":"stage_pre_process","link":1},{"name":"Solver","type":"map","link":null},{"name":"Post","type":"stage_post_process","link":null}],"outputs":[{"name":"Stage Output","type":"stage_data_model","links":null}],"properties":{}},{"id":5,"type":"Stages/PreProcess","pos":[228,204],"size":{"0":203.1999969482422,"1":46},"flags":{},"order":1,"mode":0,"inputs":[{"name":"Modeler","type":"modeler","link":null},{"name":"Process List","type":"process_array","link":null}],"outputs":[{"name":"Pre Process","type":"stage_pre_process","links":[1,1,2],"slot_index":0}],"properties":{}},{"id":6,"type":"Stages/Stage","pos":[483,1],"size":{"0":161.1999969482422,"1":66},"flags":{"collapsed":false},"order":2,"mode":0,"inputs":[{"name":"Pre","type":"stage_pre_process","link":3},{"name":"Solver","type":"map","link":null},{"name":"Post","type":"stage_post_process","link":null}],"outputs":[{"name":"Stage Output","type":"stage_data_model","links":null}],"properties":{}},{"id":7,"type":"Stages/Stage","pos":[482,269],"size":{"0":161.1999969482422,"1":66},"flags":{},"order":4,"mode":0,"inputs":[{"name":"Pre","type":"stage_pre_process","link":2},{"name":"Solver","type":"map","link":null},{"name":"Post","type":"stage_post_process","link":null}],"outputs":[{"name":"Stage Output","type":"stage_data_model","links":null}],"properties":{}},{"id":8,"type":"Stages/PreProcess","pos":[226,34],"size":{"0":203.1999969482422,"1":46},"flags":{},"order":0,"mode":0,"inputs":[{"name":"Modeler","type":"modeler","link":null},{"name":"Process List","type":"process_array","link":null}],"outputs":[{"name":"Pre Process","type":"stage_pre_process","links":[3],"slot_index":0}],"properties":{}}],"links":[[1,5,0,3,0,"stage_pre_process"],[2,5,0,7,0,"stage_pre_process"],[3,8,0,6,0,"stage_pre_process"]]} \ No newline at end of file From c19e3b838b95a6dc870cb3fe8318ae9e9fdf3b7d Mon Sep 17 00:00:00 2001 From: Carlos Roig Date: Thu, 3 Feb 2022 15:35:40 +0000 Subject: [PATCH 10/18] Delete selection.json --- resources/selection.json | 1 - 1 file changed, 1 deletion(-) delete mode 100644 resources/selection.json diff --git a/resources/selection.json b/resources/selection.json deleted file mode 100644 index b6ffac7..0000000 --- a/resources/selection.json +++ /dev/null @@ -1 +0,0 @@ -[{"id":2,"type":"Stages/Stage","pos":[569,464],"size":{"0":161.1999969482422,"1":66},"flags":{},"order":1,"mode":0,"inputs":[{"name":"Pre","type":"stage_pre_process","link":1},{"name":"Solver","type":"map","link":null},{"name":"Post","type":"stage_post_process","link":null}],"outputs":[{"name":"Stage Output","type":"stage_data_model","links":null}],"properties":{}},{"id":3,"type":"Stages/PreProcess","pos":[308,431],"size":{"0":203.1999969482422,"1":46},"flags":{},"order":0,"mode":0,"inputs":[{"name":"Modeler","type":"modeler","link":null},{"name":"Process List","type":"array","link":null}],"outputs":[{"name":"Pre Process","type":"stage_pre_process","links":[1]}],"properties":{}}] \ No newline at end of file From d820a3474d2d5731d3a55966e1fbbae063d325ad Mon Sep 17 00:00:00 2001 From: Carlos Roig Date: Wed, 9 Feb 2022 10:21:23 +0100 Subject: [PATCH 11/18] Fixed bug not changing node input origin link id --- js/code.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/js/code.js b/js/code.js index 987dea2..2b991e3 100644 --- a/js/code.js +++ b/js/code.js @@ -136,8 +136,10 @@ document.querySelector('#impt-group').addEventListener('change', (event) => { } for(var output in import_group["nodes"][node]["outputs"]) { - if( import_group["nodes"][node]["outputs"][output]["link"] in new_link_ids_map) { - import_group["nodes"][node]["outputs"][output]["link"] = new_link_ids_map[import_group["nodes"][node]["output"][output]["link"]]; + for(var link in import_group["nodes"][node]["outputs"][output]["links"]) { + if( import_group["nodes"][node]["outputs"][output]["links"][link] in new_link_ids_map) { + import_group["nodes"][node]["outputs"][output]["links"][link] = new_link_ids_map[import_group["nodes"][node]["outputs"][output]["links"][link]]; + } } } } From 2749f24231b3e197ef2e148292e4af36316a29fd Mon Sep 17 00:00:00 2001 From: Carlos Roig Date: Wed, 9 Feb 2022 10:21:39 +0100 Subject: [PATCH 12/18] Code cleanup --- js/injections/selection_import.js | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/js/injections/selection_import.js b/js/injections/selection_import.js index c5b9cfb..4f9c169 100644 --- a/js/injections/selection_import.js +++ b/js/injections/selection_import.js @@ -15,7 +15,7 @@ var nodes = data.nodes; - //decode links info (they are very verbose) + // Decode links info (they are very verbose) if (data.links && data.links.constructor === Array) { var links = []; for (var i = 0; i < data.links.length; ++i) { @@ -27,12 +27,12 @@ } var link = new LiteGraph.LLink(); link.configure(link_data); + console.log(link) this.links[link.id] = link; } - // data.links = links; } - //copy all stored fields + // Copy all stored fields for (var i in data) { if(i == "nodes" || i == "groups" || i == "links") //links must be accepted continue; @@ -41,8 +41,7 @@ var error = false; - //create nodes - // this._nodes = []; + // Create nodes if (nodes) { for (var i = 0, l = nodes.length; i < l; ++i) { var n_info = nodes[i]; //stored info From fddb42ac21b723e6bf1ddff423a6374e0fa681ca Mon Sep 17 00:00:00 2001 From: Carlos Roig Date: Wed, 9 Feb 2022 11:38:19 +0100 Subject: [PATCH 13/18] Added multistage support --- index.html | 1 + .../fluid_monolithic_solver.js | 2 +- js/nodes/stages/fluid_stage.js | 13 +++++ js/nodes/stages/post_process.js | 2 +- js/nodes/stages/pre_process.js | 2 +- js/nodes/stages/stage.js | 58 ++++++++++++++++--- 6 files changed, 66 insertions(+), 12 deletions(-) create mode 100644 js/nodes/stages/fluid_stage.js diff --git a/index.html b/index.html index 05ce3b3..aa15b13 100644 --- a/index.html +++ b/index.html @@ -57,6 +57,7 @@ + diff --git a/js/nodes/solver_settings/fluid_monolithic_solver.js b/js/nodes/solver_settings/fluid_monolithic_solver.js index 511bc40..203403b 100644 --- a/js/nodes/solver_settings/fluid_monolithic_solver.js +++ b/js/nodes/solver_settings/fluid_monolithic_solver.js @@ -12,7 +12,7 @@ class FluidMonolithicSolver { this.addInput("linear_solver_settings", "map"); // 5 this.addInput("material_import_settings", "map"); // 6 - this.addOutput("solver_settings", "map"); + this.addOutput("solver_settings", "solver_settings"); // List of properties this.properties = { diff --git a/js/nodes/stages/fluid_stage.js b/js/nodes/stages/fluid_stage.js new file mode 100644 index 0000000..732a260 --- /dev/null +++ b/js/nodes/stages/fluid_stage.js @@ -0,0 +1,13 @@ +class FluidAnalysisStage extends AnalysisStage { + constructor() { + super(); + this._type = "KratosMultiphysics.FluidDynamicsApplication.fluid_dynamics_analysis" + } +} + +FluidAnalysisStage.title = "Fluid Analysis Stage"; +FluidAnalysisStage.desc = "Select different ModelParts and access their submodelparts directly"; + +LiteGraph.registerNodeType("Stages/FluidAnalysisStage", FluidAnalysisStage); + +console.log("FluidAnalysisStage node created"); //helps to debug \ No newline at end of file diff --git a/js/nodes/stages/post_process.js b/js/nodes/stages/post_process.js index 33e98f9..9b28c72 100644 --- a/js/nodes/stages/post_process.js +++ b/js/nodes/stages/post_process.js @@ -6,7 +6,7 @@ class StagePostProcess { // List of inputs and outputs ("name", "type") this.addInput("Process List", "process_array"); - this.addOutput("Post Process", "stage_post_process"); + this.addOutput("Post Process", "stage_post"); } onExecute() { diff --git a/js/nodes/stages/pre_process.js b/js/nodes/stages/pre_process.js index 654ec30..48fd4c9 100644 --- a/js/nodes/stages/pre_process.js +++ b/js/nodes/stages/pre_process.js @@ -7,7 +7,7 @@ class StagePreProcess { this.addInput("Modeler", "modeler"); this.addInput("Process List", "process_array"); - this.addOutput("Pre Process", "stage_pre_process"); + this.addOutput("Pre Process", "stage_pre"); } onExecute() { diff --git a/js/nodes/stages/stage.js b/js/nodes/stages/stage.js index 17d3405..0eab437 100644 --- a/js/nodes/stages/stage.js +++ b/js/nodes/stages/stage.js @@ -1,26 +1,66 @@ -class Stage { +class AnalysisStage { constructor() { // Identifier Glyph this.glyph = {shape: '\uf121', font:'900 14px "Font Awesome 5 Free"', width: 16, height: 9}; + // Type + this._type = "analysis_stage" + + // Stage Name + this._name = this.addWidget("text","Stage Name", "", function(v){}, {} ); + // List of inputs and outputs ("name", "type") - this.addInput("Pre", "stage_pre_process"); // 0 - this.addInput("Solver", "map"); // 1 To be changed to solver_settings - this.addInput("Post", "stage_post_process"); // 2 + this.addInput("Previous Stage", "analysis_stage") // 0 + this.addInput("Pre", "stage_pre"); // 1 + this.addInput("Problem Data", "problem_data"); // 2 + this.addInput("Solver Settings", "solver_settings"); // 3 + + // TODO: Add processes and output_processes + + this.addInput("Post", "stage_post"); // 4 - this.addOutput("Stage Output", "stage_data_model") + this.addOutput("Stage", "analysis_stage") // 0 } onExecute() { + if(this.getInputData(0) == undefined) { + this._value = {}; + this._value["execution_order"] = []; + this._value["stages"] = {}; + } else { + this._value = this.getInputData(0); + } + + let stage_name = "auto_" + this.id + "_stage"; + if(this._name.value != undefined && this._name.value != "") { + stage_name = this._name.value + "_stage"; + } + + // The if is needed to prevent cycles and duplicated stages + if(!this._value["execution_order"].includes(stage_name)) { + this._value["execution_order"].push(stage_name); + + this._value["stages"][stage_name] = { + "stage_preprocess": this.getInputData(1), + "analysis_stage": this._type, + "problem_data": this.getInputData(2), + "solver_settings": this.getInputData(3), + "stage_postprocess": this.getInputData(4) + } + } else { + // TODO: How to report to the user that the stage is already added? + } + + this.setOutputData(0, this._value); } onSelection(e) { } } -Stage.title = "Stage"; -Stage.desc = "Select different ModelParts and access their submodelparts directly"; +AnalysisStage.title = "AnalysisStage"; +AnalysisStage.desc = "Select different ModelParts and access their submodelparts directly"; -LiteGraph.registerNodeType("Stages/Stage", Stage); +LiteGraph.registerNodeType("Stages/AnalysisStage", AnalysisStage); -console.log("Stage node created"); //helps to debug \ No newline at end of file +console.log("AnalysisStage node created"); //helps to debug \ No newline at end of file From 1ffdd8186bab53dd3020cf410e9b0b7df373b5b6 Mon Sep 17 00:00:00 2001 From: Carlos Roig Date: Thu, 10 Feb 2022 09:39:15 +0100 Subject: [PATCH 14/18] added initial error handling support --- index.html | 4 ++ js/injections/glyphs.js | 12 +++- js/nodes/error_handler.js | 47 +++++++++++++ .../fluid_monolithic_solver.js | 1 + js/nodes/stages/fluid_stage.js | 2 +- js/nodes/stages/post_process.js | 3 +- js/nodes/stages/pre_process.js | 4 +- js/nodes/stages/stage.js | 68 +++++++++++++++++-- 8 files changed, 129 insertions(+), 12 deletions(-) create mode 100644 js/nodes/error_handler.js diff --git a/index.html b/index.html index aa15b13..48518f8 100644 --- a/index.html +++ b/index.html @@ -43,6 +43,10 @@ + + + + diff --git a/js/injections/glyphs.js b/js/injections/glyphs.js index 5a91921..a012dc2 100644 --- a/js/injections/glyphs.js +++ b/js/injections/glyphs.js @@ -214,6 +214,7 @@ LGraphCanvas.prototype.drawNode = function(node, ctx) { pos[0] - offset_width / 2, pos[1] + offset_height / 2 ); + ctx.font = LiteGraph.NODE_DEFAULT_TITLE_FONT; } else { ctx.arc(pos[0], pos[1], 4, 0, Math.PI * 2); } @@ -652,7 +653,7 @@ LGraphCanvas.prototype.drawNodeShape = function( } ctx.globalAlpha = old_alpha; - //title text + // Title text if (node.onDrawTitleText) { node.onDrawTitleText( ctx, @@ -683,6 +684,15 @@ LGraphCanvas.prototype.drawNodeShape = function( LiteGraph.NODE_TITLE_TEXT_Y - title_height ); ctx.textAlign = "left"; + } else if (node.crop_title) { + ctx.textAlign = "left"; + var measure = ctx.measureText(title); + ctx.fillText( + title.substr(0,node.crop_title) + "...", //avoid urls too long + title_height,// + measure.width * 0.5, + LiteGraph.NODE_TITLE_TEXT_Y - title_height + ); + ctx.textAlign = "left"; } else { ctx.textAlign = "left"; ctx.fillText( diff --git a/js/nodes/error_handler.js b/js/nodes/error_handler.js new file mode 100644 index 0000000..b7cd220 --- /dev/null +++ b/js/nodes/error_handler.js @@ -0,0 +1,47 @@ +class ErrorHandler { + constructor() {} + + static drawErrorMark(ctx, node) { + ctx.font = '900 14px "Font Awesome 5 Free"'; + ctx.textAlign = "end"; + ctx.fillStyle = "#C44"; + ctx.fillText( + "\uf06a", + node.size[0]-10, + LiteGraph.NODE_TITLE_TEXT_Y - LiteGraph.NODE_TITLE_HEIGHT + ); + ctx.font = LiteGraph.NODE_DEFAULT_TITLE_FONT; + } + + static drawErrorTooltip(ctx, node) { + let box_x = node.size[0]-17; + let box_y = LiteGraph.NODE_TITLE_TEXT_Y - LiteGraph.NODE_TITLE_HEIGHT - 12; + let lines = ["Errors:", ...node.error_list]; + + let info = lines.map((line)=>ctx.measureText(line).width).reduce((a,b)=>Math.max(a,b)); + + let h = 24 * lines.length + 15 * 0.3; + let w = info + 20; + + ctx.font = "14px Courier New"; + ctx.shadowColor = "black"; + ctx.shadowOffsetX = 2; + ctx.shadowOffsetY = 2; + ctx.shadowBlur = 3; + ctx.fillStyle = "#544"; + ctx.beginPath(); + ctx.roundRect( box_x - w*0.5, box_y - 15 - h, w, h, [3]); + ctx.moveTo( box_x - 10, box_y - 15 ); + ctx.lineTo( box_x + 10, box_y - 15 ); + ctx.lineTo( box_x, box_y - 5 ); + ctx.fill(); + ctx.shadowColor = "transparent"; + ctx.textAlign = "center"; + ctx.fillStyle = "#ECC"; + for(let l in lines) { + ctx.fillText(lines[l], box_x, box_y - h + 24 * l + 15 * 0.3); + } + } +} + +// export default InputList; \ No newline at end of file diff --git a/js/nodes/solver_settings/fluid_monolithic_solver.js b/js/nodes/solver_settings/fluid_monolithic_solver.js index 203403b..a72c7f2 100644 --- a/js/nodes/solver_settings/fluid_monolithic_solver.js +++ b/js/nodes/solver_settings/fluid_monolithic_solver.js @@ -47,6 +47,7 @@ class FluidMonolithicSolver { this.domain_size = this.addWidget("combo","Domain Size", "2", function(v){}, { values:["2","3"]} ); this.size = this.computeSize(); + this.crop_title = 5; } onExecute() { diff --git a/js/nodes/stages/fluid_stage.js b/js/nodes/stages/fluid_stage.js index 732a260..264b336 100644 --- a/js/nodes/stages/fluid_stage.js +++ b/js/nodes/stages/fluid_stage.js @@ -10,4 +10,4 @@ FluidAnalysisStage.desc = "Select different ModelParts and access their submodel LiteGraph.registerNodeType("Stages/FluidAnalysisStage", FluidAnalysisStage); -console.log("FluidAnalysisStage node created"); //helps to debug \ No newline at end of file +console.log("FluidAnalysisStage node created"); \ No newline at end of file diff --git a/js/nodes/stages/post_process.js b/js/nodes/stages/post_process.js index 9b28c72..e4cc2c5 100644 --- a/js/nodes/stages/post_process.js +++ b/js/nodes/stages/post_process.js @@ -4,7 +4,8 @@ class StagePostProcess { this.glyph = {shape: '\uf35a', font:'900 14px "Font Awesome 5 Free"', width: 16, height: 9}; // List of inputs and outputs ("name", "type") - this.addInput("Process List", "process_array"); + this.addInput("Modelers", "modelers_array"); + this.addInput("Operations", "operations_array"); this.addOutput("Post Process", "stage_post"); } diff --git a/js/nodes/stages/pre_process.js b/js/nodes/stages/pre_process.js index 48fd4c9..914acaf 100644 --- a/js/nodes/stages/pre_process.js +++ b/js/nodes/stages/pre_process.js @@ -4,8 +4,8 @@ class StagePreProcess { this.glyph = {shape: '\uf359', font:'900 14px "Font Awesome 5 Free"', width: 16, height: 9}; // List of inputs and outputs ("name", "type") - this.addInput("Modeler", "modeler"); - this.addInput("Process List", "process_array"); + this.addInput("Modelers", "modelers_array"); + this.addInput("Operations", "operations_array"); this.addOutput("Pre Process", "stage_pre"); } diff --git a/js/nodes/stages/stage.js b/js/nodes/stages/stage.js index 0eab437..275581f 100644 --- a/js/nodes/stages/stage.js +++ b/js/nodes/stages/stage.js @@ -14,15 +14,67 @@ class AnalysisStage { this.addInput("Pre", "stage_pre"); // 1 this.addInput("Problem Data", "problem_data"); // 2 this.addInput("Solver Settings", "solver_settings"); // 3 - - // TODO: Add processes and output_processes - - this.addInput("Post", "stage_post"); // 4 + this.addInput("Processes", "process_list"); // 4 + this.addInput("Output Processes", "output_process_list"); // 5 + this.addInput("Post", "stage_post"); // 6 this.addOutput("Stage", "analysis_stage") // 0 + + this.crop_title = 20; + this.hover_tooltip = false; + // this.error_list = [] + this.error_list = ["Error1", "Error2", "Error3", "Error4"]; + } + + onDrawTitle(ctx) { + if(this.error_list.length) { + ErrorHandler.drawErrorMark(ctx, this); + } + } + + onMouseMove(e, pos, graph_canvas) { + if (pos[0] > this.size[0]-24 && + pos[0] < this.size[0]-10 && + pos[1] > LiteGraph.NODE_TITLE_TEXT_Y - LiteGraph.NODE_TITLE_HEIGHT - 13 && + pos[1] < LiteGraph.NODE_TITLE_TEXT_Y - LiteGraph.NODE_TITLE_HEIGHT + 2) { + + this.hover_tooltip = true; + } else { + this.hover_tooltip = false; + } + } + + onDrawBackground(ctx, node, canvas, graph_mouse) { + var title_height = LiteGraph.NODE_TITLE_HEIGHT; + var area = [0,0,0,0]; + area[0] = 0; //x + area[1] = -title_height; //y + area[2] = this.size[0] + 1; //w + area[3] = this.size[1] + title_height; //h + + console.log("ousize:", this.size); + ctx.strokeStyle = "#C44"; + ctx.fillStyle = "#C44"; + ctx.beginPath(); + ctx.roundRect( + area[0] + 10, + area[1] + 10, + area[2] + 10, + area[3] + 10, + [this.round_radius] + ); + ctx.fill(); + } + + onDrawForeground(ctx, area) { + if(this.hover_tooltip && this.error_list.length) { + ErrorHandler.drawErrorTooltip(ctx, this); + } } onExecute() { + this.error_list = []; + if(this.getInputData(0) == undefined) { this._value = {}; this._value["execution_order"] = []; @@ -45,10 +97,12 @@ class AnalysisStage { "analysis_stage": this._type, "problem_data": this.getInputData(2), "solver_settings": this.getInputData(3), - "stage_postprocess": this.getInputData(4) + "processes": this.getInputData(4), + "output_processes": this.getInputData(5), + "stage_postprocess": this.getInputData(6) } } else { - // TODO: How to report to the user that the stage is already added? + this.error_list.push("Stage '"+stage_name+"' already exists") } this.setOutputData(0, this._value); @@ -63,4 +117,4 @@ AnalysisStage.desc = "Select different ModelParts and access their submodelparts LiteGraph.registerNodeType("Stages/AnalysisStage", AnalysisStage); -console.log("AnalysisStage node created"); //helps to debug \ No newline at end of file +console.log("AnalysisStage node created"); \ No newline at end of file From 4730f6f70fc8f49f8598d7783b119990f0c64eca Mon Sep 17 00:00:00 2001 From: roigcarlo Date: Thu, 10 Feb 2022 18:17:05 +0000 Subject: [PATCH 15/18] Initial model transfer --- index.html | 16 ++-- js/injections/remove_widget.js | 4 + js/nodes/model_inspector.js | 123 ++++++++++++++++++++++++ js/nodes/stages/import_modeler.js | 151 ++++++++++++++++++++++++++++++ js/nodes/stages/model_view.js | 0 js/nodes/stages/multi_stage.js | 17 ++++ js/nodes/stages/post_process.js | 3 +- js/nodes/stages/pre_process.js | 62 +++++++++++- js/nodes/stages/stage.js | 16 ++-- 9 files changed, 375 insertions(+), 17 deletions(-) create mode 100644 js/injections/remove_widget.js create mode 100644 js/nodes/model_inspector.js create mode 100644 js/nodes/stages/import_modeler.js create mode 100644 js/nodes/stages/model_view.js create mode 100644 js/nodes/stages/multi_stage.js diff --git a/index.html b/index.html index 48518f8..ac814a1 100644 --- a/index.html +++ b/index.html @@ -28,6 +28,14 @@ + + + + + + + + @@ -49,6 +57,7 @@ + @@ -60,6 +69,7 @@ + @@ -90,11 +100,5 @@ - - - - - - diff --git a/js/injections/remove_widget.js b/js/injections/remove_widget.js new file mode 100644 index 0000000..2d29f0f --- /dev/null +++ b/js/injections/remove_widget.js @@ -0,0 +1,4 @@ +LGraphNode.prototype.removeWidget = function(slot) { + this.widgets.splice(slot, 1); + this.setDirtyCanvas(true, true); +}; \ No newline at end of file diff --git a/js/nodes/model_inspector.js b/js/nodes/model_inspector.js new file mode 100644 index 0000000..a924d84 --- /dev/null +++ b/js/nodes/model_inspector.js @@ -0,0 +1,123 @@ +class ModelInspector { + constructor() { + this.MODEL_INPUT = 0; + this.MODEL_OUTPUT = 0; + + this.addInput("Model", 0); + + this.addOutput("value", 0); + + this.widgets_up = true; + + this._output_slector_map = []; + this._output_slector_map.push(this.addWidget("combo","ModelPart", "", (v) => v, { + values:[] + })); + + this._model = []; + + this.output_type = ""; + } + + onConnectionsChange(type, slot, connected, link_info, input_info) { + if (type == LiteGraph.INPUT) { + this.onUpdateModel(); + this.updateModelOuputs(); + } else { + // Remove unconnected nodes + for (let i = 0; i < this.outputs.length; i++) { + if (!this.isOutputConnected(i)) { + this.removeOutput(i); + this.removeWidget(i); + + // Delete the internal reference that we have in the node. + this._output_slector_map.splice(i, 1); + } + } + + // If all nodes are connected, or there are no nodes, add one. + if (this.outputs.length <= 0 || this.isOutputConnected(this.outputs.length - 1)) { + this.addOutput("value", 0); + this._output_slector_map.push(this.addWidget("combo","ModelPart", "DEBUG", null, { + values:this._model + })); + } + + this.setSize(1, this.outputs.length); + this.size = this.computeSize(); + } + } + + /** + * Updates the value of the node's model. + */ + onUpdateModel() { + this.updateModelList(); + + // If there are nodes upstream, trigger the execution of onUpdateModel. + for (let link_id in this.outputs[this.MODEL_OUTPUT].links) { + var link = this.outputs[this.MODEL_OUTPUT].links[link_id]; + if (link != null) { + let upstream_node = this.graph.getNodeById(this.graph.links[link].target_id); + if (upstream_node.onUpdateModel) { + upstream_node.onUpdateModel(); + } + } + } + } + + /** + * Update the node's model with the values downstream. + */ + updateModelList() { + if (this.inputs[this.MODEL_INPUT].link) { + this._model = [...this.graph.getNodeById(this.graph.links[this.inputs[this.MODEL_INPUT].link].origin_id).getModelList()]; + } else { + this._model = []; + } + + for (let op_id in this._model_operations) { + switch(this._model_operations[op_id].code) { + case "add": + this._model.push(this._model_operations[op_id].data); + break; + case "rem": + // To be implemented + break; + default: + // Do nothing + break; + } + } + + this._model = [...new Set(this._model)]; + } + + /** + * Get the list of model modelparts in the node + */ + getModelList() { + return this._model; + } + + /** + * Populates the node widgets with the name of the modelparts available + * in the input's model, if exists. + */ + updateModelOuputs() { + for (let widget in this._output_slector_map) { + this._output_slector_map[widget].options.values = []; + if(this._model) { + this._output_slector_map[widget].options.values = this._model; + this._output_slector_map[widget].value = this._model[0]; + } + } + } +} + +ModelInspector.title = "Model Inspector"; +ModelInspector.desc = "Select different ModelParts and access their submodelparts directly"; + +LiteGraph.registerNodeType("model_part/Model Inspector", ModelInspector); + +console.log("ModelInspector node created"); //helps to debug \ No newline at end of file diff --git a/js/nodes/stages/import_modeler.js b/js/nodes/stages/import_modeler.js new file mode 100644 index 0000000..05f50af --- /dev/null +++ b/js/nodes/stages/import_modeler.js @@ -0,0 +1,151 @@ +class ImportMdpaModeler { + constructor() { + // Model + this.MODEL_INPUT = 0; + this.MODEL_OUTPUT = 0; + + this._model = []; + this._model_operations = []; + + // Identifier Glyph + this.glyph = {shape: '\uf6cf', font:'900 14px "Font Awesome 5 Free"', width: 16, height: 9}; + + // List of inputs and outputs ("name", "type") + this.addInput("Stage", "stage_flow"); + + // List Manager + this.input_manager = document.createElement('input'); + this.input_manager.type = 'file'; + this.input_manager.addEventListener('change', this.onSelection.bind(this)); + + this.mp_name = this.addWidget("text","Name", "", function(v){}, {} ); + this.mp_select = this.addWidget("button", "Load Mdpa", "", function (value, widget, node) { + node.input_manager.click(); + }); + + this.addOutput("Modeler", "stage_flow"); + + this.size = this.computeSize(); + this.serialize_widgets = true; + } + + onExecute() { + this._value = {"model": this._model}; + + this.setOutputData(0, this._value); + } + + onSelection(e) { + const [file] = event.target.files; + this.readModelList(file); + } + + /** + * Get the list of model modelparts in the node + */ + getModelList() { + return this._model; + } + + /** + * Update the node's model with the values downstream. + */ + updateModelList() { + if (this.inputs[this.MODEL_INPUT].link) { + this._model = [...this.graph.getNodeById(this.graph.links[this.inputs[this.MODEL_INPUT].link].origin_id).getModelList()]; + } else { + this._model = []; + } + + for (let op_id in this._model_operations) { + switch(this._model_operations[op_id].code) { + case "add": + this._model.push(this._model_operations[op_id].data); + break; + case "rem": + // To be implemented + break; + default: + // Do nothing + break; + } + } + + this._model = [...new Set(this._model)]; + } + + /** + * Read the node's model with the values from a file. + */ + readModelList(source) { + if (!source) { + return; + } + + const reader = new FileReader(); + + reader.onload = this.onReaderLoad(source); + reader.readAsText(source); + }; + + onReaderLoad(file) { + return ({ target: { result } }) => { + const mdpa_subs_re = /.*((Begin SubModelPart) ([a-zA-Z0-9_]+))|(End SubModelPart$)/gm; + const sub_mdpa = result.matchAll(mdpa_subs_re); + + // Remove existing model operations. + this._model_operations = []; + + // Obtain the name of the ModelPart to get complete routes. + let sub_mdpa_namepath = "" + this.mp_name.value = file.name.slice(0, -5); + + // Obtain the Submodelparts + for (const match of sub_mdpa) { + if (match[0].includes("Begin")) { + sub_mdpa_namepath = `${sub_mdpa_namepath}.${match[3]}`; + this._model_operations.push({code:"add", data:sub_mdpa_namepath}); + } + + if (match[0].includes("End")) { + sub_mdpa_namepath = sub_mdpa_namepath.split("."); + sub_mdpa_namepath.pop(); + sub_mdpa_namepath = sub_mdpa_namepath.join("."); + } + } + + // If there are nodes upstream, trigger the execution of onUpdateModel + this.onUpdateModel(); + } + } + + onConnectionsChange(type, slot, connected, link_info, input_info) { + if (type == LiteGraph.INPUT) { + this.onUpdateModel(); + } + } + + onUpdateModel() { + this.updateModelList(); + + // If there are nodes upstream, trigger the execution of onUpdateModel. + for (let link_id in this.outputs[this.MODEL_OUTPUT].links) { + var link = this.outputs[this.MODEL_OUTPUT].links[link_id]; + if (link != null) { + console.log("link:", link, this.graph.links[link].target_id); + let upstream_node = this.graph.getNodeById(this.graph.links[link].target_id); + console.log(upstream_node.id); + if (upstream_node.onUpdateModel) { + upstream_node.onUpdateModel(); + } + } + } + } +} + +ImportMdpaModeler.title = "ImportMdpaModeler"; +ImportMdpaModeler.desc = "Select different ModelParts and access their submodelparts directly"; + +LiteGraph.registerNodeType("Stages/ImportMdpaModeler", ImportMdpaModeler); + +console.log("ImportMdpaModeler node created"); //helps to debug \ No newline at end of file diff --git a/js/nodes/stages/model_view.js b/js/nodes/stages/model_view.js new file mode 100644 index 0000000..e69de29 diff --git a/js/nodes/stages/multi_stage.js b/js/nodes/stages/multi_stage.js new file mode 100644 index 0000000..158708b --- /dev/null +++ b/js/nodes/stages/multi_stage.js @@ -0,0 +1,17 @@ + +class MultiStage extends InputList{ + constructor() { + super(); + this.input_type = "stage_data_model"; + this.output_type = "multi_stage_data_model"; + } +}; + +MultiStage.title = "MultiStage"; +MultiStage.desc = "Creates a MultiStage object from several Stages"; + +LiteGraph.registerNodeType("Stages/multi_stage", MultiStage); + +console.log("MultiStage node created"); + + \ No newline at end of file diff --git a/js/nodes/stages/post_process.js b/js/nodes/stages/post_process.js index e4cc2c5..a7cdc22 100644 --- a/js/nodes/stages/post_process.js +++ b/js/nodes/stages/post_process.js @@ -4,10 +4,11 @@ class StagePostProcess { this.glyph = {shape: '\uf35a', font:'900 14px "Font Awesome 5 Free"', width: 16, height: 9}; // List of inputs and outputs ("name", "type") + this.addInput("Stage", "stage_flow"); this.addInput("Modelers", "modelers_array"); this.addInput("Operations", "operations_array"); - this.addOutput("Post Process", "stage_post"); + this.addOutput("Output", "stage_flow"); } onExecute() { diff --git a/js/nodes/stages/pre_process.js b/js/nodes/stages/pre_process.js index 914acaf..862370f 100644 --- a/js/nodes/stages/pre_process.js +++ b/js/nodes/stages/pre_process.js @@ -1,13 +1,21 @@ class StagePreProcess { constructor() { + // Model + this.MODEL_INPUT = 0; + this.MODEL_OUTPUT = 0; + + this._model = []; + this._model_operations = []; + // Identifier Glyph this.glyph = {shape: '\uf359', font:'900 14px "Font Awesome 5 Free"', width: 16, height: 9}; // List of inputs and outputs ("name", "type") + this.addInput("Stage", "stage_flow"); this.addInput("Modelers", "modelers_array"); this.addInput("Operations", "operations_array"); - this.addOutput("Pre Process", "stage_pre"); + this.addOutput("Output", "stage_data_model"); } onExecute() { @@ -15,6 +23,58 @@ class StagePreProcess { onSelection(e) { } + + /** + * Updates the value of the node's model. + */ + onUpdateModel() { + this.updateModelList(); + + // If there are nodes upstream, trigger the execution of onUpdateModel. + for (let link_id in this.outputs[this.MODEL_OUTPUT].links) { + var link = this.outputs[this.MODEL_OUTPUT].links[link_id]; + if (link != null) { + let upstream_node = this.graph.getNodeById(this.graph.links[link].target_id); + if (upstream_node.onUpdateModel) { + upstream_node.onUpdateModel(); + } + } + } + } + + /** + * Get the list of model modelparts in the node + */ + getModelList() { + return this._model; + } + + /** + * Update the node's model with the values downstream. + */ + updateModelList() { + if (this.inputs[this.MODEL_INPUT].link) { + this._model = this.graph.getNodeById(this.graph.links[this.inputs[this.MODEL_INPUT].link].origin_id).getModelList(); + } else { + this._model = []; + } + + for (let op_id in this._model_operations) { + switch(this._model_operations[op_id].code) { + case "add": + this._model.push(this._model[op_id].data); + break; + case "rem": + // To be implemented + break; + default: + // Do nothing + break; + } + } + + this._model = [...new Set(this._model)]; + } } StagePreProcess.title = "Pre Process"; diff --git a/js/nodes/stages/stage.js b/js/nodes/stages/stage.js index 275581f..d68bc6f 100644 --- a/js/nodes/stages/stage.js +++ b/js/nodes/stages/stage.js @@ -10,15 +10,13 @@ class AnalysisStage { this._name = this.addWidget("text","Stage Name", "", function(v){}, {} ); // List of inputs and outputs ("name", "type") - this.addInput("Previous Stage", "analysis_stage") // 0 - this.addInput("Pre", "stage_pre"); // 1 - this.addInput("Problem Data", "problem_data"); // 2 - this.addInput("Solver Settings", "solver_settings"); // 3 - this.addInput("Processes", "process_list"); // 4 - this.addInput("Output Processes", "output_process_list"); // 5 - this.addInput("Post", "stage_post"); // 6 - - this.addOutput("Stage", "analysis_stage") // 0 + this.addInput("Pre", "stage_flow") // 0 + this.addInput("Problem Data", "problem_data"); // 1 + this.addInput("Solver Settings", "solver_settings"); // 2 + this.addInput("Processes", "process_list"); // 3 + this.addInput("Output Processes", "output_process_list"); // 4 + + this.addOutput("Post", "stage_flow") // 0 this.crop_title = 20; this.hover_tooltip = false; From 393ba62a675a6f42ac80e95cd19fe305394d2057 Mon Sep 17 00:00:00 2001 From: Carlos Roig Date: Fri, 11 Feb 2022 10:11:42 +0100 Subject: [PATCH 16/18] Fixing model lists --- js/nodes/model_inspector.js | 12 ++++---- js/nodes/stages/import_modeler.js | 48 +++++++++++++++---------------- 2 files changed, 29 insertions(+), 31 deletions(-) diff --git a/js/nodes/model_inspector.js b/js/nodes/model_inspector.js index a924d84..570f57d 100644 --- a/js/nodes/model_inspector.js +++ b/js/nodes/model_inspector.js @@ -21,8 +21,7 @@ class ModelInspector { onConnectionsChange(type, slot, connected, link_info, input_info) { if (type == LiteGraph.INPUT) { - this.onUpdateModel(); - this.updateModelOuputs(); + this.onUpdateModel(link_info.id, connected); } else { // Remove unconnected nodes for (let i = 0; i < this.outputs.length; i++) { @@ -51,8 +50,9 @@ class ModelInspector { /** * Updates the value of the node's model. */ - onUpdateModel() { - this.updateModelList(); + onUpdateModel(link_id, connected) { + this.updateModelList(link_id, connected); + this.updateModelOuputs(); // If there are nodes upstream, trigger the execution of onUpdateModel. for (let link_id in this.outputs[this.MODEL_OUTPUT].links) { @@ -69,8 +69,8 @@ class ModelInspector { /** * Update the node's model with the values downstream. */ - updateModelList() { - if (this.inputs[this.MODEL_INPUT].link) { + updateModelList(link=this.inputs[this.MODEL_INPUT].link, connected) { + if (link && connected) { this._model = [...this.graph.getNodeById(this.graph.links[this.inputs[this.MODEL_INPUT].link].origin_id).getModelList()]; } else { this._model = []; diff --git a/js/nodes/stages/import_modeler.js b/js/nodes/stages/import_modeler.js index 05f50af..45280e5 100644 --- a/js/nodes/stages/import_modeler.js +++ b/js/nodes/stages/import_modeler.js @@ -40,6 +40,27 @@ class ImportMdpaModeler { this.readModelList(file); } + onConnectionsChange(type, slot, connected, link_info, input_info) { + if (type == LiteGraph.INPUT) { + this.onUpdateModel(link_info.id, connected); + } + } + + onUpdateModel(link_id, connected) { + this.updateModelList(link_id, connected); + + // If there are nodes upstream, trigger the execution of onUpdateModel. + for (let link_id in this.outputs[this.MODEL_OUTPUT].links) { + var link = this.outputs[this.MODEL_OUTPUT].links[link_id]; + if (link != null) { + let upstream_node = this.graph.getNodeById(this.graph.links[link].target_id); + if (upstream_node.onUpdateModel) { + upstream_node.onUpdateModel(); + } + } + } + } + /** * Get the list of model modelparts in the node */ @@ -50,8 +71,8 @@ class ImportMdpaModeler { /** * Update the node's model with the values downstream. */ - updateModelList() { - if (this.inputs[this.MODEL_INPUT].link) { + updateModelList(link=this.inputs[this.MODEL_INPUT].link, connected) { + if (link && connected) { this._model = [...this.graph.getNodeById(this.graph.links[this.inputs[this.MODEL_INPUT].link].origin_id).getModelList()]; } else { this._model = []; @@ -118,29 +139,6 @@ class ImportMdpaModeler { this.onUpdateModel(); } } - - onConnectionsChange(type, slot, connected, link_info, input_info) { - if (type == LiteGraph.INPUT) { - this.onUpdateModel(); - } - } - - onUpdateModel() { - this.updateModelList(); - - // If there are nodes upstream, trigger the execution of onUpdateModel. - for (let link_id in this.outputs[this.MODEL_OUTPUT].links) { - var link = this.outputs[this.MODEL_OUTPUT].links[link_id]; - if (link != null) { - console.log("link:", link, this.graph.links[link].target_id); - let upstream_node = this.graph.getNodeById(this.graph.links[link].target_id); - console.log(upstream_node.id); - if (upstream_node.onUpdateModel) { - upstream_node.onUpdateModel(); - } - } - } - } } ImportMdpaModeler.title = "ImportMdpaModeler"; From 56a53a195a4319830ed7b3441c0f380e7999f6dd Mon Sep 17 00:00:00 2001 From: Carlos Roig Date: Fri, 11 Feb 2022 12:06:08 +0100 Subject: [PATCH 17/18] Abstracting model functionality --- index.html | 7 +- js/model_manager.js | 129 ++++++++++++++++++ js/nodes/model_inspector.js | 121 +++++++--------- .../stages/{ => modelers}/import_modeler.js | 62 +-------- js/nodes/stages/{ => modelers}/modeler.js | 2 +- js/nodes/stages/stage.js | 26 +--- 6 files changed, 189 insertions(+), 158 deletions(-) create mode 100644 js/model_manager.js rename js/nodes/stages/{ => modelers}/import_modeler.js (58%) rename js/nodes/stages/{ => modelers}/modeler.js (89%) diff --git a/index.html b/index.html index ac814a1..ec3590c 100644 --- a/index.html +++ b/index.html @@ -29,6 +29,9 @@ + + + @@ -68,8 +71,8 @@ - - + + diff --git a/js/model_manager.js b/js/model_manager.js new file mode 100644 index 0000000..7cbf176 --- /dev/null +++ b/js/model_manager.js @@ -0,0 +1,129 @@ +class ModelNode { + /** + * + * @param {*} type + * @param {*} slot + * @param {*} connected + * @param {*} link_info + * @param {*} input_info + */ + onConnectionsChange(type, slot, connected, link_info, input_info) { + if (type == LiteGraph.INPUT) { + if(link_info) { + this.onUpdateModel(link_info.id, connected); + } else { + console.log("Warning: Calling oConnectionsChange with no link info") + } + } + } + + /** + * + * @param {*} link_id + * @param {*} connected + */ + onUpdateModel(link_id, connected) { + this.updateModelList(link_id, connected); + + // If there are nodes upstream, trigger the execution of onUpdateModel. + for (let link_id in this.outputs[this.MODEL_OUTPUT].links) { + var link = this.outputs[this.MODEL_OUTPUT].links[link_id]; + if (link != null) { + let upstream_node = this.graph.getNodeById(this.graph.links[link].target_id); + if (upstream_node.onUpdateModel) { + upstream_node.onUpdateModel(link, true); + } + } + } + } + + /** + * Get the list of model modelparts in the node + */ + getModelList() { + return this._model; + } + + /** + * Update the node's model with the values downstream. + */ + updateModelList(link=this.inputs[this.MODEL_INPUT].link, connected) { + if (link && connected) { + this._model = [...this.graph.getNodeById(this.graph.links[this.inputs[this.MODEL_INPUT].link].origin_id).getModelList()]; + } else { + this._model = []; + } + + for (let op_id in this._model_operations) { + switch(this._model_operations[op_id].code) { + case "add": + this._model.push(this._model_operations[op_id].data); + break; + case "rem": + // To be implemented + break; + default: + // Do nothing + break; + } + } + + this._model = [...new Set(this._model)]; + } +} + +class ModelManager { + constructor() {} + + /** + * Extends the given class to return a class that implements the callbacks + * of the model manager. + * @param {LGraphNodeProto} node_type Prototype of the node class + */ + static registerNodeType(node_type) { + + // Extend class + if (node_type.prototype) { + + // onCconnectionChange + if(!node_type.prototype.onConnectionsChange) { + node_type.prototype.onConnectionsChange = ModelNode.prototype.onConnectionsChange; + } else { + node_type.prototype._onConnectionsChange = node_type.prototype.onConnectionsChange; + node_type.prototype._onConnectionsChangeModel = ModelNode.prototype.onConnectionsChange; + node_type.prototype.onConnectionsChange = function() { + this._onConnectionsChangeModel.apply(this, arguments); + this._onConnectionsChange.apply(this, arguments); + } + } + + // onUpdateModel + if(!node_type.prototype.onUpdateModel) { + node_type.prototype.onUpdateModel = ModelNode.prototype.onUpdateModel; + } else { + node_type.prototype._onUpdateModel = node_type.prototype.onUpdateModel; + node_type.prototype._onUpdateModelModel = ModelNode.prototype.onUpdateModel; + node_type.prototype.onUpdateModel = function() { + this._onUpdateModelModel.apply(this, arguments); + this._onUpdateModel.apply(this, arguments); + } + } + + // getModelList + if(!node_type.prototype.getModelList) { + node_type.prototype.getModelList = ModelNode.prototype.getModelList; + } else { + console.log("Warning: ModelNode still does not suport extending getModelList"); + } + + // updateModelList + if(!node_type.prototype.updateModelList) { + node_type.prototype.updateModelList = ModelNode.prototype.updateModelList; + } else { + console.log("Warning: ModelNode still does not suport extending updateModelList"); + } + } + + return node_type; + } +} \ No newline at end of file diff --git a/js/nodes/model_inspector.js b/js/nodes/model_inspector.js index 570f57d..9d1295f 100644 --- a/js/nodes/model_inspector.js +++ b/js/nodes/model_inspector.js @@ -1,8 +1,13 @@ class ModelInspector { constructor() { + // Model this.MODEL_INPUT = 0; this.MODEL_OUTPUT = 0; + this._model = []; + this._model_operations = []; + + // List of inputs and outputs ("name", "type") this.addInput("Model", 0); this.addOutput("value", 0); @@ -14,15 +19,52 @@ class ModelInspector { values:[] })); - this._model = []; - this.output_type = ""; } + /** Model */ + /** + * Executed on connection change. + * This function can get extended my ModelNode. + * @param {*} type + * @param {*} slot + * @param {*} connected + * @param {*} link_info + * @param {*} input_info + */ onConnectionsChange(type, slot, connected, link_info, input_info) { - if (type == LiteGraph.INPUT) { - this.onUpdateModel(link_info.id, connected); - } else { + this.updateNodeOutputSlots(type) + } + + /** + * Updates the value of the node's model. + * This function can get extended my ModelNode. + */ + onUpdateModel(link_id, connected) { + this.updateModelOuputs(); + } + + /** Internal */ + /** + * Populates the node widgets with the name of the modelparts available + * in the input's model, if exists. + */ + updateModelOuputs() { + for (let widget in this._output_slector_map) { + this._output_slector_map[widget].options.values = []; + if(this._model) { + this._output_slector_map[widget].options.values = this._model; + this._output_slector_map[widget].value = this._model[0]; + } + } + } + + /** + * Updates the list outputs on the node + * @param {connection type} type + */ + updateNodeOutputSlots(type) { + if (type == LiteGraph.OUTPUT) { // Remove unconnected nodes for (let i = 0; i < this.outputs.length; i++) { if (!this.isOutputConnected(i)) { @@ -46,78 +88,11 @@ class ModelInspector { this.size = this.computeSize(); } } - - /** - * Updates the value of the node's model. - */ - onUpdateModel(link_id, connected) { - this.updateModelList(link_id, connected); - this.updateModelOuputs(); - - // If there are nodes upstream, trigger the execution of onUpdateModel. - for (let link_id in this.outputs[this.MODEL_OUTPUT].links) { - var link = this.outputs[this.MODEL_OUTPUT].links[link_id]; - if (link != null) { - let upstream_node = this.graph.getNodeById(this.graph.links[link].target_id); - if (upstream_node.onUpdateModel) { - upstream_node.onUpdateModel(); - } - } - } - } - - /** - * Update the node's model with the values downstream. - */ - updateModelList(link=this.inputs[this.MODEL_INPUT].link, connected) { - if (link && connected) { - this._model = [...this.graph.getNodeById(this.graph.links[this.inputs[this.MODEL_INPUT].link].origin_id).getModelList()]; - } else { - this._model = []; - } - - for (let op_id in this._model_operations) { - switch(this._model_operations[op_id].code) { - case "add": - this._model.push(this._model_operations[op_id].data); - break; - case "rem": - // To be implemented - break; - default: - // Do nothing - break; - } - } - - this._model = [...new Set(this._model)]; - } - - /** - * Get the list of model modelparts in the node - */ - getModelList() { - return this._model; - } - - /** - * Populates the node widgets with the name of the modelparts available - * in the input's model, if exists. - */ - updateModelOuputs() { - for (let widget in this._output_slector_map) { - this._output_slector_map[widget].options.values = []; - if(this._model) { - this._output_slector_map[widget].options.values = this._model; - this._output_slector_map[widget].value = this._model[0]; - } - } - } } ModelInspector.title = "Model Inspector"; ModelInspector.desc = "Select different ModelParts and access their submodelparts directly"; -LiteGraph.registerNodeType("model_part/Model Inspector", ModelInspector); +LiteGraph.registerNodeType("model_part/Model Inspector", ModelManager.registerNodeType(ModelInspector)); console.log("ModelInspector node created"); //helps to debug \ No newline at end of file diff --git a/js/nodes/stages/import_modeler.js b/js/nodes/stages/modelers/import_modeler.js similarity index 58% rename from js/nodes/stages/import_modeler.js rename to js/nodes/stages/modelers/import_modeler.js index 45280e5..fbc85d9 100644 --- a/js/nodes/stages/import_modeler.js +++ b/js/nodes/stages/modelers/import_modeler.js @@ -40,61 +40,6 @@ class ImportMdpaModeler { this.readModelList(file); } - onConnectionsChange(type, slot, connected, link_info, input_info) { - if (type == LiteGraph.INPUT) { - this.onUpdateModel(link_info.id, connected); - } - } - - onUpdateModel(link_id, connected) { - this.updateModelList(link_id, connected); - - // If there are nodes upstream, trigger the execution of onUpdateModel. - for (let link_id in this.outputs[this.MODEL_OUTPUT].links) { - var link = this.outputs[this.MODEL_OUTPUT].links[link_id]; - if (link != null) { - let upstream_node = this.graph.getNodeById(this.graph.links[link].target_id); - if (upstream_node.onUpdateModel) { - upstream_node.onUpdateModel(); - } - } - } - } - - /** - * Get the list of model modelparts in the node - */ - getModelList() { - return this._model; - } - - /** - * Update the node's model with the values downstream. - */ - updateModelList(link=this.inputs[this.MODEL_INPUT].link, connected) { - if (link && connected) { - this._model = [...this.graph.getNodeById(this.graph.links[this.inputs[this.MODEL_INPUT].link].origin_id).getModelList()]; - } else { - this._model = []; - } - - for (let op_id in this._model_operations) { - switch(this._model_operations[op_id].code) { - case "add": - this._model.push(this._model_operations[op_id].data); - break; - case "rem": - // To be implemented - break; - default: - // Do nothing - break; - } - } - - this._model = [...new Set(this._model)]; - } - /** * Read the node's model with the values from a file. */ @@ -136,14 +81,15 @@ class ImportMdpaModeler { } // If there are nodes upstream, trigger the execution of onUpdateModel + // I can call this because I will extend the class. this.onUpdateModel(); } } } -ImportMdpaModeler.title = "ImportMdpaModeler"; -ImportMdpaModeler.desc = "Select different ModelParts and access their submodelparts directly"; +ImportMdpaModeler.title = "Import Mdpa Modeler"; +ImportMdpaModeler.desc = "This modeler loads a Mpda file and makes its modelparts avaliable"; -LiteGraph.registerNodeType("Stages/ImportMdpaModeler", ImportMdpaModeler); +LiteGraph.registerNodeType("Stages/Modelers/Import Mdpa", ModelManager.registerNodeType(ImportMdpaModeler)); console.log("ImportMdpaModeler node created"); //helps to debug \ No newline at end of file diff --git a/js/nodes/stages/modeler.js b/js/nodes/stages/modelers/modeler.js similarity index 89% rename from js/nodes/stages/modeler.js rename to js/nodes/stages/modelers/modeler.js index 22da43a..5b096de 100644 --- a/js/nodes/stages/modeler.js +++ b/js/nodes/stages/modelers/modeler.js @@ -19,6 +19,6 @@ class Modeler { Modeler.title = "Modeler"; Modeler.desc = "Select different ModelParts and access their submodelparts directly"; -LiteGraph.registerNodeType("Stages/Modeler", Modeler); +LiteGraph.registerNodeType("Stages/Modelers/Modeler", Modeler); console.log("Modeler node created"); //helps to debug \ No newline at end of file diff --git a/js/nodes/stages/stage.js b/js/nodes/stages/stage.js index d68bc6f..a371886 100644 --- a/js/nodes/stages/stage.js +++ b/js/nodes/stages/stage.js @@ -20,8 +20,8 @@ class AnalysisStage { this.crop_title = 20; this.hover_tooltip = false; - // this.error_list = [] - this.error_list = ["Error1", "Error2", "Error3", "Error4"]; + this.error_list = [] + // this.error_list = ["Error1", "Error2", "Error3", "Error4"]; } onDrawTitle(ctx) { @@ -42,28 +42,6 @@ class AnalysisStage { } } - onDrawBackground(ctx, node, canvas, graph_mouse) { - var title_height = LiteGraph.NODE_TITLE_HEIGHT; - var area = [0,0,0,0]; - area[0] = 0; //x - area[1] = -title_height; //y - area[2] = this.size[0] + 1; //w - area[3] = this.size[1] + title_height; //h - - console.log("ousize:", this.size); - ctx.strokeStyle = "#C44"; - ctx.fillStyle = "#C44"; - ctx.beginPath(); - ctx.roundRect( - area[0] + 10, - area[1] + 10, - area[2] + 10, - area[3] + 10, - [this.round_radius] - ); - ctx.fill(); - } - onDrawForeground(ctx, area) { if(this.hover_tooltip && this.error_list.length) { ErrorHandler.drawErrorTooltip(ctx, this); From 2e2656c1c03f636cd59287d35a0b46d52db2b263 Mon Sep 17 00:00:00 2001 From: Carlos Roig Date: Fri, 11 Feb 2022 17:36:05 +0100 Subject: [PATCH 18/18] Delete Modeler may or may not have been quite dificult to implement --- index.html | 1 + js/model_manager.js | 155 +++++++++++++++------ js/nodes/model_inspector.js | 25 +++- js/nodes/stages/model_view.js | 0 js/nodes/stages/modelers/delete_modeler.js | 93 +++++++++++++ js/nodes/stages/modelers/import_modeler.js | 7 +- js/nodes/stages/modelers/modeler.js | 4 +- js/nodes/stages/post_process.js | 6 +- js/nodes/stages/pre_process.js | 63 +-------- js/nodes/stages/stage.js | 6 +- 10 files changed, 244 insertions(+), 116 deletions(-) delete mode 100644 js/nodes/stages/model_view.js create mode 100644 js/nodes/stages/modelers/delete_modeler.js diff --git a/index.html b/index.html index ec3590c..a22301e 100644 --- a/index.html +++ b/index.html @@ -73,6 +73,7 @@ + + diff --git a/js/model_manager.js b/js/model_manager.js index 7cbf176..0c50be7 100644 --- a/js/model_manager.js +++ b/js/model_manager.js @@ -7,10 +7,10 @@ class ModelNode { * @param {*} link_info * @param {*} input_info */ - onConnectionsChange(type, slot, connected, link_info, input_info) { + _beforeConnectionsChange(type, slot, connected, link_info, input_info) { if (type == LiteGraph.INPUT) { if(link_info) { - this.onUpdateModel(link_info.id, connected); + this._beforeUpdateModel(link_info.id, connected); } else { console.log("Warning: Calling oConnectionsChange with no link info") } @@ -19,13 +19,41 @@ class ModelNode { /** * + * @param {*} type + * @param {*} slot + * @param {*} connected + * @param {*} link_info + * @param {*} input_info + */ + _afterConnectionsChange(type, slot, connected, link_info, input_info) { + if (type == LiteGraph.INPUT) { + if(link_info) { + if (this._onUpdateModel) { + this._onUpdateModel(link_info.id, connected); + } + this._afterUpdateModel(link_info.id, connected); + } else { + console.log("Warning: Calling oConnectionsChange with no link info") + } + } + } + + /** + * Get the model list from the nodes downstream. * @param {*} link_id * @param {*} connected */ - onUpdateModel(link_id, connected) { - this.updateModelList(link_id, connected); + _beforeUpdateModel() { + this._getDownstreamModelList(); + } - // If there are nodes upstream, trigger the execution of onUpdateModel. + /** + * Propagate the changes to nodes upstream. + * @param {*} link_id + * @param {*} connected + */ + _afterUpdateModel() { + this._setUpstreamModelList(); for (let link_id in this.outputs[this.MODEL_OUTPUT].links) { var link = this.outputs[this.MODEL_OUTPUT].links[link_id]; if (link != null) { @@ -38,37 +66,53 @@ class ModelNode { } /** - * Get the list of model modelparts in the node + * + * @param {*} link + * @param {*} connected */ - getModelList() { - return this._model; + _getDownstreamModelList(link=this.inputs[this.MODEL_INPUT].link, connected=true){ + if (link && connected) { + this._in_model = [...this.graph.getNodeById(this.graph.links[this.inputs[this.MODEL_INPUT].link].origin_id).getModelList()]; + } else { + this._in_model = []; + } + + this._out_model = [...this._in_model]; } /** * Update the node's model with the values downstream. */ - updateModelList(link=this.inputs[this.MODEL_INPUT].link, connected) { - if (link && connected) { - this._model = [...this.graph.getNodeById(this.graph.links[this.inputs[this.MODEL_INPUT].link].origin_id).getModelList()]; - } else { - this._model = []; - } - + _setUpstreamModelList() { + this._out_model = [...this._in_model]; for (let op_id in this._model_operations) { switch(this._model_operations[op_id].code) { case "add": - this._model.push(this._model_operations[op_id].data); + this._out_model.push(this._model_operations[op_id].data); break; case "rem": - // To be implemented + let index = this._out_model.indexOf(this._model_operations[op_id].data); + if (index !== -1) { + this._out_model.splice(index, 1); + } break; default: - // Do nothing break; } } - this._model = [...new Set(this._model)]; + this._out_model = [...new Set(this._out_model)]; + } + + /** + * Get the list of model modelparts in the node + */ + getModelList() { + return this._out_model; + } + + triggerUpstreamUpdate() { + this._afterUpdateModel(); } } @@ -85,30 +129,59 @@ class ModelManager { // Extend class if (node_type.prototype) { - // onCconnectionChange - if(!node_type.prototype.onConnectionsChange) { - node_type.prototype.onConnectionsChange = ModelNode.prototype.onConnectionsChange; - } else { - node_type.prototype._onConnectionsChange = node_type.prototype.onConnectionsChange; - node_type.prototype._onConnectionsChangeModel = ModelNode.prototype.onConnectionsChange; - node_type.prototype.onConnectionsChange = function() { - this._onConnectionsChangeModel.apply(this, arguments); - this._onConnectionsChange.apply(this, arguments); - } + /////////////// + // Internal // + /////////////// + + // add types + node_type.prototype._in_model = []; + node_type.prototype._out_model = []; + node_type.prototype._model_operations = []; + + // _onCconnectionChange + node_type.prototype._onConnectionsChange = node_type.prototype.onConnectionsChange; + node_type.prototype._beforeConnectionsChange = ModelNode.prototype._beforeConnectionsChange; + node_type.prototype._afterConnectionsChangeModel = ModelNode.prototype._afterConnectionsChange; + node_type.prototype.onConnectionsChange = function() { + // First process the connection change, then apply the node code. + this._beforeConnectionsChange.apply(this, arguments); + if(node_type.prototype._onConnectionsChange) { + this._onConnectionsChange.apply(this, arguments); + } + this._afterConnectionsChangeModel.apply(this, arguments); } - // onUpdateModel - if(!node_type.prototype.onUpdateModel) { - node_type.prototype.onUpdateModel = ModelNode.prototype.onUpdateModel; - } else { - node_type.prototype._onUpdateModel = node_type.prototype.onUpdateModel; - node_type.prototype._onUpdateModelModel = ModelNode.prototype.onUpdateModel; - node_type.prototype.onUpdateModel = function() { - this._onUpdateModelModel.apply(this, arguments); + // _onUpdateModel + node_type.prototype._onUpdateModel = node_type.prototype.onUpdateModel; + node_type.prototype._beforeUpdateModel = ModelNode.prototype._beforeUpdateModel; + node_type.prototype._afterUpdateModel = ModelNode.prototype._afterUpdateModel; + node_type.prototype.onUpdateModel = function() { + // First apply the node code, then propagate the update. + this._beforeUpdateModel.apply(this, arguments); + if (node_type.prototype._onUpdateModel) { this._onUpdateModel.apply(this, arguments); } + this._afterUpdateModel.apply(this, arguments); } + // _getDownstreamModelList + if(!node_type.prototype._getDownstreamModelList) { + node_type.prototype._getDownstreamModelList = ModelNode.prototype._getDownstreamModelList; + } else { + console.log("Warning: ModelNode still does not suport extending _getDownstreamModelList"); + } + + // _setUpstreamModelList + if(!node_type.prototype._setUpstreamModelList) { + node_type.prototype._setUpstreamModelList = ModelNode.prototype._setUpstreamModelList; + } else { + console.log("Warning: ModelNode still does not suport extending _setUpstreamModelList"); + } + + /////////////// + // Interface // + /////////////// + // getModelList if(!node_type.prototype.getModelList) { node_type.prototype.getModelList = ModelNode.prototype.getModelList; @@ -116,11 +189,11 @@ class ModelManager { console.log("Warning: ModelNode still does not suport extending getModelList"); } - // updateModelList - if(!node_type.prototype.updateModelList) { - node_type.prototype.updateModelList = ModelNode.prototype.updateModelList; + // triggerUpstreamUpdate + if(!node_type.prototype.triggerUpstreamUpdate) { + node_type.prototype.triggerUpstreamUpdate = ModelNode.prototype.triggerUpstreamUpdate; } else { - console.log("Warning: ModelNode still does not suport extending updateModelList"); + console.log("Warning: ModelNode still does not suport extending triggerUpstreamUpdate"); } } diff --git a/js/nodes/model_inspector.js b/js/nodes/model_inspector.js index 9d1295f..3f2d8ac 100644 --- a/js/nodes/model_inspector.js +++ b/js/nodes/model_inspector.js @@ -4,9 +4,6 @@ class ModelInspector { this.MODEL_INPUT = 0; this.MODEL_OUTPUT = 0; - this._model = []; - this._model_operations = []; - // List of inputs and outputs ("name", "type") this.addInput("Model", 0); @@ -50,11 +47,25 @@ class ModelInspector { * in the input's model, if exists. */ updateModelOuputs() { + console.log("Executing updateModelOuputs") for (let widget in this._output_slector_map) { this._output_slector_map[widget].options.values = []; - if(this._model) { - this._output_slector_map[widget].options.values = this._model; - this._output_slector_map[widget].value = this._model[0]; + let model_values = this.getModelList(); + if(model_values) { + let old_value = this._output_slector_map[widget].value; + + this._output_slector_map[widget].options.values = model_values; + this._output_slector_map[widget].value = model_values[0]; + + if (old_value) { + let index = this._output_slector_map[widget].options.values.indexOf(old_value); + if (index != -1) { + this._output_slector_map[widget].value = old_value; + } + } + } else { + this._output_slector_map[widget].options.values = []; + this._output_slector_map[widget].value = ""; } } } @@ -80,7 +91,7 @@ class ModelInspector { if (this.outputs.length <= 0 || this.isOutputConnected(this.outputs.length - 1)) { this.addOutput("value", 0); this._output_slector_map.push(this.addWidget("combo","ModelPart", "DEBUG", null, { - values:this._model + values:this.getModelList() })); } diff --git a/js/nodes/stages/model_view.js b/js/nodes/stages/model_view.js deleted file mode 100644 index e69de29..0000000 diff --git a/js/nodes/stages/modelers/delete_modeler.js b/js/nodes/stages/modelers/delete_modeler.js new file mode 100644 index 0000000..253a6e7 --- /dev/null +++ b/js/nodes/stages/modelers/delete_modeler.js @@ -0,0 +1,93 @@ +class DeleteModeler { + constructor() { + // Model + this.MODEL_INPUT = 0; + this.MODEL_OUTPUT = 0; + + // List of inputs and outputs ("name", "type") + this.addInput("", 0); + this.addOutput("", 0); + + this.widgets_up = true; + + this._output_slector_map = []; + + this.output_type = ""; + } + + /** + * Executed on connection change. + * This function can get extended my ModelNode. + * @param {*} type + * @param {*} slot + * @param {*} connected + * @param {*} link_info + * @param {*} input_info + */ + onConnectionsChange(type, slot, connected, link_info, input_info) { + if (type == LiteGraph.INPUT) { + this.updateWidgetList(); + } + } + + /** + * Updates the value of the node's model. + * This function can get extended my ModelNode. + */ + onUpdateModel(link_id, connected) { + this.updateWidgetList(); + this.updateModelOuputs(); + } + + onUpdateDropdown() { + this.updateModelOuputs(); + this.triggerUpstreamUpdate(); + } + + /** Internal */ + /** + * Populates the node widgets with the name of the modelparts available + * in the input's model, if exists. + */ + updateModelOuputs() { + this._model_operations = []; + for (let index in this._output_slector_map) { + let widget = this._output_slector_map[index]; + if (widget.value == "Delete") { + this._model_operations.push({code:"rem", data:widget.name}); + } + } + } + + /** + * Updates the list outputs on the node + */ + updateWidgetList() { + let model_list = this.getModelList(); + + // Remove unconnected nodes + if (this.widgets) { + while ( this.widgets.length ) { + this.removeWidget(0); + } + } + + // Delete the internal reference that we have in the node. + this._output_slector_map = []; + + for (let index in model_list) { + this._output_slector_map.push(this.addWidget("combo", model_list[index], "Keep", (v) => {console.log("hola?"); this.onUpdateDropdown();}, { + values:["Keep", "Delete"] + })); + } + + this.size = this.computeSize(); + } +} + +DeleteModeler.title = "Delete Modeler"; +DeleteModeler.desc = "Select different ModelParts and access their submodelparts directly"; + +LiteGraph.registerNodeType("Stages/Modelers/Delete Modeler", ModelManager.registerNodeType(DeleteModeler)); + +console.log("DeleteModeler node created"); //helps to debug \ No newline at end of file diff --git a/js/nodes/stages/modelers/import_modeler.js b/js/nodes/stages/modelers/import_modeler.js index fbc85d9..aa41dcb 100644 --- a/js/nodes/stages/modelers/import_modeler.js +++ b/js/nodes/stages/modelers/import_modeler.js @@ -4,14 +4,11 @@ class ImportMdpaModeler { this.MODEL_INPUT = 0; this.MODEL_OUTPUT = 0; - this._model = []; - this._model_operations = []; - // Identifier Glyph this.glyph = {shape: '\uf6cf', font:'900 14px "Font Awesome 5 Free"', width: 16, height: 9}; // List of inputs and outputs ("name", "type") - this.addInput("Stage", "stage_flow"); + this.addInput("Stage", "stage_modeler"); // List Manager this.input_manager = document.createElement('input'); @@ -23,7 +20,7 @@ class ImportMdpaModeler { node.input_manager.click(); }); - this.addOutput("Modeler", "stage_flow"); + this.addOutput("Modeler", "stage_modeler"); this.size = this.computeSize(); this.serialize_widgets = true; diff --git a/js/nodes/stages/modelers/modeler.js b/js/nodes/stages/modelers/modeler.js index 5b096de..a37596a 100644 --- a/js/nodes/stages/modelers/modeler.js +++ b/js/nodes/stages/modelers/modeler.js @@ -4,9 +4,9 @@ class Modeler { this.glyph = {shape: '\uf6cf', font:'900 14px "Font Awesome 5 Free"', width: 16, height: 9}; // List of inputs and outputs ("name", "type") - this.addInput("Input", "stage_data_model"); + this.addInput("Input", "stage_modeler"); - this.addOutput("Modeler", "modeler") + this.addOutput("Modeler", "stage_modeler") } onExecute() { diff --git a/js/nodes/stages/post_process.js b/js/nodes/stages/post_process.js index a7cdc22..81b4da1 100644 --- a/js/nodes/stages/post_process.js +++ b/js/nodes/stages/post_process.js @@ -1,5 +1,9 @@ class StagePostProcess { constructor() { + // Model + this.MODEL_INPUT = 0; + this.MODEL_OUTPUT = 0; + // Identifier Glyph this.glyph = {shape: '\uf35a', font:'900 14px "Font Awesome 5 Free"', width: 16, height: 9}; @@ -21,6 +25,6 @@ class StagePostProcess { StagePostProcess.title = "Post Process"; StagePostProcess.desc = "Select different ModelParts and access their submodelparts directly"; -LiteGraph.registerNodeType("Stages/PostProcess", StagePostProcess); +LiteGraph.registerNodeType("Stages/PostProcess", ModelManager.registerNodeType(StagePostProcess)); console.log("Modeler node created"); //helps to debug \ No newline at end of file diff --git a/js/nodes/stages/pre_process.js b/js/nodes/stages/pre_process.js index 862370f..72dd586 100644 --- a/js/nodes/stages/pre_process.js +++ b/js/nodes/stages/pre_process.js @@ -1,21 +1,18 @@ class StagePreProcess { constructor() { // Model - this.MODEL_INPUT = 0; + this.MODEL_INPUT = 1; this.MODEL_OUTPUT = 0; - - this._model = []; - this._model_operations = []; // Identifier Glyph this.glyph = {shape: '\uf359', font:'900 14px "Font Awesome 5 Free"', width: 16, height: 9}; // List of inputs and outputs ("name", "type") this.addInput("Stage", "stage_flow"); - this.addInput("Modelers", "modelers_array"); + this.addInput("Modelers", "stage_modeler"); this.addInput("Operations", "operations_array"); - this.addOutput("Output", "stage_data_model"); + this.addOutput("Output", "stage_flow"); } onExecute() { @@ -23,63 +20,11 @@ class StagePreProcess { onSelection(e) { } - - /** - * Updates the value of the node's model. - */ - onUpdateModel() { - this.updateModelList(); - - // If there are nodes upstream, trigger the execution of onUpdateModel. - for (let link_id in this.outputs[this.MODEL_OUTPUT].links) { - var link = this.outputs[this.MODEL_OUTPUT].links[link_id]; - if (link != null) { - let upstream_node = this.graph.getNodeById(this.graph.links[link].target_id); - if (upstream_node.onUpdateModel) { - upstream_node.onUpdateModel(); - } - } - } - } - - /** - * Get the list of model modelparts in the node - */ - getModelList() { - return this._model; - } - - /** - * Update the node's model with the values downstream. - */ - updateModelList() { - if (this.inputs[this.MODEL_INPUT].link) { - this._model = this.graph.getNodeById(this.graph.links[this.inputs[this.MODEL_INPUT].link].origin_id).getModelList(); - } else { - this._model = []; - } - - for (let op_id in this._model_operations) { - switch(this._model_operations[op_id].code) { - case "add": - this._model.push(this._model[op_id].data); - break; - case "rem": - // To be implemented - break; - default: - // Do nothing - break; - } - } - - this._model = [...new Set(this._model)]; - } } StagePreProcess.title = "Pre Process"; StagePreProcess.desc = "Select different ModelParts and access their submodelparts directly"; -LiteGraph.registerNodeType("Stages/PreProcess", StagePreProcess); +LiteGraph.registerNodeType("Stages/PreProcess", ModelManager.registerNodeType(StagePreProcess)); console.log("Modeler node created"); //helps to debug \ No newline at end of file diff --git a/js/nodes/stages/stage.js b/js/nodes/stages/stage.js index a371886..20b28e6 100644 --- a/js/nodes/stages/stage.js +++ b/js/nodes/stages/stage.js @@ -1,5 +1,9 @@ class AnalysisStage { constructor() { + // Model + this.MODEL_INPUT = 0; + this.MODEL_OUTPUT = 0; + // Identifier Glyph this.glyph = {shape: '\uf121', font:'900 14px "Font Awesome 5 Free"', width: 16, height: 9}; @@ -91,6 +95,6 @@ class AnalysisStage { AnalysisStage.title = "AnalysisStage"; AnalysisStage.desc = "Select different ModelParts and access their submodelparts directly"; -LiteGraph.registerNodeType("Stages/AnalysisStage", AnalysisStage); +LiteGraph.registerNodeType("Stages/AnalysisStage", ModelManager.registerNodeType(AnalysisStage)); console.log("AnalysisStage node created"); \ No newline at end of file