this repo has no description

saving / loading node graphs maybe working?

phaz.uk 88be0c10 0dd169d3

verified
Changed files
+189 -26
src
src-tauri
src
frontend_calls
+2 -1
src-tauri/src/frontend_calls/mod.rs
··· 1 - pub mod get_addresses; 1 + pub mod get_addresses; 2 + pub mod save_graph;
+16
src-tauri/src/frontend_calls/save_graph.rs
··· 1 + use std::{ fs::File, io::Write }; 2 + 3 + use flate2::{ write::GzEncoder, Compression }; 4 + 5 + #[tauri::command] 6 + pub fn save_graph( graph: String ) { 7 + dbg!(&graph); 8 + 9 + let path = dirs::config_dir().unwrap().join("VRCMacros").join("graph"); 10 + 11 + let file = File::create(&path).unwrap(); 12 + let mut encoder = GzEncoder::new(file, Compression::default()); 13 + 14 + encoder.write_all(graph.as_bytes()).unwrap(); 15 + encoder.finish().unwrap(); 16 + }
+2
src-tauri/src/lib.rs
··· 35 35 .plugin(tauri_plugin_opener::init()) 36 36 .invoke_handler(tauri::generate_handler![ 37 37 get_addresses::get_addresses, 38 + 39 + save_graph::save_graph 38 40 ]) 39 41 .manage(conf) 40 42 .manage(&ADDRESSES)
+3
src/App.tsx
··· 239 239 movingNode.y = movingNode.y - (mouseStartPos[1] - e.clientY) / scale; 240 240 241 241 mouseStartPos = [ e.clientX, e.clientY ]; 242 + NodeManager.Instance.UpdateConfig(); 242 243 } else{ 243 244 offsetTarget = [ offsetTarget[0] - (mouseStartPos[0] - e.clientX) / scale, offsetTarget[1] - (mouseStartPos[1] - e.clientY) / scale ]; 244 245 mouseStartPos = [ e.clientX, e.clientY ]; ··· 305 306 ){ 306 307 drawingFrom!.connections.push(input); 307 308 input.connections.push(drawingFrom!); 309 + 310 + NodeManager.Instance.UpdateConfig(); 308 311 } 309 312 } 310 313 }
+6 -5
src/ContextMenu/Canvas.tsx
··· 14 14 15 15 let node: Node = { 16 16 name: 'OSC Trigger', 17 - id: 'trigger', 17 + id: NodeManager.Instance.GetNewNodeId(), 18 18 x: pos[0], 19 19 y: pos[1], 20 20 w: 200, ··· 94 94 }); 95 95 96 96 node.h = 60 + (parameters.length + 1) * 30; 97 + NodeManager.Instance.UpdateConfig(); 97 98 })(); 98 99 } 99 100 }; ··· 115 116 116 117 let node: Node = { 117 118 name: 'If Equals', 118 - id: 'ifeq', 119 + id: NodeManager.Instance.GetNewNodeId(), 119 120 x: pos[0], 120 121 y: pos[1], 121 122 w: 220, ··· 191 192 192 193 let node: Node = { 193 194 name: 'String', 194 - id: 'static-string', 195 + id: NodeManager.Instance.GetNewNodeId(), 195 196 x: pos[0], 196 197 y: pos[1], 197 198 w: 200, ··· 223 224 224 225 let node: Node = { 225 226 name: 'Int', 226 - id: 'static-int', 227 + id: NodeManager.Instance.GetNewNodeId(), 227 228 x: pos[0], 228 229 y: pos[1], 229 230 w: 200, ··· 266 267 267 268 let node: Node = { 268 269 name: 'Send Chatbox', 269 - id: 'send-chatbox', 270 + id: NodeManager.Instance.GetNewNodeId(), 270 271 x: pos[0], 271 272 y: pos[1], 272 273 w: 200,
+5
src/ControlBar.tsx
··· 6 6 import { invoke } from '@tauri-apps/api/core'; 7 7 import { OSCMessage } from './structs/OscMessage'; 8 8 import { ParameterList } from './components/ParameterList'; 9 + import { NodeManager } from './Mangers/NodeManager'; 9 10 10 11 export interface ControlBarProps{ 11 12 node: Accessor<Node | null>, ··· 55 56 56 57 item.value = text; 57 58 node.onStaticsUpdate(node); 59 + 60 + NodeManager.Instance.UpdateConfig(); 58 61 }} /> 59 62 </div> 60 63 </Match> ··· 79 82 80 83 item.value = value; 81 84 node.onStaticsUpdate(node); 85 + 86 + NodeManager.Instance.UpdateConfig(); 82 87 }} /> 83 88 </Show> 84 89 </Match>
+129
src/Mangers/NodeManager.tsx
··· 1 + import { invoke } from "@tauri-apps/api/core"; 1 2 import { Node } from "../structs/node"; 2 3 3 4 export class NodeManager{ 4 5 public static Instance: NodeManager; 5 6 6 7 private _nodes: Node[] = []; 8 + private _newestNodeId = 0; 9 + private _needsSave = false; 7 10 8 11 constructor(){ 9 12 NodeManager.Instance = this; 13 + 14 + setInterval(() => { 15 + // Save config every 1 second 16 + if(this._needsSave)this._saveConfigToDisk(); 17 + }, 1_000); 10 18 } 11 19 12 20 public AddNode( node: Node ){ 13 21 this._nodes.push(node); 22 + this.UpdateConfig(); 14 23 } 15 24 16 25 public RemoveNode( node: Node ){ 17 26 this._nodes = this._nodes.filter(x => x !== node); 27 + this.UpdateConfig(); 18 28 } 19 29 20 30 public GetNodes(): Node[]{ 21 31 return this._nodes; 32 + } 33 + 34 + public GetNewNodeId(){ 35 + return this._newestNodeId++; // TODO: really need a better solution than this, but it'll work for now 36 + } 37 + 38 + public UpdateConfig(){ 39 + this._needsSave = true; 40 + } 41 + 42 + private _loadFromConfig( config: string ){ 43 + let json = JSON.parse(config); 44 + 45 + this._nodes = []; 46 + 47 + // Populate nodes 48 + for (let i = 0; i < json.length; i++) { 49 + let node = json[i]; 50 + 51 + this._nodes.push({ 52 + name: node.name, 53 + id: node.id, 54 + x: node.x, y: node.y, 55 + w: node.w, h: node.h, 56 + inputs: [], 57 + outputs: [], 58 + selected: false, 59 + statics: node.statics, 60 + onStaticsUpdate: ( _node ) => {} // TODO: Make a seperate setup for node logic so we can load from that 61 + }) 62 + 63 + this._newestNodeId = node.id 64 + } 65 + 66 + // Populate node outputs 67 + for (let i = 0; i < json.length; i++) { 68 + let configNode = json[i]; 69 + let node = this._nodes[i]; 70 + 71 + for (let j = 0; j < configNode.outputs.length; j++) { 72 + let output = configNode.outputs[j]; 73 + node.outputs.push({ 74 + name: output.name, 75 + type: output.type, 76 + connections: [], 77 + parent: node, 78 + index: j 79 + }) 80 + } 81 + } 82 + 83 + // Populate node inputs 84 + for (let i = 0; i < json.length; i++) { 85 + let configNode = json[i]; 86 + let outputParentNode = this._nodes[i]; 87 + 88 + for (let j = 0; j < configNode.outputs.length; j++) { 89 + let output = configNode.outputs[j]; 90 + 91 + for (let k = 0; k < output.connections.length; k++) { 92 + let input = output.connections[k]; 93 + let node = this._nodes[input.node]; 94 + 95 + let realInput = node.inputs.find(x => x.index === input.index); 96 + let realOutput = outputParentNode.outputs[j]; 97 + 98 + if(realInput){ 99 + realInput.connections.push(realOutput); 100 + } else{ 101 + node.inputs.push({ 102 + name: input.name, 103 + type: input.type, 104 + parent: node, 105 + connections: [ realOutput ], 106 + index: input.index 107 + }) 108 + } 109 + } 110 + } 111 + } 112 + } 113 + 114 + private _saveConfigToDisk(){ 115 + this._needsSave = false; 116 + // Convert it into a structure we can actually save... 117 + 118 + let nodesToSave = []; 119 + 120 + for (let i = 0; i < this._nodes.length; i++) { 121 + let node = this._nodes[i]; 122 + 123 + let nodeOutputs = []; 124 + 125 + for (let j = 0; j < node.outputs.length; j++) { 126 + let output = node.outputs[j]; 127 + 128 + nodeOutputs.push({ 129 + name: output.name, 130 + type: output.type, 131 + connections: output.connections.map(x => { return { 132 + name: x.name, 133 + node: x.parent.id, 134 + index: x.index, 135 + type: x.type 136 + }}) 137 + }) 138 + } 139 + 140 + nodesToSave.push({ 141 + name: node.name, 142 + id: node.id, 143 + x: node.x, y: node.y, 144 + w: node.w, h: node.h, 145 + statics: node.statics, 146 + outputs: nodeOutputs 147 + }) 148 + } 149 + 150 + invoke('save_graph', { graph: JSON.stringify(nodesToSave) }); 22 151 } 23 152 }
+22 -16
src/renderer.ts
··· 60 60 ctx.textBaseline = 'top'; 61 61 62 62 nodes.map(node => { 63 + let nodeX = Math.round(node.x / 10) * 10; 64 + let nodeY = Math.round(node.y / 10) * 10; 65 + 63 66 ctx.fillStyle = '#1f2129'; 64 67 ctx.strokeStyle = node.selected ? '#004696ff' : '#fff5'; 65 68 ctx.lineWidth = 5 * position.scale; 66 69 67 70 // Draw Node Box 68 71 drawRoundedRect(ctx, 69 - (node.x + startX + position.x) * position.scale, 70 - (node.y + startY + position.y) * position.scale, 72 + (nodeX + startX + position.x) * position.scale, 73 + (nodeY + startY + position.y) * position.scale, 71 74 node.w * position.scale, 72 75 node.h * position.scale, 73 76 10 * position.scale); ··· 81 84 ctx.textAlign = 'center'; 82 85 83 86 ctx.fillText(node.name, 84 - (node.x + (node.w * 0.5) + startX + position.x) * position.scale, 85 - (node.y + 10 + startY + position.y) * position.scale 87 + (nodeX + (node.w * 0.5) + startX + position.x) * position.scale, 88 + (nodeY + 10 + startY + position.y) * position.scale 86 89 ); 87 90 88 91 // Draw Inputs ··· 92 95 node.inputs.map(( input, i ) => { 93 96 ctx.fillStyle = NodeIOLinkColours(input); 94 97 ctx.fillRect( 95 - (node.x + 10 + startX + position.x) * position.scale, 96 - (node.y + 50 + (30 * i) + startY + position.y) * position.scale, 98 + (nodeX + 10 + startX + position.x) * position.scale, 99 + (nodeY + 50 + (30 * i) + startY + position.y) * position.scale, 97 100 20 * position.scale, 20 * position.scale 98 101 ) 99 102 100 103 ctx.fillText(input.name, 101 - (node.x + 35 + startX + position.x) * position.scale, 102 - (node.y + 53 + (30 * i) + startY + position.y) * position.scale, 104 + (nodeX + 35 + startX + position.x) * position.scale, 105 + (nodeY + 53 + (30 * i) + startY + position.y) * position.scale, 103 106 ) 104 107 }) 105 108 ··· 109 112 node.outputs.map(( output, i ) => { 110 113 ctx.fillStyle = NodeIOLinkColours(output); 111 114 ctx.fillRect( 112 - (node.x + (node.w - 30) + startX + position.x) * position.scale, 113 - (node.y + 50 + (30 * i) + startY + position.y) * position.scale, 115 + (nodeX + (node.w - 30) + startX + position.x) * position.scale, 116 + (nodeY + 50 + (30 * i) + startY + position.y) * position.scale, 114 117 20 * position.scale, 20 * position.scale 115 118 ) 116 119 117 120 ctx.fillText(output.name, 118 - (node.x + (node.w - 35) + startX + position.x) * position.scale, 119 - (node.y + 53 + (30 * i) + startY + position.y) * position.scale, 121 + (nodeX + (node.w - 35) + startX + position.x) * position.scale, 122 + (nodeY + 53 + (30 * i) + startY + position.y) * position.scale, 120 123 ) 121 124 }) 122 125 }) 123 126 124 127 nodes.map(node => { 128 + let nodeX = Math.round(node.x / 10) * 10; 129 + let nodeY = Math.round(node.y / 10) * 10; 130 + 125 131 node.outputs.map(( output, i ) => { 126 132 output.connections.map(partner => { 127 133 ctx.strokeStyle = NodeIOLinkColours(output); 128 134 drawCurve(ctx, 129 - (node.x + (node.w - 30) + 10 + startX + position.x) * position.scale, 130 - (node.y + 50 + (30 * i) + 10 + startY + position.y) * position.scale, 131 - (partner.parent.x + 20 + startX + position.x) * position.scale, 132 - (partner.parent.y + 60 + (30 * partner.index) + startY + position.y) * position.scale, 135 + (nodeX + (node.w - 30) + 10 + startX + position.x) * position.scale, 136 + (nodeY + 50 + (30 * i) + 10 + startY + position.y) * position.scale, 137 + ((Math.round(partner.parent.x / 10) * 10) + 20 + startX + position.x) * position.scale, 138 + ((Math.round(partner.parent.y / 10) * 10) + 60 + (30 * partner.index) + startY + position.y) * position.scale, 133 139 ); 134 140 ctx.stroke(); 135 141 })
+4 -4
src/structs/node.ts
··· 1 1 export interface Node{ 2 2 name: string, 3 - id: string, 3 + id: number, 4 4 x: number, 5 5 y: number, 6 6 w: number, ··· 21 21 } 22 22 23 23 export enum NodeType{ 24 - Label, 24 + Label = 0, 25 25 26 26 String = 1, 27 27 Float = 2, ··· 33 33 AnyTypeB = 7, 34 34 AnyTypeC = 8, 35 35 36 - OSCAddress, 37 - ParameterList 36 + OSCAddress = 9, 37 + ParameterList = 10 38 38 } 39 39 40 40 export let NodeIOResolveAnyTypes = ( nodeio: NodeIO ): NodeType | null => {