+2
-1
src-tauri/src/frontend_calls/mod.rs
+2
-1
src-tauri/src/frontend_calls/mod.rs
+16
src-tauri/src/frontend_calls/save_graph.rs
+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
+2
src-tauri/src/lib.rs
+3
src/App.tsx
+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
+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
+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
+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
+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
+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 => {