+4
-1
.gitignore
+4
-1
.gitignore
-3
.vscode/extensions.json
-3
.vscode/extensions.json
+2
package.json
+2
package.json
···
13
13
"license": "MIT",
14
14
"dependencies": {
15
15
"@tauri-apps/api": "^2.9.0",
16
+
"@tauri-apps/plugin-clipboard-manager": "~2",
16
17
"@tauri-apps/plugin-dialog": "~2.4.2",
17
18
"@tauri-apps/plugin-opener": "^2.5.2",
19
+
"@tauri-apps/plugin-os": "~2",
18
20
"animejs": "^4.2.2",
19
21
"solid-js": "^1.9.10"
20
22
},
+20
pnpm-lock.yaml
+20
pnpm-lock.yaml
···
11
11
'@tauri-apps/api':
12
12
specifier: ^2.9.0
13
13
version: 2.9.0
14
+
'@tauri-apps/plugin-clipboard-manager':
15
+
specifier: ~2
16
+
version: 2.3.2
14
17
'@tauri-apps/plugin-dialog':
15
18
specifier: ~2.4.2
16
19
version: 2.4.2
17
20
'@tauri-apps/plugin-opener':
18
21
specifier: ^2.5.2
19
22
version: 2.5.2
23
+
'@tauri-apps/plugin-os':
24
+
specifier: ~2
25
+
version: 2.3.2
20
26
animejs:
21
27
specifier: ^4.2.2
22
28
version: 4.2.2
···
479
485
engines: {node: '>= 10'}
480
486
hasBin: true
481
487
488
+
'@tauri-apps/plugin-clipboard-manager@2.3.2':
489
+
resolution: {integrity: sha512-CUlb5Hqi2oZbcZf4VUyUH53XWPPdtpw43EUpCza5HWZJwxEoDowFzNUDt1tRUXA8Uq+XPn17Ysfptip33sG4eQ==}
490
+
482
491
'@tauri-apps/plugin-dialog@2.4.2':
483
492
resolution: {integrity: sha512-lNIn5CZuw8WZOn8zHzmFmDSzg5zfohWoa3mdULP0YFh/VogVdMVWZPcWSHlydsiJhRQYaTNSYKN7RmZKE2lCYQ==}
484
493
485
494
'@tauri-apps/plugin-opener@2.5.2':
486
495
resolution: {integrity: sha512-ei/yRRoCklWHImwpCcDK3VhNXx+QXM9793aQ64YxpqVF0BDuuIlXhZgiAkc15wnPVav+IbkYhmDJIv5R326Mew==}
496
+
497
+
'@tauri-apps/plugin-os@2.3.2':
498
+
resolution: {integrity: sha512-n+nXWeuSeF9wcEsSPmRnBEGrRgOy6jjkSU+UVCOV8YUGKb2erhDOxis7IqRXiRVHhY8XMKks00BJ0OAdkpf6+A==}
487
499
488
500
'@types/animejs@3.1.13':
489
501
resolution: {integrity: sha512-yWg9l1z7CAv/TKpty4/vupEh24jDGUZXv4r26StRkpUPQm04ztJaftgpto8vwdFs8SiTq6XfaPKCSI+wjzNMvQ==}
···
1065
1077
'@tauri-apps/cli-win32-ia32-msvc': 2.9.3
1066
1078
'@tauri-apps/cli-win32-x64-msvc': 2.9.3
1067
1079
1080
+
'@tauri-apps/plugin-clipboard-manager@2.3.2':
1081
+
dependencies:
1082
+
'@tauri-apps/api': 2.9.0
1083
+
1068
1084
'@tauri-apps/plugin-dialog@2.4.2':
1069
1085
dependencies:
1070
1086
'@tauri-apps/api': 2.9.0
1071
1087
1072
1088
'@tauri-apps/plugin-opener@2.5.2':
1089
+
dependencies:
1090
+
'@tauri-apps/api': 2.9.0
1091
+
1092
+
'@tauri-apps/plugin-os@2.3.2':
1073
1093
dependencies:
1074
1094
'@tauri-apps/api': 2.9.0
1075
1095
+98
-54
src/App.tsx
+98
-54
src/App.tsx
···
1
-
import { createSignal, onCleanup, onMount } from "solid-js";
1
+
import { createEffect, createSignal, onCleanup, onMount } from "solid-js";
2
2
import "./App.css";
3
3
import { renderBackgroundGrid, renderContextMenu, renderNodes, renderNullTab, renderTempDrawing } from "./renderer";
4
4
import { lerp } from "./utils/lerp";
···
10
10
import { ContextMenu } from "./structs/ContextMenu";
11
11
import { NodeManager } from "./Mangers/NodeManager";
12
12
import { TabMenu } from "./components/TabMenu";
13
+
import { ConfirmationPopup } from "./components/ConfirmationPopup";
13
14
14
15
import * as keybinds from './keybinds';
15
16
import { listen } from "@tauri-apps/api/event";
16
-
import { ConfirmationPopup } from "./components/ConfirmationPopup";
17
+
18
+
// TODO: Only allow one node to input on non-flow inputs
17
19
18
20
let App = () => {
19
-
let [ selectedNodes, setSelectedNodes ] = createSignal<Node[]>([]);
21
+
let [ selectedNodes, setSelectedNodes ] = createSignal<Node[]>([], { equals: false });
22
+
let [ mousePos, setMousePos ] = createSignal<[ number, number ]>([ 0, 0 ]);
20
23
21
24
let canvas!: HTMLCanvasElement;
22
25
let ctx: CanvasRenderingContext2D;
···
56
59
size: [ 0, 0 ],
57
60
visible: false
58
61
}
62
+
63
+
createEffect(() => {
64
+
let snodes = selectedNodes();
65
+
66
+
let anodes = NodeManager.Instance.GetNodes();
67
+
if(!anodes)return;
68
+
69
+
for(let node of anodes)node.selected = false;
70
+
for(let node of snodes)node.selected = true;
71
+
})
59
72
60
73
onMount(async () => {
61
74
NodeManager.Instance.HookTabChange(() => setSelectedNodes([]));
···
110
123
contextMenu.visible = true;
111
124
}
112
125
126
+
let isShiftClick = false;
113
127
canvas.onmousedown = ( e ) => {
128
+
isShiftClick = e.shiftKey;
129
+
114
130
if(
115
131
e.clientY < 60 ||
116
132
e.clientX < 220 ||
···
156
172
157
173
if(nodes){
158
174
nodes.map(node => {
159
-
if(!e.shiftKey)node.selected = false;
160
-
161
175
if(isPointInRectApplyOffset(canvas, { x: offset[0], y: offset[1], scale },
162
176
e.clientX, e.clientY,
163
177
node.x - 20, node.y, node.w + 40, node.h
···
216
230
217
231
movingNode = clickedNode;
218
232
219
-
if(!e.shiftKey){
220
-
if(clickedNode){
221
-
clickedNode.selected = true;
222
-
setSelectedNodes([ clickedNode ]);
223
-
} else{
224
-
setSelectedNodes([]);
225
-
}
226
-
} else{
227
-
clickedNode.selected = true;
228
-
229
-
let snodes = selectedNodes();
230
-
if(!snodes.find(x => x.id === clickedNode!.id)){
231
-
snodes.push(clickedNode);
232
-
clickedNode.selected = true;
233
-
234
-
setSelectedNodes(snodes);
235
-
}
236
-
}
237
-
238
233
isMouseDown = true;
239
234
mouseStartPos = [ e.clientX, e.clientY ];
235
+
mouseMovePos = [ e.clientX, e.clientY ];
240
236
}
241
237
242
238
canvas.onmousemove = ( e ) => {
239
+
setMousePos([ e.clientX, e.clientY ]);
240
+
243
241
if(e.shiftKey && isMouseDown){
244
242
let nodes = NodeManager.Instance.GetNodes();
245
243
let hoveredNode: Node | null = null;
···
262
260
snodes.push(hoveredNode);
263
261
264
262
// @ts-ignore
265
-
hoveredNode.selected = true;
263
+
hoveredNode.x = Math.round(hoveredNode.x / 10) * 10;
264
+
// @ts-ignore
265
+
hoveredNode.y = Math.round(hoveredNode.y / 10) * 10;
266
+
266
267
setSelectedNodes(snodes);
267
268
}
268
269
}
269
270
270
271
return;
271
-
}
272
+
} else if(isShiftClick)return;
272
273
273
274
if(isMouseDown){
274
275
if(isDrawing){
275
276
drawingTo = screenToWorldSpace(canvas, { x: offset[0], y: offset[1], scale }, e.clientX - 10 * scale, e.clientY - 10 * scale) as [ number, number ];
276
277
} else if(movingNode){
277
-
movingNode.x = movingNode.x - (mouseStartPos[0] - e.clientX) / scale;
278
-
movingNode.y = movingNode.y - (mouseStartPos[1] - e.clientY) / scale;
278
+
let nodes = selectedNodes();
279
279
280
-
mouseStartPos = [ e.clientX, e.clientY ];
280
+
for(let node of nodes){
281
+
node.x = node.x - (mouseMovePos[0] - e.clientX) / scale;
282
+
node.y = node.y - (mouseMovePos[1] - e.clientY) / scale;
283
+
}
284
+
285
+
mouseMovePos = [ e.clientX, e.clientY ];
281
286
NodeManager.Instance.UpdateConfig();
282
287
} else{
283
-
offsetTarget = [ offsetTarget[0] - (mouseStartPos[0] - e.clientX) / scale, offsetTarget[1] - (mouseStartPos[1] - e.clientY) / scale ];
284
-
mouseStartPos = [ e.clientX, e.clientY ];
288
+
offsetTarget = [ offsetTarget[0] - (mouseMovePos[0] - e.clientX) / scale, offsetTarget[1] - (mouseMovePos[1] - e.clientY) / scale ];
289
+
mouseMovePos = [ e.clientX, e.clientY ];
285
290
286
291
screenMoved = true;
287
292
}
···
325
330
326
331
canvas.onmouseup = ( e ) => {
327
332
let nodes = NodeManager.Instance.GetNodes();
333
+
let clickedNode;
328
334
329
335
if(nodes){
330
336
nodes.map(node => {
331
-
node.inputs.map(( input, i ) => {
332
-
if(isPointInRectApplyOffset(canvas, { x: offset[0], y: offset[1], scale },
333
-
e.clientX, e.clientY,
334
-
node.x - 10,
335
-
node.y + 50 + (30 * i),
336
-
20, 20
337
-
)){
338
-
if(isDrawing){
339
-
let fromType = NodeIOResolveAnyTypes(drawingFrom!);
340
-
let toType = NodeIOResolveAnyTypes(input);
337
+
if(isPointInRectApplyOffset(canvas, { x: offset[0], y: offset[1], scale },
338
+
e.clientX, e.clientY,
339
+
node.x - 20, node.y, node.w + 40, node.h
340
+
)){
341
+
clickedNode = node;
341
342
342
-
if(
343
-
drawingFrom!.connections.indexOf(input) === -1 &&
344
-
(
345
-
toType === null ||
346
-
NodeIOCanCast(fromType, toType)
347
-
)
348
-
){
349
-
drawingFrom!.connections.push(input);
350
-
input.connections.push(drawingFrom!);
343
+
node.inputs.map(( input, i ) => {
344
+
if(isPointInRectApplyOffset(canvas, { x: offset[0], y: offset[1], scale },
345
+
e.clientX, e.clientY,
346
+
node.x - 10,
347
+
node.y + 50 + (30 * i),
348
+
20, 20
349
+
)){
350
+
if(isDrawing){
351
+
let fromType = NodeIOResolveAnyTypes(drawingFrom!);
352
+
let toType = NodeIOResolveAnyTypes(input);
351
353
352
-
NodeManager.Instance.UpdateConfig();
354
+
if(
355
+
drawingFrom!.connections.indexOf(input) === -1 &&
356
+
(
357
+
toType === null ||
358
+
NodeIOCanCast(fromType, toType)
359
+
)
360
+
){
361
+
drawingFrom!.connections.push(input);
362
+
input.connections.push(drawingFrom!);
363
+
364
+
NodeManager.Instance.UpdateConfig();
365
+
}
353
366
}
354
367
}
355
-
}
356
-
})
368
+
})
369
+
}
357
370
})
358
371
}
359
372
373
+
let diffX = mouseStartPos[0] - e.clientX;
374
+
let diffY = mouseStartPos[1] - e.clientY;
375
+
376
+
let dist = Math.sqrt(diffX * diffX + diffY * diffY);
377
+
378
+
if(dist < 10){
379
+
if(clickedNode){
380
+
if(e.shiftKey){
381
+
let snodes = selectedNodes();
382
+
if(snodes.indexOf(clickedNode) === -1)snodes.push(clickedNode);
383
+
384
+
// @ts-ignore
385
+
clickedNode.x = Math.round(clickedNode.x / 10) * 10;
386
+
// @ts-ignore
387
+
clickedNode.y = Math.round(clickedNode.y / 10) * 10;
388
+
389
+
setSelectedNodes(snodes);
390
+
} else{
391
+
// @ts-ignore
392
+
clickedNode.x = Math.round(clickedNode.x / 10) * 10;
393
+
// @ts-ignore
394
+
clickedNode.y = Math.round(clickedNode.y / 10) * 10;
395
+
396
+
setSelectedNodes([ clickedNode ]);
397
+
}
398
+
} else {
399
+
setSelectedNodes([]);
400
+
}
401
+
}
402
+
360
403
isDrawing = false;
361
404
isMouseDown = false;
362
405
}
363
406
364
-
keybinds.load(selectedNodes, setSelectedNodes);
407
+
keybinds.load(canvas, mousePos, selectedNodes, setSelectedNodes);
365
408
requestAnimationFrame(update);
366
409
367
410
let unlisten_0 = await listen('hide-window', () => {
···
407
450
408
451
let isMouseDown = false;
409
452
let mouseStartPos = [ 0, 0 ];
453
+
let mouseMovePos = [ 0, 0 ];
410
454
411
455
let interval = setInterval(() => {
412
456
if(screenMoved){
+35
-2
src/Mangers/NodeManager.tsx
+35
-2
src/Mangers/NodeManager.tsx
···
7
7
import { NodesByID } from "../Nodes/Nodes";
8
8
import { save } from "@tauri-apps/plugin-dialog";
9
9
import { ConfirmationManager } from "./ConfirmationManager";
10
+
import { platform } from "@tauri-apps/plugin-os";
10
11
11
12
export interface TabHashMap {
12
13
[details: string] : Tab;
···
26
27
setInterval(() => {
27
28
let tabs = Object.values(this._tabs).filter(x => x.needSync);
28
29
for(let tab of tabs){
29
-
invoke('sync_tab', { graph: this._generateTabGraph(tab.id)[0], id: tab.id, name: tab.name, location: tab.saveLocation });
30
+
invoke('sync_tab', {
31
+
graph: this._generateTabGraph(tab.id)[0],
32
+
id: tab.id,
33
+
name: tab.name,
34
+
location: tab.saveLocation,
35
+
saveState: tab.needsSave()
36
+
});
37
+
30
38
tab.needSync = false;
31
39
}
32
40
}, 1000);
···
39
47
let version = await getVersion();
40
48
41
49
for(let tab of Object.entries<any>(tabs)){
42
-
await this._loadFromConfig(tab[1][2], tab[0], JSON.stringify({
50
+
let loaded_tab = await this._loadFromConfig(tab[1][2], tab[0], JSON.stringify({
43
51
tab_name: tab[1][1],
44
52
version,
45
53
graph: tab[1][0]
46
54
}));
55
+
56
+
if(loaded_tab)
57
+
loaded_tab.setNeedsSave(tab[1][3]);
47
58
};
59
+
60
+
this.UpdateConfig();
48
61
});
49
62
50
63
listen('prompt_to_close', async _ => {
···
333
346
334
347
tab.needSync = false;
335
348
if(!id)this.UpdateConfig(false);
349
+
350
+
return tab;
336
351
}
337
352
338
353
private _generateTabGraph( tabId: string | null ): [ any, Tab | null ]{
···
348
363
let node = tab.nodes[i];
349
364
350
365
let nodeOutputs = [];
366
+
let nodeInputs = [];
351
367
352
368
for (let j = 0; j < node.outputs.length; j++) {
353
369
let output = node.outputs[j];
···
364
380
})
365
381
}
366
382
383
+
for (let j = 0; j < node.inputs.length; j++) {
384
+
let input = node.inputs[j];
385
+
386
+
nodeInputs.push({
387
+
name: input.name,
388
+
type: input.type,
389
+
connections: input.connections.map(x => { return {
390
+
name: x.name,
391
+
node: x.parent.id,
392
+
index: x.index,
393
+
type: x.type
394
+
}})
395
+
})
396
+
}
397
+
367
398
nodesToSave.push({
368
399
name: node.name,
369
400
id: node.id,
370
401
typeId: node.typeId,
371
402
pos: [ node.x, node.y ],
372
403
outputs: nodeOutputs,
404
+
inputs: nodeInputs,
373
405
statics: node.statics
374
406
})
375
407
}
···
384
416
invoke('save_graph', { graph: JSON.stringify({
385
417
tab_name: tab.name,
386
418
version: await getVersion(),
419
+
platform: platform(),
387
420
graph: nodesToSave
388
421
}), path });
389
422
}
+2
-1
src/Nodes/Conditional/IfEqual.tsx
+2
-1
src/Nodes/Conditional/IfEqual.tsx
+2
-1
src/Nodes/Conditional/IfFalse.tsx
+2
-1
src/Nodes/Conditional/IfFalse.tsx
+2
-1
src/Nodes/Conditional/IfTrue.tsx
+2
-1
src/Nodes/Conditional/IfTrue.tsx
+2
src/Nodes/Conditional.tsx
+2
src/Nodes/Conditional.tsx
+2
-1
src/Nodes/Debug.tsx
+2
-1
src/Nodes/Debug.tsx
+24
-3
src/Nodes/Nodes.tsx
+24
-3
src/Nodes/Nodes.tsx
···
1
1
import { Node, NodeStatic, NodeType } from "../structs/node";
2
+
import { platform } from '@tauri-apps/plugin-os';
2
3
3
4
import { NodeConditional } from "./Conditional";
4
5
import { NodeDebug } from "./Debug";
5
6
import { NodeOSCActions } from "./OSCActions";
6
7
import { NodeOSCTrigger } from "./OSCTrigger";
8
+
import { NodePressKey } from "./PressKey";
7
9
import { NodeStatics } from "./Statics";
10
+
import { NodeShellCommand } from "./Shell";
8
11
9
12
export interface NodeDefinition{
13
+
os: string,
10
14
isSingle: boolean,
11
15
name: string,
12
16
typeId?: string,
13
17
onStaticsUpdate?: ( node: Node ) => Promise<void>,
14
18
// build?: ( pos: [ number, number ], onStaticsUpdate: ( node: Node ) => void ) => Promise<Node>,
15
19
w?: number,
16
-
h?: number,
17
20
statics?: NodeStatic[],
18
21
inputs?: { name: string, type: NodeType }[],
19
22
outputs?: { name: string, type: NodeType }[],
···
25
28
[details: string] : NodeDefinition;
26
29
}
27
30
28
-
export let Nodes: NodeDefinition[] = [
31
+
// TODO: (Node Additions) Pressing keyboard keys (like to do linux, but has extra steps)
32
+
// TODO: (Node Additions) Getting media state from os
33
+
// TODO: (Node Additions) Sending custom OSC messages
34
+
// TODO: (Node Additions) Sending HTTP requests?
35
+
// TODO: (Node Additions) Voicemeeter integrations (win only)
36
+
// TODO: (Node Additions) Voicemod integrations (win only)
37
+
// TODO: (Node Additions) Executing shell commands? (probably need some kinda popup warning when these are imported about dangerous usage)
38
+
39
+
export let Nodes: NodeDefinition[] = [];
40
+
let nodes = [
29
41
NodeOSCTrigger,
30
42
NodeConditional,
31
43
NodeStatics,
32
44
NodeOSCActions,
33
-
NodeDebug
45
+
NodeDebug,
46
+
NodePressKey,
47
+
NodeShellCommand
34
48
]
49
+
50
+
let os = platform();
51
+
52
+
for (let i = 0; i < nodes.length; i++) {
53
+
let node = nodes[i];
54
+
if(node.os === 'any' || node.os === os)Nodes.push(node);
55
+
}
35
56
36
57
export let NodesByID: NodeDefinitionHashMap = {}
37
58
+2
-1
src/Nodes/OSCActions/Send Chatbox.tsx
+2
-1
src/Nodes/OSCActions/Send Chatbox.tsx
+2
src/Nodes/OSCActions.tsx
+2
src/Nodes/OSCActions.tsx
+27
-10
src/Nodes/OSCTrigger.tsx
+27
-10
src/Nodes/OSCTrigger.tsx
···
5
5
import { NodeDefinition } from "./Nodes";
6
6
7
7
export let NodeOSCTrigger: NodeDefinition = {
8
+
os: 'any',
9
+
8
10
isSingle: true,
9
11
name: 'OSC Trigger',
10
12
typeId: 'osctrigger',
11
13
12
14
w: 200,
13
-
h: 55,
14
15
15
16
statics: [
16
17
{
···
39
40
}
40
41
}
41
42
42
-
node.outputs.map(output => {
43
-
output.connections.map(partner => {
44
-
partner.connections = partner.connections.filter(x => x != output);
45
-
})
46
-
})
47
-
node.outputs = [];
43
+
let tempOutputs = [];
48
44
49
-
node.outputs.push({
45
+
tempOutputs.push({
50
46
name: 'Flow',
51
47
type: NodeType.Flow,
52
48
connections: [],
···
73
69
}
74
70
75
71
if(type){
76
-
node.outputs.push({
72
+
tempOutputs.push({
77
73
name: dat.desc === '' ? dat.type : dat.desc,
78
74
type: type,
79
75
connections: [],
···
83
79
}
84
80
});
85
81
86
-
node.h = 65 + (parameters.length + 1) * 30;
82
+
let hasChanged = false;
83
+
84
+
for(let i in tempOutputs){
85
+
if(
86
+
node.outputs[i] === undefined ||
87
+
tempOutputs[i].type != node.outputs[i].type
88
+
){
89
+
hasChanged = true;
90
+
}
91
+
}
92
+
93
+
if(hasChanged){
94
+
node.outputs.map(output => {
95
+
output.connections.map(partner => {
96
+
partner.connections = partner.connections.filter(x => x != output);
97
+
})
98
+
})
99
+
100
+
node.outputs = tempOutputs;
101
+
node.updateSize();
102
+
}
103
+
87
104
NodeManager.Instance.UpdateConfig();
88
105
}
89
106
};
+29
src/Nodes/PressKey.tsx
+29
src/Nodes/PressKey.tsx
···
1
+
import { Node, NodeType } from "../structs/node";
2
+
import { NodeDefinition } from "./Nodes";
3
+
4
+
export let NodePressKey: NodeDefinition = {
5
+
os: 'windows',
6
+
7
+
isSingle: true,
8
+
name: 'Press Key',
9
+
typeId: 'presskey',
10
+
11
+
w: 200,
12
+
13
+
statics: [
14
+
{
15
+
name: "Key",
16
+
type: NodeType.String,
17
+
value: ""
18
+
}
19
+
],
20
+
21
+
inputs: [
22
+
{
23
+
name: "Flow",
24
+
type: NodeType.Flow,
25
+
}
26
+
],
27
+
28
+
onStaticsUpdate: async ( _node: Node ) => {}
29
+
};
+38
src/Nodes/Shell.tsx
+38
src/Nodes/Shell.tsx
···
1
+
import { Node, NodeType } from "../structs/node";
2
+
import { NodeDefinition } from "./Nodes";
3
+
4
+
export let NodeShellCommand: NodeDefinition = {
5
+
os: 'any',
6
+
7
+
isSingle: true,
8
+
name: 'Shell Command',
9
+
typeId: 'shellcommand',
10
+
11
+
w: 200,
12
+
13
+
statics: [],
14
+
15
+
inputs: [
16
+
{
17
+
name: "Flow",
18
+
type: NodeType.Flow,
19
+
},
20
+
{
21
+
name: "Command",
22
+
type: NodeType.String,
23
+
},
24
+
],
25
+
26
+
outputs: [
27
+
{
28
+
name: "Flow",
29
+
type: NodeType.Flow,
30
+
},
31
+
{
32
+
name: "Output",
33
+
type: NodeType.String,
34
+
},
35
+
],
36
+
37
+
onStaticsUpdate: async ( _node: Node ) => {}
38
+
};
+2
-1
src/Nodes/Statics/Float.tsx
+2
-1
src/Nodes/Statics/Float.tsx
+2
-1
src/Nodes/Statics/Int.tsx
+2
-1
src/Nodes/Statics/Int.tsx
+2
-1
src/Nodes/Statics/String.tsx
+2
-1
src/Nodes/Statics/String.tsx
+2
src/Nodes/Statics.tsx
+2
src/Nodes/Statics.tsx
+1
-5
src/components/ControlBar.tsx
+1
-5
src/components/ControlBar.tsx
···
1
1
import './ControlBar.css';
2
2
3
-
import { Accessor, createEffect, createSignal, For, Match, Show, Switch } from 'solid-js';
3
+
import { Accessor, createSignal, For, Match, Show, Switch } from 'solid-js';
4
4
import { Node, NodeType } from '../structs/node';
5
5
import { TextInput } from './TextInput';
6
6
import { invoke } from '@tauri-apps/api/core';
···
14
14
}
15
15
16
16
export let ControlBar = ( props: ControlBarProps ) => {
17
-
createEffect(() => {
18
-
console.log(props.node());
19
-
})
20
-
21
17
return (
22
18
<div class="control-bar">
23
19
<For each={props.node()[0]?.statics}>
+8
-8
src/components/SettingsMenu.css
+8
-8
src/components/SettingsMenu.css
···
1
1
.settings-menu{
2
-
position: fixed;
3
-
z-index: 110;
4
-
top: 0;
5
-
left: 0;
6
-
width: 100vw;
7
-
height: 100vh;
8
-
background: rgba(0, 0, 0, 0.75);
9
-
}
2
+
position: fixed;
3
+
z-index: 110;
4
+
top: 0;
5
+
left: 0;
6
+
width: 100vw;
7
+
height: 100vh;
8
+
background: rgba(0, 0, 0, 0.75);
9
+
}
10
10
11
11
.settings-menu-inner{
12
12
position: fixed;
+3
src/components/SettingsMenu.tsx
+3
src/components/SettingsMenu.tsx
+40
-7
src/keybinds.ts
+40
-7
src/keybinds.ts
···
1
1
import { Accessor, Setter } from "solid-js";
2
2
import { NodeManager } from "./Mangers/NodeManager";
3
3
import { Node } from "./structs/node";
4
+
import { readText, writeText } from "@tauri-apps/plugin-clipboard-manager";
5
+
import { decodeNodeList, encodeNodeList } from "./utils/clipboard";
4
6
5
7
let isKeyDown: any = {};
6
8
7
-
export let load = ( selectedNode: Accessor<Node[]>, setSelectedNode: Setter<Node[]> ) => {
8
-
// TODO: Copy / paste
9
+
export let load = ( canvas: HTMLCanvasElement, mousePos: Accessor<[ number, number ]>, selectedNode: Accessor<Node[]>, setSelectedNode: Setter<Node[]> ) => {
9
10
// TODO: Add undo / redo -ing
10
11
11
-
window.onkeydown = ( e ) => {
12
-
isKeyDown[e.key] = true;
13
-
14
-
console.log(e.key);
15
-
12
+
canvas.onkeydown = async ( e ) => {
16
13
switch(e.key){
17
14
case 'Delete':
18
15
let nodes = selectedNode();
···
34
31
35
32
setSelectedNode([]);
36
33
break;
34
+
}
35
+
}
36
+
37
+
window.onkeydown = async ( e ) => {
38
+
isKeyDown[e.key] = true;
39
+
40
+
switch(e.key){
37
41
case 's':
38
42
if(e.ctrlKey){
39
43
let currentTab = NodeManager.Instance.CurrentTab();
···
50
54
51
55
// Save
52
56
NodeManager.Instance.SaveTab(currentTab, true);
57
+
}
58
+
break;
59
+
case 'c':
60
+
if(e.ctrlKey){
61
+
let nodes = selectedNode();
62
+
await writeText(encodeNodeList(nodes, mousePos()));
63
+
}
64
+
break;
65
+
case 'v':
66
+
if(e.ctrlKey){
67
+
let text = await readText();
68
+
69
+
let nodes = await decodeNodeList(text, mousePos());
70
+
if(!nodes)return;
71
+
72
+
for(let node of nodes)
73
+
NodeManager.Instance.AddNode(node);
74
+
75
+
setSelectedNode(nodes);
76
+
}
77
+
break;
78
+
case 'z':
79
+
if(e.ctrlKey){
80
+
console.log('undo');
81
+
}
82
+
break;
83
+
case 'y':
84
+
if(e.ctrlKey){
85
+
console.log('redo');
53
86
}
54
87
break;
55
88
}
+7
-2
src/structs/node.ts
+7
-2
src/structs/node.ts
···
20
20
this.typeId = node.typeId!;
21
21
this.x = pos[0];
22
22
this.y = pos[1];
23
-
this.w = node.w!;
24
-
this.h = node.h!;
25
23
26
24
this.inputs = node.inputs ? node.inputs.map(( x, indx ) => {
27
25
return {
···
43
41
}
44
42
}) : [];
45
43
44
+
this.w = node.w || 200;
45
+
this.h = 50 + Math.max(this.outputs.length, this.inputs.length) * 30;
46
+
46
47
this.selected = false;
47
48
this.statics = node.statics!,
48
49
this.onStaticsUpdate = node.onStaticsUpdate!;
50
+
}
51
+
52
+
updateSize(){
53
+
this.h = 50 + Math.max(this.outputs.length, this.inputs.length) * 30;
49
54
}
50
55
}
51
56
+80
src/utils/clipboard.ts
+80
src/utils/clipboard.ts
···
1
+
import { NodeManager } from "../Mangers/NodeManager";
2
+
import { NodesByID } from "../Nodes/Nodes";
3
+
import { Node } from "../structs/node";
4
+
5
+
export let encodeNodeList = ( selectedNodes: Node[], mousePos: [ number, number ] ): string => {
6
+
let arr: any[] = [];
7
+
8
+
for(let node of selectedNodes){
9
+
arr.push({
10
+
id: node.id,
11
+
type_id: node.typeId,
12
+
statics: node.statics,
13
+
x: node.x - mousePos[0],
14
+
y: node.y - mousePos[1],
15
+
outputs: node.outputs.map(x => {
16
+
return x.connections.map(x => {
17
+
return { node: x.parent.id, index: x.index } }) })
18
+
})
19
+
}
20
+
21
+
for(let node of arr){
22
+
for(let output of node.outputs){
23
+
for(let i in output){
24
+
let indx = arr.findIndex(x => x.id === output[i].node);
25
+
if(indx === -1)
26
+
delete output[i];
27
+
else
28
+
output[i].node = indx;
29
+
}
30
+
}
31
+
}
32
+
33
+
for(let node of arr)delete node.id;
34
+
35
+
console.log(arr);
36
+
return 'VRCMACRO' + btoa(JSON.stringify(arr));
37
+
}
38
+
39
+
export let decodeNodeList = async ( text: string, mousePos: [ number, number ] ): Promise<Node[] | null> => {
40
+
if(!text.startsWith("VRCMACRO"))return null;
41
+
42
+
let data = text.slice(8);
43
+
let json = JSON.parse(atob(data));
44
+
45
+
let nodes: Node[] = [];
46
+
for(let node of json){
47
+
let n = new Node(
48
+
[ node.x + mousePos[0] + 10, node.y + mousePos[1] + 10 ],
49
+
NodesByID[node.type_id],
50
+
await NodeManager.Instance.GetNewNodeId()
51
+
);
52
+
53
+
n.statics = node.statics;
54
+
await n.onStaticsUpdate(n);
55
+
56
+
nodes.push(n);
57
+
}
58
+
59
+
for(let i in nodes){
60
+
let outputs: { node: number, index: number }[][] = json[i].outputs;
61
+
let node = nodes[i];
62
+
63
+
for(let j in outputs){
64
+
let output = node.outputs[j];
65
+
66
+
for(let k in outputs[j]){
67
+
let connection = outputs[j][k];
68
+
if(!connection)continue;
69
+
70
+
let peerNode = nodes[connection.node];
71
+
let input = peerNode.inputs[connection.index];
72
+
73
+
output.connections.push(input);
74
+
input.connections.push(output);
75
+
}
76
+
}
77
+
}
78
+
79
+
return nodes;
80
+
}
+435
-10
src-tauri/Cargo.lock
+435
-10
src-tauri/Cargo.lock
···
7
7
version = "0.1.0"
8
8
dependencies = [
9
9
"anyhow",
10
+
"chrono",
10
11
"crossbeam-channel",
11
12
"dirs",
13
+
"enigo",
12
14
"flate2",
13
15
"serde",
14
16
"serde_json",
15
17
"tauri",
16
18
"tauri-build",
19
+
"tauri-plugin-clipboard-manager",
17
20
"tauri-plugin-dialog",
18
21
"tauri-plugin-opener",
22
+
"tauri-plugin-os",
19
23
"tokio",
20
24
]
21
25
···
65
69
checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61"
66
70
67
71
[[package]]
72
+
name = "arboard"
73
+
version = "3.6.1"
74
+
source = "registry+https://github.com/rust-lang/crates.io-index"
75
+
checksum = "0348a1c054491f4bfe6ab86a7b6ab1e44e45d899005de92f58b3df180b36ddaf"
76
+
dependencies = [
77
+
"clipboard-win",
78
+
"image",
79
+
"log",
80
+
"objc2 0.6.3",
81
+
"objc2-app-kit",
82
+
"objc2-core-foundation",
83
+
"objc2-core-graphics",
84
+
"objc2-foundation 0.3.2",
85
+
"parking_lot",
86
+
"percent-encoding",
87
+
"windows-sys 0.60.2",
88
+
"wl-clipboard-rs",
89
+
"x11rb",
90
+
]
91
+
92
+
[[package]]
68
93
name = "ashpd"
69
94
version = "0.11.0"
70
95
source = "registry+https://github.com/rust-lang/crates.io-index"
···
136
161
"futures-lite",
137
162
"parking",
138
163
"polling",
139
-
"rustix",
164
+
"rustix 1.1.2",
140
165
"slab",
141
166
"windows-sys 0.61.2",
142
167
]
···
167
192
"cfg-if",
168
193
"event-listener",
169
194
"futures-lite",
170
-
"rustix",
195
+
"rustix 1.1.2",
171
196
]
172
197
173
198
[[package]]
···
193
218
"cfg-if",
194
219
"futures-core",
195
220
"futures-io",
196
-
"rustix",
221
+
"rustix 1.1.2",
197
222
"signal-hook-registry",
198
223
"slab",
199
224
"windows-sys 0.61.2",
···
495
520
checksum = "145052bdd345b87320e369255277e3fb5152762ad123a901ef5c262dd38fe8d2"
496
521
dependencies = [
497
522
"iana-time-zone",
523
+
"js-sys",
498
524
"num-traits",
499
525
"serde",
526
+
"wasm-bindgen",
500
527
"windows-link 0.2.1",
528
+
]
529
+
530
+
[[package]]
531
+
name = "clipboard-win"
532
+
version = "5.4.1"
533
+
source = "registry+https://github.com/rust-lang/crates.io-index"
534
+
checksum = "bde03770d3df201d4fb868f2c9c59e66a3e4e2bd06692a0fe701e7103c7e84d4"
535
+
dependencies = [
536
+
"error-code",
501
537
]
502
538
503
539
[[package]]
···
565
601
]
566
602
567
603
[[package]]
604
+
name = "core-graphics"
605
+
version = "0.25.0"
606
+
source = "registry+https://github.com/rust-lang/crates.io-index"
607
+
checksum = "064badf302c3194842cf2c5d61f56cc88e54a759313879cdf03abdd27d0c3b97"
608
+
dependencies = [
609
+
"bitflags 2.10.0",
610
+
"core-foundation",
611
+
"core-graphics-types",
612
+
"foreign-types",
613
+
"libc",
614
+
]
615
+
616
+
[[package]]
568
617
name = "core-graphics-types"
569
618
version = "0.2.0"
570
619
source = "registry+https://github.com/rust-lang/crates.io-index"
···
609
658
checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
610
659
611
660
[[package]]
661
+
name = "crunchy"
662
+
version = "0.2.4"
663
+
source = "registry+https://github.com/rust-lang/crates.io-index"
664
+
checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5"
665
+
666
+
[[package]]
612
667
name = "crypto-common"
613
668
version = "0.1.6"
614
669
source = "registry+https://github.com/rust-lang/crates.io-index"
···
874
929
checksum = "a3d8a32ae18130a3c84dd492d4215c3d913c3b07c6b63c2eb3eb7ff1101ab7bf"
875
930
876
931
[[package]]
932
+
name = "enigo"
933
+
version = "0.6.1"
934
+
source = "registry+https://github.com/rust-lang/crates.io-index"
935
+
checksum = "71c6c56e50f7acae2906a0dcbb34529ca647e40421119ad5d12e7f8ba6e50010"
936
+
dependencies = [
937
+
"core-foundation",
938
+
"core-graphics 0.25.0",
939
+
"foreign-types-shared",
940
+
"libc",
941
+
"log",
942
+
"nom 8.0.0",
943
+
"objc2 0.6.3",
944
+
"objc2-app-kit",
945
+
"objc2-foundation 0.3.2",
946
+
"windows",
947
+
"x11rb",
948
+
"xkbcommon",
949
+
"xkeysym",
950
+
]
951
+
952
+
[[package]]
877
953
name = "enumflags2"
878
954
version = "0.7.12"
879
955
source = "registry+https://github.com/rust-lang/crates.io-index"
···
922
998
]
923
999
924
1000
[[package]]
1001
+
name = "error-code"
1002
+
version = "3.3.2"
1003
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1004
+
checksum = "dea2df4cf52843e0452895c455a1a2cfbb842a1e7329671acf418fdc53ed4c59"
1005
+
1006
+
[[package]]
925
1007
name = "event-listener"
926
1008
version = "5.4.1"
927
1009
source = "registry+https://github.com/rust-lang/crates.io-index"
···
949
1031
checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
950
1032
951
1033
[[package]]
1034
+
name = "fax"
1035
+
version = "0.2.6"
1036
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1037
+
checksum = "f05de7d48f37cd6730705cbca900770cab77a89f413d23e100ad7fad7795a0ab"
1038
+
dependencies = [
1039
+
"fax_derive",
1040
+
]
1041
+
1042
+
[[package]]
1043
+
name = "fax_derive"
1044
+
version = "0.2.0"
1045
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1046
+
checksum = "a0aca10fb742cb43f9e7bb8467c91aa9bcb8e3ffbc6a6f7389bb93ffc920577d"
1047
+
dependencies = [
1048
+
"proc-macro2",
1049
+
"quote",
1050
+
"syn 2.0.109",
1051
+
]
1052
+
1053
+
[[package]]
952
1054
name = "fdeflate"
953
1055
version = "0.3.7"
954
1056
source = "registry+https://github.com/rust-lang/crates.io-index"
···
974
1076
checksum = "52051878f80a721bb68ebfbc930e07b65ba72f2da88968ea5c06fd6ca3d3a127"
975
1077
976
1078
[[package]]
1079
+
name = "fixedbitset"
1080
+
version = "0.5.7"
1081
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1082
+
checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99"
1083
+
1084
+
[[package]]
977
1085
name = "flate2"
978
1086
version = "1.1.5"
979
1087
source = "registry+https://github.com/rust-lang/crates.io-index"
···
988
1096
version = "1.0.7"
989
1097
source = "registry+https://github.com/rust-lang/crates.io-index"
990
1098
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
1099
+
1100
+
[[package]]
1101
+
name = "foldhash"
1102
+
version = "0.1.5"
1103
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1104
+
checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
991
1105
992
1106
[[package]]
993
1107
name = "foreign-types"
···
1239
1353
]
1240
1354
1241
1355
[[package]]
1356
+
name = "gethostname"
1357
+
version = "1.1.0"
1358
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1359
+
checksum = "1bd49230192a3797a9a4d6abe9b3eed6f7fa4c8a8a4947977c6f80025f92cbd8"
1360
+
dependencies = [
1361
+
"rustix 1.1.2",
1362
+
"windows-link 0.2.1",
1363
+
]
1364
+
1365
+
[[package]]
1242
1366
name = "getrandom"
1243
1367
version = "0.1.16"
1244
1368
source = "registry+https://github.com/rust-lang/crates.io-index"
···
1421
1545
]
1422
1546
1423
1547
[[package]]
1548
+
name = "half"
1549
+
version = "2.7.1"
1550
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1551
+
checksum = "6ea2d84b969582b4b1864a92dc5d27cd2b77b622a8d79306834f1be5ba20d84b"
1552
+
dependencies = [
1553
+
"cfg-if",
1554
+
"crunchy",
1555
+
"zerocopy",
1556
+
]
1557
+
1558
+
[[package]]
1424
1559
name = "hashbrown"
1425
1560
version = "0.12.3"
1426
1561
source = "registry+https://github.com/rust-lang/crates.io-index"
1427
1562
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
1563
+
1564
+
[[package]]
1565
+
name = "hashbrown"
1566
+
version = "0.15.5"
1567
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1568
+
checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1"
1569
+
dependencies = [
1570
+
"foldhash",
1571
+
]
1428
1572
1429
1573
[[package]]
1430
1574
name = "hashbrown"
···
1706
1850
"moxcms",
1707
1851
"num-traits",
1708
1852
"png 0.18.0",
1853
+
"tiff",
1709
1854
]
1710
1855
1711
1856
[[package]]
···
1949
2094
1950
2095
[[package]]
1951
2096
name = "linux-raw-sys"
2097
+
version = "0.4.15"
2098
+
source = "registry+https://github.com/rust-lang/crates.io-index"
2099
+
checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab"
2100
+
2101
+
[[package]]
2102
+
name = "linux-raw-sys"
1952
2103
version = "0.11.0"
1953
2104
source = "registry+https://github.com/rust-lang/crates.io-index"
1954
2105
checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039"
···
2018
2169
checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273"
2019
2170
2020
2171
[[package]]
2172
+
name = "memmap2"
2173
+
version = "0.9.9"
2174
+
source = "registry+https://github.com/rust-lang/crates.io-index"
2175
+
checksum = "744133e4a0e0a658e1374cf3bf8e415c4052a15a111acd372764c55b4177d490"
2176
+
dependencies = [
2177
+
"libc",
2178
+
]
2179
+
2180
+
[[package]]
2021
2181
name = "memoffset"
2022
2182
version = "0.9.1"
2023
2183
source = "registry+https://github.com/rust-lang/crates.io-index"
···
2033
2193
checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
2034
2194
2035
2195
[[package]]
2196
+
name = "minimal-lexical"
2197
+
version = "0.2.1"
2198
+
source = "registry+https://github.com/rust-lang/crates.io-index"
2199
+
checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
2200
+
2201
+
[[package]]
2036
2202
name = "miniz_oxide"
2037
2203
version = "0.8.9"
2038
2204
source = "registry+https://github.com/rust-lang/crates.io-index"
···
2138
2304
version = "0.1.14"
2139
2305
source = "registry+https://github.com/rust-lang/crates.io-index"
2140
2306
checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb"
2307
+
2308
+
[[package]]
2309
+
name = "nom"
2310
+
version = "7.1.3"
2311
+
source = "registry+https://github.com/rust-lang/crates.io-index"
2312
+
checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a"
2313
+
dependencies = [
2314
+
"memchr",
2315
+
"minimal-lexical",
2316
+
]
2317
+
2318
+
[[package]]
2319
+
name = "nom"
2320
+
version = "8.0.0"
2321
+
source = "registry+https://github.com/rust-lang/crates.io-index"
2322
+
checksum = "df9761775871bdef83bee530e60050f7e54b1105350d6884eb0fb4f46c2f9405"
2323
+
dependencies = [
2324
+
"memchr",
2325
+
]
2141
2326
2142
2327
[[package]]
2143
2328
name = "num-conv"
···
2280
2465
]
2281
2466
2282
2467
[[package]]
2468
+
name = "objc2-core-location"
2469
+
version = "0.3.2"
2470
+
source = "registry+https://github.com/rust-lang/crates.io-index"
2471
+
checksum = "ca347214e24bc973fc025fd0d36ebb179ff30536ed1f80252706db19ee452009"
2472
+
dependencies = [
2473
+
"objc2 0.6.3",
2474
+
"objc2-foundation 0.3.2",
2475
+
]
2476
+
2477
+
[[package]]
2283
2478
name = "objc2-core-text"
2284
2479
version = "0.3.2"
2285
2480
source = "registry+https://github.com/rust-lang/crates.io-index"
···
2398
2593
dependencies = [
2399
2594
"bitflags 2.10.0",
2400
2595
"objc2 0.6.3",
2596
+
"objc2-core-foundation",
2401
2597
"objc2-foundation 0.3.2",
2402
2598
]
2403
2599
···
2419
2615
checksum = "d87d638e33c06f577498cbcc50491496a3ed4246998a7fbba7ccb98b1e7eab22"
2420
2616
dependencies = [
2421
2617
"bitflags 2.10.0",
2618
+
"block2 0.6.2",
2422
2619
"objc2 0.6.3",
2620
+
"objc2-cloud-kit",
2621
+
"objc2-core-data",
2423
2622
"objc2-core-foundation",
2623
+
"objc2-core-graphics",
2624
+
"objc2-core-image",
2625
+
"objc2-core-location",
2626
+
"objc2-core-text",
2627
+
"objc2-foundation 0.3.2",
2628
+
"objc2-quartz-core 0.3.2",
2629
+
"objc2-user-notifications",
2630
+
]
2631
+
2632
+
[[package]]
2633
+
name = "objc2-user-notifications"
2634
+
version = "0.3.2"
2635
+
source = "registry+https://github.com/rust-lang/crates.io-index"
2636
+
checksum = "9df9128cbbfef73cda168416ccf7f837b62737d748333bfe9ab71c245d76613e"
2637
+
dependencies = [
2638
+
"objc2 0.6.3",
2424
2639
"objc2-foundation 0.3.2",
2425
2640
]
2426
2641
···
2475
2690
]
2476
2691
2477
2692
[[package]]
2693
+
name = "os_info"
2694
+
version = "3.13.0"
2695
+
source = "registry+https://github.com/rust-lang/crates.io-index"
2696
+
checksum = "7c39b5918402d564846d5aba164c09a66cc88d232179dfd3e3c619a25a268392"
2697
+
dependencies = [
2698
+
"android_system_properties",
2699
+
"log",
2700
+
"nix",
2701
+
"objc2 0.6.3",
2702
+
"objc2-foundation 0.3.2",
2703
+
"objc2-ui-kit",
2704
+
"serde",
2705
+
"windows-sys 0.61.2",
2706
+
]
2707
+
2708
+
[[package]]
2709
+
name = "os_pipe"
2710
+
version = "1.2.3"
2711
+
source = "registry+https://github.com/rust-lang/crates.io-index"
2712
+
checksum = "7d8fae84b431384b68627d0f9b3b1245fcf9f46f6c0e3dc902e9dce64edd1967"
2713
+
dependencies = [
2714
+
"libc",
2715
+
"windows-sys 0.61.2",
2716
+
]
2717
+
2718
+
[[package]]
2478
2719
name = "pango"
2479
2720
version = "0.18.3"
2480
2721
source = "registry+https://github.com/rust-lang/crates.io-index"
···
2541
2782
checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220"
2542
2783
2543
2784
[[package]]
2785
+
name = "petgraph"
2786
+
version = "0.8.3"
2787
+
source = "registry+https://github.com/rust-lang/crates.io-index"
2788
+
checksum = "8701b58ea97060d5e5b155d383a69952a60943f0e6dfe30b04c287beb0b27455"
2789
+
dependencies = [
2790
+
"fixedbitset",
2791
+
"hashbrown 0.15.5",
2792
+
"indexmap 2.12.0",
2793
+
]
2794
+
2795
+
[[package]]
2544
2796
name = "phf"
2545
2797
version = "0.8.0"
2546
2798
source = "registry+https://github.com/rust-lang/crates.io-index"
···
2752
3004
"concurrent-queue",
2753
3005
"hermit-abi",
2754
3006
"pin-project-lite",
2755
-
"rustix",
3007
+
"rustix 1.1.2",
2756
3008
"windows-sys 0.61.2",
2757
3009
]
2758
3010
···
2864
3116
]
2865
3117
2866
3118
[[package]]
3119
+
name = "quick-error"
3120
+
version = "2.0.1"
3121
+
source = "registry+https://github.com/rust-lang/crates.io-index"
3122
+
checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3"
3123
+
3124
+
[[package]]
2867
3125
name = "quick-xml"
2868
3126
version = "0.37.5"
2869
3127
source = "registry+https://github.com/rust-lang/crates.io-index"
···
3148
3406
checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92"
3149
3407
dependencies = [
3150
3408
"semver",
3409
+
]
3410
+
3411
+
[[package]]
3412
+
name = "rustix"
3413
+
version = "0.38.44"
3414
+
source = "registry+https://github.com/rust-lang/crates.io-index"
3415
+
checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154"
3416
+
dependencies = [
3417
+
"bitflags 2.10.0",
3418
+
"errno",
3419
+
"libc",
3420
+
"linux-raw-sys 0.4.15",
3421
+
"windows-sys 0.59.0",
3151
3422
]
3152
3423
3153
3424
[[package]]
···
3159
3430
"bitflags 2.10.0",
3160
3431
"errno",
3161
3432
"libc",
3162
-
"linux-raw-sys",
3433
+
"linux-raw-sys 0.11.0",
3163
3434
"windows-sys 0.61.2",
3164
3435
]
3165
3436
···
3519
3790
dependencies = [
3520
3791
"bytemuck",
3521
3792
"cfg_aliases",
3522
-
"core-graphics",
3793
+
"core-graphics 0.24.0",
3523
3794
"foreign-types",
3524
3795
"js-sys",
3525
3796
"log",
···
3656
3927
]
3657
3928
3658
3929
[[package]]
3930
+
name = "sys-locale"
3931
+
version = "0.3.2"
3932
+
source = "registry+https://github.com/rust-lang/crates.io-index"
3933
+
checksum = "8eab9a99a024a169fe8a903cf9d4a3b3601109bcc13bd9e3c6fff259138626c4"
3934
+
dependencies = [
3935
+
"libc",
3936
+
]
3937
+
3938
+
[[package]]
3659
3939
name = "system-deps"
3660
3940
version = "6.2.2"
3661
3941
source = "registry+https://github.com/rust-lang/crates.io-index"
···
3677
3957
"bitflags 2.10.0",
3678
3958
"block2 0.6.2",
3679
3959
"core-foundation",
3680
-
"core-graphics",
3960
+
"core-graphics 0.24.0",
3681
3961
"crossbeam-channel",
3682
3962
"dispatch",
3683
3963
"dlopen2",
···
3858
4138
]
3859
4139
3860
4140
[[package]]
4141
+
name = "tauri-plugin-clipboard-manager"
4142
+
version = "2.3.2"
4143
+
source = "registry+https://github.com/rust-lang/crates.io-index"
4144
+
checksum = "206dc20af4ed210748ba945c2774e60fd0acd52b9a73a028402caf809e9b6ecf"
4145
+
dependencies = [
4146
+
"arboard",
4147
+
"log",
4148
+
"serde",
4149
+
"serde_json",
4150
+
"tauri",
4151
+
"tauri-plugin",
4152
+
"thiserror 2.0.17",
4153
+
]
4154
+
4155
+
[[package]]
3861
4156
name = "tauri-plugin-dialog"
3862
4157
version = "2.4.2"
3863
4158
source = "registry+https://github.com/rust-lang/crates.io-index"
···
3920
4215
]
3921
4216
3922
4217
[[package]]
4218
+
name = "tauri-plugin-os"
4219
+
version = "2.3.2"
4220
+
source = "registry+https://github.com/rust-lang/crates.io-index"
4221
+
checksum = "d8f08346c8deb39e96f86973da0e2d76cbb933d7ac9b750f6dc4daf955a6f997"
4222
+
dependencies = [
4223
+
"gethostname",
4224
+
"log",
4225
+
"os_info",
4226
+
"serde",
4227
+
"serde_json",
4228
+
"serialize-to-javascript",
4229
+
"sys-locale",
4230
+
"tauri",
4231
+
"tauri-plugin",
4232
+
"thiserror 2.0.17",
4233
+
]
4234
+
4235
+
[[package]]
3923
4236
name = "tauri-runtime"
3924
4237
version = "2.9.1"
3925
4238
source = "registry+https://github.com/rust-lang/crates.io-index"
···
4028
4341
"fastrand",
4029
4342
"getrandom 0.3.4",
4030
4343
"once_cell",
4031
-
"rustix",
4344
+
"rustix 1.1.2",
4032
4345
"windows-sys 0.61.2",
4033
4346
]
4034
4347
···
4084
4397
]
4085
4398
4086
4399
[[package]]
4400
+
name = "tiff"
4401
+
version = "0.10.3"
4402
+
source = "registry+https://github.com/rust-lang/crates.io-index"
4403
+
checksum = "af9605de7fee8d9551863fd692cce7637f548dbd9db9180fcc07ccc6d26c336f"
4404
+
dependencies = [
4405
+
"fax",
4406
+
"flate2",
4407
+
"half",
4408
+
"quick-error",
4409
+
"weezl",
4410
+
"zune-jpeg",
4411
+
]
4412
+
4413
+
[[package]]
4087
4414
name = "time"
4088
4415
version = "0.3.44"
4089
4416
source = "registry+https://github.com/rust-lang/crates.io-index"
···
4360
4687
]
4361
4688
4362
4689
[[package]]
4690
+
name = "tree_magic_mini"
4691
+
version = "3.2.1"
4692
+
source = "registry+https://github.com/rust-lang/crates.io-index"
4693
+
checksum = "52fac5f7d176f7f7f7e827299ead28ef98de642c5d93a97e0cd0816d17557f19"
4694
+
dependencies = [
4695
+
"memchr",
4696
+
"nom 7.1.3",
4697
+
"petgraph",
4698
+
]
4699
+
4700
+
[[package]]
4363
4701
name = "try-lock"
4364
4702
version = "0.2.5"
4365
4703
source = "registry+https://github.com/rust-lang/crates.io-index"
···
4640
4978
dependencies = [
4641
4979
"cc",
4642
4980
"downcast-rs",
4643
-
"rustix",
4981
+
"rustix 1.1.2",
4644
4982
"scoped-tls",
4645
4983
"smallvec",
4646
4984
"wayland-sys",
···
4653
4991
checksum = "c66a47e840dc20793f2264eb4b3e4ecb4b75d91c0dd4af04b456128e0bdd449d"
4654
4992
dependencies = [
4655
4993
"bitflags 2.10.0",
4656
-
"rustix",
4994
+
"rustix 1.1.2",
4657
4995
"wayland-backend",
4658
4996
"wayland-scanner",
4659
4997
]
···
4671
5009
]
4672
5010
4673
5011
[[package]]
5012
+
name = "wayland-protocols-wlr"
5013
+
version = "0.3.9"
5014
+
source = "registry+https://github.com/rust-lang/crates.io-index"
5015
+
checksum = "efd94963ed43cf9938a090ca4f7da58eb55325ec8200c3848963e98dc25b78ec"
5016
+
dependencies = [
5017
+
"bitflags 2.10.0",
5018
+
"wayland-backend",
5019
+
"wayland-client",
5020
+
"wayland-protocols",
5021
+
"wayland-scanner",
5022
+
]
5023
+
5024
+
[[package]]
4674
5025
name = "wayland-scanner"
4675
5026
version = "0.31.7"
4676
5027
source = "registry+https://github.com/rust-lang/crates.io-index"
···
4781
5132
"windows",
4782
5133
"windows-core 0.61.2",
4783
5134
]
5135
+
5136
+
[[package]]
5137
+
name = "weezl"
5138
+
version = "0.1.12"
5139
+
source = "registry+https://github.com/rust-lang/crates.io-index"
5140
+
checksum = "a28ac98ddc8b9274cb41bb4d9d4d5c425b6020c50c46f25559911905610b4a88"
4784
5141
4785
5142
[[package]]
4786
5143
name = "winapi"
···
5242
5599
checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59"
5243
5600
5244
5601
[[package]]
5602
+
name = "wl-clipboard-rs"
5603
+
version = "0.9.2"
5604
+
source = "registry+https://github.com/rust-lang/crates.io-index"
5605
+
checksum = "8e5ff8d0e60065f549fafd9d6cb626203ea64a798186c80d8e7df4f8af56baeb"
5606
+
dependencies = [
5607
+
"libc",
5608
+
"log",
5609
+
"os_pipe",
5610
+
"rustix 0.38.44",
5611
+
"tempfile",
5612
+
"thiserror 2.0.17",
5613
+
"tree_magic_mini",
5614
+
"wayland-backend",
5615
+
"wayland-client",
5616
+
"wayland-protocols",
5617
+
"wayland-protocols-wlr",
5618
+
]
5619
+
5620
+
[[package]]
5245
5621
name = "writeable"
5246
5622
version = "0.6.2"
5247
5623
source = "registry+https://github.com/rust-lang/crates.io-index"
···
5312
5688
"once_cell",
5313
5689
"pkg-config",
5314
5690
]
5691
+
5692
+
[[package]]
5693
+
name = "x11rb"
5694
+
version = "0.13.2"
5695
+
source = "registry+https://github.com/rust-lang/crates.io-index"
5696
+
checksum = "9993aa5be5a26815fe2c3eacfc1fde061fc1a1f094bf1ad2a18bf9c495dd7414"
5697
+
dependencies = [
5698
+
"gethostname",
5699
+
"rustix 1.1.2",
5700
+
"x11rb-protocol",
5701
+
]
5702
+
5703
+
[[package]]
5704
+
name = "x11rb-protocol"
5705
+
version = "0.13.2"
5706
+
source = "registry+https://github.com/rust-lang/crates.io-index"
5707
+
checksum = "ea6fc2961e4ef194dcbfe56bb845534d0dc8098940c7e5c012a258bfec6701bd"
5708
+
5709
+
[[package]]
5710
+
name = "xkbcommon"
5711
+
version = "0.9.0"
5712
+
source = "registry+https://github.com/rust-lang/crates.io-index"
5713
+
checksum = "a7a974f48060a14e95705c01f24ad9c3345022f4d97441b8a36beb7ed5c4a02d"
5714
+
dependencies = [
5715
+
"libc",
5716
+
"memmap2",
5717
+
"xkeysym",
5718
+
]
5719
+
5720
+
[[package]]
5721
+
name = "xkeysym"
5722
+
version = "0.2.1"
5723
+
source = "registry+https://github.com/rust-lang/crates.io-index"
5724
+
checksum = "b9cc00251562a284751c9973bace760d86c0276c471b4be569fe6b068ee97a56"
5315
5725
5316
5726
[[package]]
5317
5727
name = "yoke"
···
5470
5880
"proc-macro2",
5471
5881
"quote",
5472
5882
"syn 2.0.109",
5883
+
]
5884
+
5885
+
[[package]]
5886
+
name = "zune-core"
5887
+
version = "0.4.12"
5888
+
source = "registry+https://github.com/rust-lang/crates.io-index"
5889
+
checksum = "3f423a2c17029964870cfaabb1f13dfab7d092a62a29a89264f4d36990ca414a"
5890
+
5891
+
[[package]]
5892
+
name = "zune-jpeg"
5893
+
version = "0.4.21"
5894
+
source = "registry+https://github.com/rust-lang/crates.io-index"
5895
+
checksum = "29ce2c8a9384ad323cf564b67da86e21d3cfdff87908bc1223ed5c99bc792713"
5896
+
dependencies = [
5897
+
"zune-core",
5473
5898
]
5474
5899
5475
5900
[[package]]
+6
src-tauri/Cargo.toml
+6
src-tauri/Cargo.toml
+4
-1
src-tauri/capabilities/default.json
+4
-1
src-tauri/capabilities/default.json
+2
-2
src-tauri/src/frontend_calls/close_app.rs
+2
-2
src-tauri/src/frontend_calls/close_app.rs
+9
-4
src-tauri/src/frontend_calls/load_previous_tabs.rs
+9
-4
src-tauri/src/frontend_calls/load_previous_tabs.rs
···
2
2
3
3
use tauri::{State, Window};
4
4
5
-
use crate::{ structs::nodes::Node, utils::config::Config };
5
+
use crate::{structs::nodes::Node, utils::config::Config};
6
6
7
7
#[tauri::command]
8
-
pub fn load_previous_tabs( window: Window, conf: State<Config> ) -> HashMap<String, ( Vec<Node>, String, Option<String> )> {
8
+
pub fn load_previous_tabs(
9
+
window: Window,
10
+
conf: State<Config>,
11
+
) -> HashMap<String, (Vec<Node>, String, Option<String>, bool)> {
9
12
let config = conf.store.lock().unwrap();
10
13
11
-
if !config.hide_editor_on_start { window.show().unwrap(); }
14
+
if !config.hide_editor_on_start {
15
+
window.show().unwrap();
16
+
}
12
17
13
18
let tabs = config.loaded_tabs.clone();
14
19
tabs
15
-
}
20
+
}
+3
-3
src-tauri/src/frontend_calls/mod.rs
+3
-3
src-tauri/src/frontend_calls/mod.rs
+2
-2
src-tauri/src/frontend_calls/save_graph.rs
+2
-2
src-tauri/src/frontend_calls/save_graph.rs
···
1
1
use std::{fs::File, io::Write, path::PathBuf};
2
2
3
-
use flate2::{ write::GzEncoder, Compression };
3
+
use flate2::{write::GzEncoder, Compression};
4
4
use tauri::State;
5
5
6
6
use crate::utils::config::Config;
7
7
8
8
#[tauri::command]
9
-
pub fn save_graph( graph: String, path: PathBuf, conf: State<Config> ) {
9
+
pub fn save_graph(graph: String, path: PathBuf, conf: State<Config>) {
10
10
let file = File::create(&path).unwrap();
11
11
let mut encoder = GzEncoder::new(file, Compression::default());
12
12
+3
-3
src-tauri/src/frontend_calls/settings.rs
+3
-3
src-tauri/src/frontend_calls/settings.rs
···
3
3
use crate::utils::config::Config;
4
4
5
5
#[tauri::command]
6
-
pub fn set_hide_editor_on_app_start( value: bool, conf: State<Config> ){
6
+
pub fn set_hide_editor_on_app_start(value: bool, conf: State<Config>) {
7
7
let mut config = conf.store.lock().unwrap();
8
8
config.hide_editor_on_start = value;
9
9
}
10
10
11
11
#[tauri::command]
12
-
pub fn get_hide_editor_on_app_start( conf: State<Config> ) -> bool {
12
+
pub fn get_hide_editor_on_app_start(conf: State<Config>) -> bool {
13
13
let config = conf.store.lock().unwrap();
14
14
config.hide_editor_on_start
15
-
}
15
+
}
+19
-6
src-tauri/src/frontend_calls/sync_tab.rs
+19
-6
src-tauri/src/frontend_calls/sync_tab.rs
···
1
+
use chrono::Utc;
1
2
use crossbeam_channel::Sender;
2
3
3
4
use tauri::State;
4
5
5
-
use crate::{ runtime::commands::RuntimeCommand, structs::nodes::Node, utils::config::Config };
6
+
use crate::{runtime::commands::RuntimeCommand, structs::nodes::Node, utils::config::Config};
6
7
7
8
#[tauri::command]
8
-
pub fn sync_tab( graph: Vec<Node>, id: String, name: String, location: Option<String>, cmd: State<Sender<RuntimeCommand>>, conf: State<Config> ){
9
-
cmd.send(RuntimeCommand::AddTab(graph.clone(), id.clone())).unwrap();
9
+
pub fn sync_tab(
10
+
graph: Vec<Node>,
11
+
id: String,
12
+
name: String,
13
+
save_state: bool,
14
+
location: Option<String>,
15
+
cmd: State<Sender<RuntimeCommand>>,
16
+
conf: State<Config>,
17
+
) {
18
+
cmd
19
+
.send(RuntimeCommand::AddTab(graph.clone(), id.clone()))
20
+
.unwrap();
10
21
11
22
let mut config = conf.store.lock().unwrap();
12
-
config.loaded_tabs.insert(id, ( graph, name, location )); // TODO: When loading a tab into config, store the save state of it too
23
+
config.loaded_tabs.insert(id, (graph, name, location, save_state));
24
+
25
+
conf.save_prelocked(config);
13
26
}
14
27
15
28
#[tauri::command]
16
-
pub fn discard_tab( id: String, cmd: State<Sender<RuntimeCommand>>, conf: State<Config> ){
29
+
pub fn discard_tab(id: String, cmd: State<Sender<RuntimeCommand>>, conf: State<Config>) {
17
30
cmd.send(RuntimeCommand::RemoveTab(id.clone())).unwrap();
18
31
19
32
let mut config = conf.store.lock().unwrap();
20
33
config.loaded_tabs.remove(&id);
21
-
}
34
+
}
+10
-7
src-tauri/src/lib.rs
+10
-7
src-tauri/src/lib.rs
···
1
-
use std::{ fs, sync::Mutex };
1
+
use std::{fs, sync::Mutex};
2
2
3
3
use crossbeam_channel::bounded;
4
4
use frontend_calls::*;
5
5
6
-
use crate::{ osc::OSCMessage, setup::setup, utils::config::Config };
6
+
use crate::{osc::OSCMessage, setup::setup, utils::{config::Config, vrchat_builtin_parameters}};
7
7
8
8
mod frontend_calls;
9
9
mod osc;
10
+
mod runtime;
10
11
mod setup;
11
12
mod structs;
12
13
mod utils;
13
-
mod runtime;
14
-
15
-
// TODO: Add built-in OSC endpoints
16
14
17
15
#[cfg_attr(mobile, tauri::mobile_entry_point)]
18
16
#[tokio::main]
···
35
33
36
34
static ADDRESSES: Mutex<Vec<OSCMessage>> = Mutex::new(Vec::new());
37
35
38
-
let ( runtime_sender, runtime_receiver ) = bounded(1024);
36
+
let mut addresses = ADDRESSES.lock().unwrap();
37
+
addresses.append(&mut vrchat_builtin_parameters::get_read_addresses());
38
+
drop(addresses);
39
+
40
+
let (runtime_sender, runtime_receiver) = bounded(1024);
39
41
40
42
tauri::Builder::default()
43
+
.plugin(tauri_plugin_os::init())
44
+
.plugin(tauri_plugin_clipboard_manager::init())
41
45
.plugin(tauri_plugin_dialog::init())
42
46
.plugin(tauri_plugin_opener::init())
43
47
.invoke_handler(tauri::generate_handler![
···
47
51
sync_tab::discard_tab,
48
52
load_previous_tabs::load_previous_tabs,
49
53
close_app::close_app,
50
-
51
54
settings::set_hide_editor_on_app_start,
52
55
settings::get_hide_editor_on_app_start,
53
56
])
+3
-1
src-tauri/src/main.rs
+3
-1
src-tauri/src/main.rs
+4
-4
src-tauri/src/runtime/commands.rs
+4
-4
src-tauri/src/runtime/commands.rs
···
1
-
use crate::{ osc::OSCMessage, structs::nodes::Node };
1
+
use crate::{osc::OSCMessage, structs::nodes::Node};
2
2
3
3
#[derive(Debug)]
4
-
pub enum RuntimeCommand{
4
+
pub enum RuntimeCommand {
5
5
OSCMessage(OSCMessage),
6
6
7
7
AddTab(Vec<Node>, String),
8
-
RemoveTab(String)
9
-
}
8
+
RemoveTab(String),
9
+
}
+37
-36
src-tauri/src/runtime/nodes/conditional/ifequal.rs
+37
-36
src-tauri/src/runtime/nodes/conditional/ifequal.rs
···
1
-
use crate::{ runtime::nodes::RuntimeNode, structs::{ nodes::Node, parameter_types::ParameterType } };
1
+
use crate::{
2
+
runtime::nodes::RuntimeNode,
3
+
structs::{nodes::Node, parameter_types::ParameterType},
4
+
};
2
5
3
-
pub struct ConditionalIfEqual{
4
-
outputs: Vec<Vec<( String, isize, isize )>>,
5
-
value1: ParameterType,
6
-
value2: ParameterType
6
+
pub struct ConditionalIfEqual {
7
+
outputs: Vec<Vec<(String, isize, isize)>>,
8
+
inputs: Vec<Option<(String, isize, isize)>>,
7
9
}
8
10
9
-
impl ConditionalIfEqual{
10
-
pub fn new( node: Node ) -> Box<Self>{
11
+
impl ConditionalIfEqual {
12
+
pub fn new(node: Node) -> Box<Self> {
11
13
Box::new(Self {
12
-
outputs: node.outputs.iter()
13
-
.map(| x | {
14
-
x.connections.iter().map(| x | { ( x.node.clone(), x.index, x.value_type ) }).collect()
15
-
}).collect(),
16
-
value1: ParameterType::None,
17
-
value2: ParameterType::None,
14
+
outputs: node.outputs.iter().map(|x| {
15
+
x.connections.iter()
16
+
.map(|x| (x.node.clone(), x.index, x.value_type)).collect()}).collect(),
17
+
18
+
inputs: node.inputs.iter().map(|x| {
19
+
let y = x.connections.get(0);
20
+
if let Some(y) = y{
21
+
Some((y.node.clone(), y.index, y.value_type))
22
+
} else{
23
+
None
24
+
}
25
+
}).collect(),
18
26
})
19
27
}
20
28
}
21
29
22
-
impl RuntimeNode for ConditionalIfEqual{
23
-
fn outputs( &self ) -> Vec<Vec<( String, isize, isize )>> { self.outputs.clone() }
24
-
fn execute_dry( &mut self, _: &Vec<ParameterType> ) -> Option<Vec<ParameterType>> { Some(vec![]) }
30
+
impl RuntimeNode for ConditionalIfEqual {
31
+
fn outputs(&self) -> Vec<Vec<(String, isize, isize)>> {
32
+
self.outputs.clone()
33
+
}
25
34
26
-
fn execute( &mut self ) -> Option<Vec<ParameterType>> {
27
-
if self.value1 == ParameterType::None && self.value2 == ParameterType::None{
28
-
None
29
-
} else{
30
-
let equal = self.value1 == self.value2;
31
-
Some(vec![ ParameterType::Flow(equal), ParameterType::Flow(!equal) ])
32
-
}
35
+
fn inputs(&self) -> Vec<Option<(String, isize, isize)>> {
36
+
self.inputs.clone()
33
37
}
34
38
35
-
fn update_arg( &mut self, index: usize, arg: ParameterType ) -> bool {
36
-
match index{
37
-
1 => {
38
-
self.value1 = arg;
39
-
}
40
-
2 => {
41
-
self.value2 = arg;
42
-
}
43
-
_ => {}
44
-
}
39
+
fn execute(&mut self, args: Vec<ParameterType>) -> Vec<ParameterType> {
40
+
let is_equal = args[1] == args[2];
45
41
46
-
false
42
+
vec![
43
+
ParameterType::Flow(is_equal),
44
+
ParameterType::Flow(!is_equal),
45
+
]
47
46
}
48
47
49
-
fn is_entrypoint( &self ) -> bool { false }
50
-
}
48
+
fn is_entrypoint(&self) -> bool {
49
+
false
50
+
}
51
+
}
+38
-26
src-tauri/src/runtime/nodes/conditional/iffalse.rs
+38
-26
src-tauri/src/runtime/nodes/conditional/iffalse.rs
···
1
-
use crate::{ runtime::nodes::RuntimeNode, structs::{ nodes::Node, parameter_types::ParameterType } };
1
+
use crate::{
2
+
runtime::nodes::RuntimeNode,
3
+
structs::{nodes::Node, parameter_types::ParameterType},
4
+
};
2
5
3
-
pub struct ConditionalIfFalse{
4
-
outputs: Vec<Vec<( String, isize, isize )>>,
5
-
runtime_active: bool
6
+
pub struct ConditionalIfFalse {
7
+
outputs: Vec<Vec<(String, isize, isize)>>,
8
+
inputs: Vec<Option<(String, isize, isize)>>,
6
9
}
7
10
8
-
impl ConditionalIfFalse{
9
-
pub fn new( node: Node ) -> Box<Self>{
11
+
impl ConditionalIfFalse {
12
+
pub fn new(node: Node) -> Box<Self> {
10
13
Box::new(Self {
11
-
outputs: node.outputs.iter()
12
-
.map(| x | {
13
-
x.connections.iter().map(| x | { ( x.node.clone(), x.index, x.value_type ) }).collect()
14
-
}).collect(),
15
-
runtime_active: false
14
+
outputs: node.outputs.iter().map(|x| {
15
+
x.connections.iter()
16
+
.map(|x| (x.node.clone(), x.index, x.value_type)).collect()}).collect(),
17
+
18
+
inputs: node.inputs.iter().map(|x| {
19
+
let y = x.connections.get(0);
20
+
if let Some(y) = y{
21
+
Some((y.node.clone(), y.index, y.value_type))
22
+
} else{
23
+
None
24
+
}
25
+
}).collect(),
16
26
})
17
27
}
18
28
}
19
29
20
-
impl RuntimeNode for ConditionalIfFalse{
21
-
fn outputs( &self ) -> Vec<Vec<( String, isize, isize )>> { self.outputs.clone() }
22
-
fn execute_dry( &mut self, _: &Vec<ParameterType> ) -> Option<Vec<ParameterType>> { Some(vec![]) }
30
+
impl RuntimeNode for ConditionalIfFalse {
31
+
fn outputs(&self) -> Vec<Vec<(String, isize, isize)>> {
32
+
self.outputs.clone()
33
+
}
23
34
24
-
fn execute( &mut self ) -> Option<Vec<ParameterType>> {
25
-
Some(vec![ ParameterType::Flow(!self.runtime_active), ParameterType::Flow(self.runtime_active) ])
35
+
fn inputs(&self) -> Vec<Option<(String, isize, isize)>> {
36
+
self.inputs.clone()
26
37
}
27
38
28
-
fn update_arg( &mut self, _: usize, arg: ParameterType ) -> bool {
29
-
if arg.as_bool().unwrap(){
30
-
self.runtime_active = true;
31
-
true
32
-
} else{
33
-
self.runtime_active = false;
34
-
false
35
-
}
39
+
fn execute(&mut self, args: Vec<ParameterType>) -> Vec<ParameterType> {
40
+
let is_false = !args[1].as_bool().unwrap();
41
+
42
+
vec![
43
+
ParameterType::Flow(is_false),
44
+
ParameterType::Flow(!is_false),
45
+
]
36
46
}
37
47
38
-
fn is_entrypoint( &self ) -> bool { false }
39
-
}
48
+
fn is_entrypoint(&self) -> bool {
49
+
false
50
+
}
51
+
}
+38
-26
src-tauri/src/runtime/nodes/conditional/iftrue.rs
+38
-26
src-tauri/src/runtime/nodes/conditional/iftrue.rs
···
1
-
use crate::{ runtime::nodes::RuntimeNode, structs::{ nodes::Node, parameter_types::ParameterType } };
1
+
use crate::{
2
+
runtime::nodes::RuntimeNode,
3
+
structs::{nodes::Node, parameter_types::ParameterType},
4
+
};
2
5
3
-
pub struct ConditionalIfTrue{
4
-
outputs: Vec<Vec<( String, isize, isize )>>,
5
-
runtime_active: bool
6
+
pub struct ConditionalIfTrue {
7
+
outputs: Vec<Vec<(String, isize, isize)>>,
8
+
inputs: Vec<Option<(String, isize, isize)>>,
6
9
}
7
10
8
-
impl ConditionalIfTrue{
9
-
pub fn new( node: Node ) -> Box<Self>{
11
+
impl ConditionalIfTrue {
12
+
pub fn new(node: Node) -> Box<Self> {
10
13
Box::new(Self {
11
-
outputs: node.outputs.iter()
12
-
.map(| x | {
13
-
x.connections.iter().map(| x | { ( x.node.clone(), x.index, x.value_type ) }).collect()
14
-
}).collect(),
15
-
runtime_active: false
14
+
outputs: node.outputs.iter().map(|x| {
15
+
x.connections.iter()
16
+
.map(|x| (x.node.clone(), x.index, x.value_type)).collect()}).collect(),
17
+
18
+
inputs: node.inputs.iter().map(|x| {
19
+
let y = x.connections.get(0);
20
+
if let Some(y) = y{
21
+
Some((y.node.clone(), y.index, y.value_type))
22
+
} else{
23
+
None
24
+
}
25
+
}).collect(),
16
26
})
17
27
}
18
28
}
19
29
20
-
impl RuntimeNode for ConditionalIfTrue{
21
-
fn outputs( &self ) -> Vec<Vec<( String, isize, isize )>> { self.outputs.clone() }
22
-
fn execute_dry( &mut self, _: &Vec<ParameterType> ) -> Option<Vec<ParameterType>> { Some(vec![]) }
30
+
impl RuntimeNode for ConditionalIfTrue {
31
+
fn outputs(&self) -> Vec<Vec<(String, isize, isize)>> {
32
+
self.outputs.clone()
33
+
}
23
34
24
-
fn execute( &mut self ) -> Option<Vec<ParameterType>> {
25
-
Some(vec![ ParameterType::Flow(self.runtime_active), ParameterType::Flow(!self.runtime_active) ])
35
+
fn inputs(&self) -> Vec<Option<(String, isize, isize)>> {
36
+
self.inputs.clone()
26
37
}
27
38
28
-
fn update_arg( &mut self, _: usize, arg: ParameterType ) -> bool {
29
-
if arg.as_bool().unwrap(){
30
-
self.runtime_active = true;
31
-
true
32
-
} else{
33
-
self.runtime_active = false;
34
-
false
35
-
}
39
+
fn execute(&mut self, args: Vec<ParameterType>) -> Vec<ParameterType> {
40
+
let is_true = args[1].as_bool().unwrap();
41
+
42
+
vec![
43
+
ParameterType::Flow(is_true),
44
+
ParameterType::Flow(!is_true),
45
+
]
36
46
}
37
47
38
-
fn is_entrypoint( &self ) -> bool { false }
39
-
}
48
+
fn is_entrypoint(&self) -> bool {
49
+
false
50
+
}
51
+
}
+2
-2
src-tauri/src/runtime/nodes/conditional/mod.rs
+2
-2
src-tauri/src/runtime/nodes/conditional/mod.rs
+36
-23
src-tauri/src/runtime/nodes/debug.rs
+36
-23
src-tauri/src/runtime/nodes/debug.rs
···
1
-
use crate::{ runtime::nodes::RuntimeNode, structs::{ nodes::Node, parameter_types::ParameterType } };
1
+
use crate::{
2
+
runtime::nodes::RuntimeNode,
3
+
structs::{nodes::Node, parameter_types::ParameterType},
4
+
};
2
5
3
-
pub struct Debug{
4
-
to_log: Option<ParameterType>
6
+
pub struct Debug {
7
+
outputs: Vec<Vec<(String, isize, isize)>>,
8
+
inputs: Vec<Option<(String, isize, isize)>>
5
9
}
6
10
7
-
impl Debug{
8
-
pub fn new( _: Node ) -> Box<Self>{
9
-
Box::new(Self { to_log: None })
11
+
impl Debug {
12
+
pub fn new(node: Node) -> Box<Self> {
13
+
Box::new(Self {
14
+
outputs: node.outputs.iter().map(|x| {
15
+
x.connections.iter()
16
+
.map(|x| (x.node.clone(), x.index, x.value_type)).collect()}).collect(),
17
+
18
+
inputs: node.inputs.iter().map(|x| {
19
+
let y = x.connections.get(0);
20
+
if let Some(y) = y{
21
+
Some((y.node.clone(), y.index, y.value_type))
22
+
} else{
23
+
None
24
+
}
25
+
}).collect(),
26
+
})
10
27
}
11
28
}
12
29
13
-
impl RuntimeNode for Debug{
14
-
fn outputs( &self ) -> Vec<Vec<( String, isize, isize )>> { vec![] }
15
-
fn execute_dry( &mut self, _: &Vec<ParameterType> ) -> Option<Vec<ParameterType>> { Some(vec![]) }
30
+
impl RuntimeNode for Debug {
31
+
fn outputs(&self) -> Vec<Vec<(String, isize, isize)>> {
32
+
self.outputs.clone()
33
+
}
16
34
17
-
fn execute( &mut self ) -> Option<Vec<ParameterType>> {
18
-
dbg!(&self.to_log);
19
-
self.to_log = None;
35
+
fn inputs(&self) -> Vec<Option<(String, isize, isize)>> {
36
+
self.inputs.clone()
37
+
}
20
38
21
-
None
39
+
fn execute(&mut self, args: Vec<ParameterType>) -> Vec<ParameterType> {
40
+
dbg!(&args); // TODO: Debug to actual UI instead of console
41
+
vec![]
22
42
}
23
43
24
-
fn update_arg( &mut self, index: usize, value: ParameterType ) -> bool {
25
-
if index == 1{
26
-
self.to_log = Some(value);
27
-
true
28
-
} else{
29
-
false
30
-
}
44
+
fn is_entrypoint(&self) -> bool {
45
+
false
31
46
}
32
-
33
-
fn is_entrypoint( &self ) -> bool { false }
34
-
}
47
+
}
+1
-1
src-tauri/src/runtime/nodes/oscactions/mod.rs
+1
-1
src-tauri/src/runtime/nodes/oscactions/mod.rs
+47
-25
src-tauri/src/runtime/nodes/oscactions/sendchatbox.rs
+47
-25
src-tauri/src/runtime/nodes/oscactions/sendchatbox.rs
···
1
1
use std::vec;
2
2
3
-
use crate::{ osc, runtime::nodes::RuntimeNode, structs::{ nodes::Node, parameter_types::ParameterType } };
3
+
use crate::{
4
+
osc,
5
+
runtime::nodes::RuntimeNode,
6
+
structs::{nodes::Node, parameter_types::ParameterType},
7
+
};
4
8
5
-
pub struct OSCActionsSendChatbox{
6
-
to_log: String
9
+
pub struct OSCActionsSendChatbox {
10
+
outputs: Vec<Vec<(String, isize, isize)>>,
11
+
inputs: Vec<Option<(String, isize, isize)>>,
7
12
}
8
13
9
-
impl OSCActionsSendChatbox{
10
-
pub fn new( _: Node ) -> Box<Self>{
11
-
Box::new(Self { to_log: "".into() })
14
+
impl OSCActionsSendChatbox {
15
+
pub fn new(node: Node) -> Box<Self> {
16
+
Box::new(Self {
17
+
outputs: node.outputs.iter().map(|x| {
18
+
x.connections.iter()
19
+
.map(|x| (x.node.clone(), x.index, x.value_type)).collect()}).collect(),
20
+
21
+
inputs: node.inputs.iter().map(|x| {
22
+
let y = x.connections.get(0);
23
+
if let Some(y) = y{
24
+
Some((y.node.clone(), y.index, y.value_type))
25
+
} else{
26
+
None
27
+
}
28
+
}).collect(),
29
+
})
12
30
}
13
31
}
14
32
15
-
impl RuntimeNode for OSCActionsSendChatbox{
16
-
fn outputs( &self ) -> Vec<Vec<( String, isize, isize )>> { vec![] }
17
-
fn execute_dry( &mut self, _: &Vec<ParameterType> ) -> Option<Vec<ParameterType>> { Some(vec![]) }
18
-
19
-
fn execute( &mut self ) -> Option<Vec<ParameterType>> {
20
-
osc::send_message("/chatbox/input", vec![
21
-
ParameterType::String(self.to_log.clone()),
22
-
ParameterType::Boolean(true),
23
-
ParameterType::Boolean(false)
24
-
], "127.0.0.1:9000");
33
+
impl RuntimeNode for OSCActionsSendChatbox {
34
+
fn outputs(&self) -> Vec<Vec<(String, isize, isize)>> {
35
+
self.outputs.clone()
36
+
}
25
37
26
-
None
38
+
fn inputs(&self) -> Vec<Option<(String, isize, isize)>> {
39
+
self.inputs.clone()
27
40
}
28
41
29
-
fn update_arg( &mut self, index: usize, value: ParameterType ) -> bool {
30
-
if index == 1{
31
-
self.to_log = value.as_string().unwrap();
32
-
true
33
-
} else{
34
-
false
42
+
fn execute(&mut self, args: Vec<ParameterType>) -> Vec<ParameterType> {
43
+
if let Ok(msg) = args[1].as_string(){
44
+
osc::send_message(
45
+
"/chatbox/input",
46
+
vec![
47
+
ParameterType::String(msg.clone()),
48
+
ParameterType::Boolean(true),
49
+
ParameterType::Boolean(false),
50
+
],
51
+
"127.0.0.1:9000",
52
+
);
35
53
}
54
+
55
+
vec![]
36
56
}
37
57
38
-
fn is_entrypoint( &self ) -> bool { false }
39
-
}
58
+
fn is_entrypoint(&self) -> bool {
59
+
false
60
+
}
61
+
}
+49
-42
src-tauri/src/runtime/nodes/osctrigger.rs
+49
-42
src-tauri/src/runtime/nodes/osctrigger.rs
···
1
-
use crate::{ runtime::nodes::RuntimeNode, structs::{ nodes::Node, parameter_types::ParameterType } };
1
+
use crate::{
2
+
runtime::nodes::RuntimeNode,
3
+
structs::{nodes::Node, parameter_types::ParameterType},
4
+
};
2
5
3
-
pub struct OSCTrigger{
4
-
outputs: Vec<Vec<( String, isize, isize )>>,
5
-
address: Option<String>,
6
-
runtime_active: bool
6
+
pub struct OSCTrigger {
7
+
outputs: Vec<Vec<(String, isize, isize)>>,
8
+
inputs: Vec<Option<(String, isize, isize)>>,
9
+
10
+
address: Option<String>
7
11
}
8
12
9
-
impl OSCTrigger{
10
-
pub fn new( node: Node ) -> Box<Self>{
13
+
impl OSCTrigger {
14
+
pub fn new(node: Node) -> Box<Self> {
11
15
let value = &node.statics[0].value;
12
16
13
17
Box::new(Self {
14
-
address: if value.is_null(){ None } else { Some(value.as_str().unwrap().to_owned()) },
15
-
outputs: node.outputs.iter()
16
-
.map(| x | {
17
-
x.connections.iter().map(| x | { ( x.node.clone(), x.index, x.value_type ) }).collect()
18
-
}).collect(),
19
-
runtime_active: false
18
+
outputs: node.outputs.iter().map(|x| {
19
+
x.connections.iter()
20
+
.map(|x| (x.node.clone(), x.index, x.value_type)).collect()}).collect(),
21
+
22
+
inputs: node.inputs.iter().map(|x| {
23
+
let y = x.connections.get(0);
24
+
if let Some(y) = y{
25
+
Some((y.node.clone(), y.index, y.value_type))
26
+
} else{
27
+
None
28
+
}
29
+
}).collect(),
30
+
31
+
address: if value.is_null() {
32
+
None
33
+
} else {
34
+
Some(value.as_str().unwrap().to_owned())
35
+
},
20
36
})
21
37
}
22
38
}
23
39
24
-
impl RuntimeNode for OSCTrigger{
25
-
fn outputs( &self ) -> Vec<Vec<( String, isize, isize )>> {
40
+
impl RuntimeNode for OSCTrigger {
41
+
fn outputs(&self) -> Vec<Vec<(String, isize, isize)>> {
26
42
self.outputs.clone()
27
43
}
28
44
29
-
fn execute_dry( &mut self, msg: &Vec<ParameterType> ) -> Option<Vec<ParameterType>> {
30
-
if self.address.is_none(){
31
-
self.runtime_active = false;
32
-
return None
33
-
}
45
+
fn inputs(&self) -> Vec<Option<(String, isize, isize)>> {
46
+
self.inputs.clone()
47
+
}
34
48
35
-
if let ParameterType::String(address) = &msg[0]{
36
-
if *address == *self.address.as_ref().unwrap(){
37
-
self.runtime_active = true;
38
-
Some(msg.clone())
39
-
// The first value is technically the address value,
40
-
// but this value gets ignored as the first output of
41
-
// the osctrigger node is a flow output which gets ignored
42
-
// on dry runs.
49
+
fn execute(&mut self, mut args: Vec<ParameterType>) -> Vec<ParameterType> {
50
+
if args.len() == 0{ return args }
51
+
52
+
let execute = if let Some(internal_address) = &self.address {
53
+
if let Ok(address) = args[0].as_string() {
54
+
address == *internal_address
43
55
} else{
44
-
self.runtime_active = false;
45
-
None
56
+
false
46
57
}
47
58
} else{
48
-
self.runtime_active = false;
49
-
None
50
-
}
51
-
}
52
-
53
-
fn execute( &mut self ) -> Option<Vec<ParameterType>> {
54
-
let execute = self.runtime_active;
55
-
self.runtime_active = false;
59
+
false
60
+
};
56
61
57
-
Some(vec![ ParameterType::Flow(execute) ])
62
+
args[0] = ParameterType::Flow(execute);
63
+
args
58
64
}
59
65
60
-
fn update_arg( &mut self, _: usize, _: ParameterType ) -> bool { false }
61
-
fn is_entrypoint( &self ) -> bool { true }
62
-
}
66
+
fn is_entrypoint(&self) -> bool {
67
+
true
68
+
}
69
+
}
+73
src-tauri/src/runtime/nodes/press_key.rs
+73
src-tauri/src/runtime/nodes/press_key.rs
···
1
+
use std::sync::{Arc, Mutex};
2
+
3
+
use enigo::{Direction, Enigo, Key, Keyboard};
4
+
5
+
use crate::{
6
+
runtime::nodes::RuntimeNode,
7
+
structs::{nodes::Node, parameter_types::ParameterType},
8
+
};
9
+
10
+
pub struct PressKey {
11
+
outputs: Vec<Vec<(String, isize, isize)>>,
12
+
inputs: Vec<Option<(String, isize, isize)>>,
13
+
14
+
key: Option<char>,
15
+
enigo: Arc<Mutex<Enigo>>,
16
+
}
17
+
18
+
impl PressKey {
19
+
pub fn new(node: Node, enigo: Arc<Mutex<Enigo>>) -> Box<Self> {
20
+
let value = &node.statics[0].value;
21
+
22
+
Box::new(Self {
23
+
outputs: node.outputs.iter().map(|x| {
24
+
x.connections.iter()
25
+
.map(|x| (x.node.clone(), x.index, x.value_type)).collect()}).collect(),
26
+
27
+
inputs: node.inputs.iter().map(|x| {
28
+
let y = x.connections.get(0);
29
+
if let Some(y) = y{
30
+
Some((y.node.clone(), y.index, y.value_type))
31
+
} else{
32
+
None
33
+
}
34
+
}).collect(),
35
+
36
+
enigo,
37
+
key: if value.is_null() {
38
+
None
39
+
} else {
40
+
let string = value.as_str().unwrap().to_owned();
41
+
42
+
if string.len() == 1 {
43
+
Some(string.chars().nth(0).unwrap())
44
+
} else {
45
+
None
46
+
}
47
+
},
48
+
})
49
+
}
50
+
}
51
+
52
+
impl RuntimeNode for PressKey {
53
+
fn outputs(&self) -> Vec<Vec<(String, isize, isize)>> {
54
+
self.outputs.clone()
55
+
}
56
+
57
+
fn inputs(&self) -> Vec<Option<(String, isize, isize)>> {
58
+
self.inputs.clone()
59
+
}
60
+
61
+
fn execute(&mut self, _: Vec<ParameterType>) -> Vec<ParameterType> {
62
+
if self.key.is_some() {
63
+
let mut enigo = self.enigo.lock().unwrap();
64
+
enigo.key(Key::MediaPlayPause, Direction::Click).unwrap();
65
+
}
66
+
67
+
vec![]
68
+
}
69
+
70
+
fn is_entrypoint(&self) -> bool {
71
+
false
72
+
}
73
+
}
+70
src-tauri/src/runtime/nodes/shell.rs
+70
src-tauri/src/runtime/nodes/shell.rs
···
1
+
use std::{io::Stdin, process::{Command, Stdio}};
2
+
3
+
use crate::{
4
+
runtime::nodes::RuntimeNode,
5
+
structs::{nodes::Node, parameter_types::ParameterType},
6
+
};
7
+
8
+
pub struct ShellCommand {
9
+
outputs: Vec<Vec<(String, isize, isize)>>,
10
+
inputs: Vec<Option<(String, isize, isize)>>
11
+
}
12
+
13
+
impl ShellCommand {
14
+
pub fn new(node: Node) -> Box<Self> {
15
+
Box::new(Self {
16
+
outputs: node.outputs.iter().map(|x| {
17
+
x.connections.iter()
18
+
.map(|x| (x.node.clone(), x.index, x.value_type)).collect()}).collect(),
19
+
20
+
inputs: node.inputs.iter().map(|x| {
21
+
let y = x.connections.get(0);
22
+
if let Some(y) = y{
23
+
Some((y.node.clone(), y.index, y.value_type))
24
+
} else{
25
+
None
26
+
}
27
+
}).collect()
28
+
})
29
+
}
30
+
}
31
+
32
+
impl RuntimeNode for ShellCommand {
33
+
fn outputs(&self) -> Vec<Vec<(String, isize, isize)>> {
34
+
self.outputs.clone()
35
+
}
36
+
37
+
fn inputs(&self) -> Vec<Option<(String, isize, isize)>> {
38
+
self.inputs.clone()
39
+
}
40
+
41
+
fn execute(&mut self, args: Vec<ParameterType>) -> Vec<ParameterType> {
42
+
if let Ok(cmd) = args[1].as_string(){
43
+
if cmd != ""{
44
+
let mut split_cmd = cmd.split(" ");
45
+
46
+
let mut cmd = Command::new(split_cmd.nth(0).unwrap());
47
+
if split_cmd.clone().count() > 0{ cmd.args(split_cmd); }
48
+
49
+
cmd.stdout(Stdio::piped());
50
+
cmd.stderr(Stdio::piped());
51
+
52
+
let child = cmd.spawn().unwrap();
53
+
let output = child.wait_with_output().unwrap();
54
+
55
+
vec![
56
+
ParameterType::Flow(true),
57
+
ParameterType::String(str::from_utf8(&output.stdout).unwrap().to_owned())
58
+
]
59
+
} else{
60
+
vec![ ParameterType::Flow(false) ]
61
+
}
62
+
} else{
63
+
vec![ ParameterType::Flow(false) ]
64
+
}
65
+
}
66
+
67
+
fn is_entrypoint(&self) -> bool {
68
+
false
69
+
}
70
+
}
+44
-23
src-tauri/src/runtime/nodes/statics/float.rs
+44
-23
src-tauri/src/runtime/nodes/statics/float.rs
···
1
-
use crate::{ runtime::nodes::RuntimeNode, structs::{ nodes::Node, parameter_types::ParameterType } };
1
+
use crate::{
2
+
runtime::nodes::RuntimeNode,
3
+
structs::{nodes::Node, parameter_types::ParameterType},
4
+
};
5
+
6
+
pub struct StaticFloat {
7
+
outputs: Vec<Vec<(String, isize, isize)>>,
8
+
inputs: Vec<Option<(String, isize, isize)>>,
2
9
3
-
pub struct StaticFloat{
4
-
outputs: Vec<Vec<( String, isize, isize )>>,
5
-
value: Option<f32>
10
+
value: Option<f32>,
6
11
}
7
12
8
-
impl StaticFloat{
9
-
pub fn new( node: Node ) -> Box<Self>{
13
+
impl StaticFloat {
14
+
pub fn new(node: Node) -> Box<Self> {
10
15
let value = &node.statics[0].value;
11
16
12
17
Box::new(Self {
13
-
value: if value.is_null(){ None } else { Some(value.as_f64().unwrap() as f32) },
14
-
outputs: node.outputs.iter()
15
-
.map(| x | {
16
-
x.connections.iter().map(| x | { ( x.node.clone(), x.index, x.value_type ) }).collect()
17
-
}).collect(),
18
+
outputs: node.outputs.iter().map(|x| {
19
+
x.connections.iter()
20
+
.map(|x| (x.node.clone(), x.index, x.value_type)).collect()}).collect(),
21
+
22
+
inputs: node.inputs.iter().map(|x| {
23
+
let y = x.connections.get(0);
24
+
if let Some(y) = y{
25
+
Some((y.node.clone(), y.index, y.value_type))
26
+
} else{
27
+
None
28
+
}
29
+
}).collect(),
30
+
31
+
value: if value.is_null() {
32
+
None
33
+
} else {
34
+
Some(value.as_f64().unwrap() as f32)
35
+
}
18
36
})
19
37
}
20
38
}
21
39
22
-
impl RuntimeNode for StaticFloat{
23
-
fn outputs( &self ) -> Vec<Vec<( String, isize, isize )>> {
40
+
impl RuntimeNode for StaticFloat {
41
+
fn outputs(&self) -> Vec<Vec<(String, isize, isize)>> {
24
42
self.outputs.clone()
25
43
}
26
44
27
-
fn execute_dry( &mut self, _: &Vec<ParameterType> ) -> Option<Vec<ParameterType>> {
28
-
if self.value.is_some(){
29
-
Some(vec![ ParameterType::Float(self.value.clone().unwrap()) ])
30
-
} else{
31
-
None
45
+
fn inputs(&self) -> Vec<Option<(String, isize, isize)>> {
46
+
self.inputs.clone()
47
+
}
48
+
49
+
fn execute(&mut self, _: Vec<ParameterType>) -> Vec<ParameterType> {
50
+
if self.value.is_some() {
51
+
vec![ParameterType::Float(self.value.clone().unwrap())]
52
+
} else {
53
+
vec![ParameterType::Float(0.0)]
32
54
}
33
55
}
34
56
35
-
fn execute( &mut self ) -> Option<Vec<ParameterType>> { None }
36
-
37
-
fn update_arg( &mut self, _: usize, _: ParameterType ) -> bool { false }
38
-
fn is_entrypoint( &self ) -> bool { true }
39
-
}
57
+
fn is_entrypoint(&self) -> bool {
58
+
false
59
+
}
60
+
}
+44
-23
src-tauri/src/runtime/nodes/statics/int.rs
+44
-23
src-tauri/src/runtime/nodes/statics/int.rs
···
1
-
use crate::{ runtime::nodes::RuntimeNode, structs::{ nodes::Node, parameter_types::ParameterType } };
1
+
use crate::{
2
+
runtime::nodes::RuntimeNode,
3
+
structs::{nodes::Node, parameter_types::ParameterType},
4
+
};
5
+
6
+
pub struct StaticInt {
7
+
outputs: Vec<Vec<(String, isize, isize)>>,
8
+
inputs: Vec<Option<(String, isize, isize)>>,
2
9
3
-
pub struct StaticInt{
4
-
outputs: Vec<Vec<( String, isize, isize )>>,
5
-
value: Option<i32>
10
+
value: Option<i32>,
6
11
}
7
12
8
-
impl StaticInt{
9
-
pub fn new( node: Node ) -> Box<Self>{
13
+
impl StaticInt {
14
+
pub fn new(node: Node) -> Box<Self> {
10
15
let value = &node.statics[0].value;
11
16
12
17
Box::new(Self {
13
-
value: if value.is_null(){ None } else { Some(value.as_i64().unwrap() as i32) },
14
-
outputs: node.outputs.iter()
15
-
.map(| x | {
16
-
x.connections.iter().map(| x | { ( x.node.clone(), x.index, x.value_type ) }).collect()
17
-
}).collect(),
18
+
outputs: node.outputs.iter().map(|x| {
19
+
x.connections.iter()
20
+
.map(|x| (x.node.clone(), x.index, x.value_type)).collect()}).collect(),
21
+
22
+
inputs: node.inputs.iter().map(|x| {
23
+
let y = x.connections.get(0);
24
+
if let Some(y) = y{
25
+
Some((y.node.clone(), y.index, y.value_type))
26
+
} else{
27
+
None
28
+
}
29
+
}).collect(),
30
+
31
+
value: if value.is_null() {
32
+
None
33
+
} else {
34
+
Some(value.as_i64().unwrap() as i32)
35
+
}
18
36
})
19
37
}
20
38
}
21
39
22
-
impl RuntimeNode for StaticInt{
23
-
fn outputs( &self ) -> Vec<Vec<( String, isize, isize )>> {
40
+
impl RuntimeNode for StaticInt {
41
+
fn outputs(&self) -> Vec<Vec<(String, isize, isize)>> {
24
42
self.outputs.clone()
25
43
}
26
44
27
-
fn execute_dry( &mut self, _: &Vec<ParameterType> ) -> Option<Vec<ParameterType>> {
28
-
if self.value.is_some(){
29
-
Some(vec![ ParameterType::Int(self.value.clone().unwrap()) ])
30
-
} else{
31
-
None
45
+
fn inputs(&self) -> Vec<Option<(String, isize, isize)>> {
46
+
self.inputs.clone()
47
+
}
48
+
49
+
fn execute(&mut self, _: Vec<ParameterType>) -> Vec<ParameterType> {
50
+
if self.value.is_some() {
51
+
vec![ParameterType::Int(self.value.clone().unwrap())]
52
+
} else {
53
+
vec![ParameterType::Int(0)]
32
54
}
33
55
}
34
56
35
-
fn execute( &mut self ) -> Option<Vec<ParameterType>> { None }
36
-
37
-
fn update_arg( &mut self, _: usize, _: ParameterType ) -> bool { false }
38
-
fn is_entrypoint( &self ) -> bool { true }
39
-
}
57
+
fn is_entrypoint(&self) -> bool {
58
+
false
59
+
}
60
+
}
+2
-2
src-tauri/src/runtime/nodes/statics/mod.rs
+2
-2
src-tauri/src/runtime/nodes/statics/mod.rs
+44
-23
src-tauri/src/runtime/nodes/statics/string.rs
+44
-23
src-tauri/src/runtime/nodes/statics/string.rs
···
1
-
use crate::{ runtime::nodes::RuntimeNode, structs::{ nodes::Node, parameter_types::ParameterType } };
1
+
use crate::{
2
+
runtime::nodes::RuntimeNode,
3
+
structs::{nodes::Node, parameter_types::ParameterType},
4
+
};
5
+
6
+
pub struct StaticString {
7
+
outputs: Vec<Vec<(String, isize, isize)>>,
8
+
inputs: Vec<Option<(String, isize, isize)>>,
2
9
3
-
pub struct StaticString{
4
-
outputs: Vec<Vec<( String, isize, isize )>>,
5
-
value: Option<String>
10
+
value: Option<String>,
6
11
}
7
12
8
-
impl StaticString{
9
-
pub fn new( node: Node ) -> Box<Self>{
13
+
impl StaticString {
14
+
pub fn new(node: Node) -> Box<Self> {
10
15
let value = &node.statics[0].value;
11
16
12
17
Box::new(Self {
13
-
value: if value.is_null(){ None } else { Some(value.as_str().unwrap().to_owned()) },
14
-
outputs: node.outputs.iter()
15
-
.map(| x | {
16
-
x.connections.iter().map(| x | { ( x.node.clone(), x.index, x.value_type ) }).collect()
17
-
}).collect(),
18
+
outputs: node.outputs.iter().map(|x| {
19
+
x.connections.iter()
20
+
.map(|x| (x.node.clone(), x.index, x.value_type)).collect()}).collect(),
21
+
22
+
inputs: node.inputs.iter().map(|x| {
23
+
let y = x.connections.get(0);
24
+
if let Some(y) = y{
25
+
Some((y.node.clone(), y.index, y.value_type))
26
+
} else{
27
+
None
28
+
}
29
+
}).collect(),
30
+
31
+
value: if value.is_null() {
32
+
None
33
+
} else {
34
+
Some(value.as_str().unwrap().to_owned())
35
+
}
18
36
})
19
37
}
20
38
}
21
39
22
-
impl RuntimeNode for StaticString{
23
-
fn outputs( &self ) -> Vec<Vec<( String, isize, isize )>> {
40
+
impl RuntimeNode for StaticString {
41
+
fn outputs(&self) -> Vec<Vec<(String, isize, isize)>> {
24
42
self.outputs.clone()
25
43
}
26
44
27
-
fn execute_dry( &mut self, _: &Vec<ParameterType> ) -> Option<Vec<ParameterType>> {
28
-
if self.value.is_some(){
29
-
Some(vec![ ParameterType::String(self.value.clone().unwrap()) ])
30
-
} else{
31
-
None
45
+
fn inputs(&self) -> Vec<Option<(String, isize, isize)>> {
46
+
self.inputs.clone()
47
+
}
48
+
49
+
fn execute(&mut self, _: Vec<ParameterType>) -> Vec<ParameterType> {
50
+
if self.value.is_some() {
51
+
vec![ParameterType::String(self.value.clone().unwrap())]
52
+
} else {
53
+
vec![ParameterType::String("".to_owned())]
32
54
}
33
55
}
34
56
35
-
fn execute( &mut self ) -> Option<Vec<ParameterType>> { None }
36
-
37
-
fn update_arg( &mut self, _: usize, _: ParameterType ) -> bool { false }
38
-
fn is_entrypoint( &self ) -> bool { true }
39
-
}
57
+
fn is_entrypoint(&self) -> bool {
58
+
false
59
+
}
60
+
}
+78
-28
src-tauri/src/runtime/nodes.rs
+78
-28
src-tauri/src/runtime/nodes.rs
···
1
-
use std::collections::HashMap;
1
+
use std::{
2
+
collections::HashMap,
3
+
sync::{Arc, Mutex},
4
+
};
5
+
6
+
// #[cfg(target_os = "windows")]
7
+
use enigo::Enigo;
8
+
9
+
use crate::{
10
+
runtime::nodes::{
11
+
conditional::{
12
+
ifequal::ConditionalIfEqual, iffalse::ConditionalIfFalse, iftrue::ConditionalIfTrue,
13
+
}, debug::Debug, oscactions::sendchatbox::OSCActionsSendChatbox, osctrigger::OSCTrigger, shell::ShellCommand, statics::{float::StaticFloat, int::StaticInt, string::StaticString}
14
+
},
15
+
structs::{nodes::Node, parameter_types::ParameterType},
16
+
};
2
17
3
-
use crate::{ runtime::nodes::{ conditional::{ ifequal::ConditionalIfEqual, iffalse::ConditionalIfFalse, iftrue::ConditionalIfTrue }, debug::Debug, oscactions::sendchatbox::OSCActionsSendChatbox, osctrigger::OSCTrigger, statics::{ float::StaticFloat, int::StaticInt, string::StaticString } }, structs::{ nodes::Node, parameter_types::ParameterType } };
18
+
// #[cfg(target_os = "windows")]
19
+
use crate::runtime::nodes::press_key::PressKey;
4
20
21
+
mod conditional;
22
+
mod debug;
23
+
mod oscactions;
5
24
mod osctrigger;
6
-
mod debug;
7
25
mod statics;
8
-
mod conditional;
9
-
mod oscactions;
26
+
mod shell;
27
+
28
+
// #[cfg(target_os = "windows")]
29
+
mod press_key;
10
30
11
-
pub struct RuntimeNodeTree{
12
-
pub nodes: HashMap<String, Box<dyn RuntimeNode>>
31
+
pub struct RuntimeNodeTree {
32
+
pub nodes: HashMap<String, Box<dyn RuntimeNode>>,
13
33
}
14
34
15
-
unsafe impl Send for RuntimeNodeTree{}
35
+
unsafe impl Send for RuntimeNodeTree {}
16
36
17
-
impl RuntimeNodeTree{
18
-
pub fn from( tree: Vec<Node> ) -> Self{
37
+
impl RuntimeNodeTree {
38
+
pub fn from(tree: Vec<Node>, /*#[cfg(target_os = "windows")]*/ enigo: Arc<Mutex<Enigo>>) -> Self {
19
39
let mut runtime_nodes: HashMap<String, Box<dyn RuntimeNode>> = HashMap::new();
20
-
for node in tree{
21
-
match node.type_id.as_str(){
22
-
"osctrigger" => { runtime_nodes.insert(node.id.clone(), OSCTrigger::new(node)); }
40
+
for node in tree {
41
+
match node.type_id.as_str() {
42
+
"osctrigger" => {
43
+
runtime_nodes.insert(node.id.clone(), OSCTrigger::new(node));
44
+
}
23
45
24
-
"staticstring" => { runtime_nodes.insert(node.id.clone(), StaticString::new(node)); }
25
-
"staticint" => { runtime_nodes.insert(node.id.clone(), StaticInt::new(node)); }
26
-
"staticfloat" => { runtime_nodes.insert(node.id.clone(), StaticFloat::new(node)); }
46
+
"staticstring" => {
47
+
runtime_nodes.insert(node.id.clone(), StaticString::new(node));
48
+
}
49
+
"staticint" => {
50
+
runtime_nodes.insert(node.id.clone(), StaticInt::new(node));
51
+
}
52
+
"staticfloat" => {
53
+
runtime_nodes.insert(node.id.clone(), StaticFloat::new(node));
54
+
}
27
55
28
-
"iftrue" => { runtime_nodes.insert(node.id.clone(), ConditionalIfTrue::new(node)); }
29
-
"iffalse" => { runtime_nodes.insert(node.id.clone(), ConditionalIfFalse::new(node)); }
30
-
"ifequal" => { runtime_nodes.insert(node.id.clone(), ConditionalIfEqual::new(node)); }
56
+
"iftrue" => {
57
+
runtime_nodes.insert(node.id.clone(), ConditionalIfTrue::new(node));
58
+
}
59
+
"iffalse" => {
60
+
runtime_nodes.insert(node.id.clone(), ConditionalIfFalse::new(node));
61
+
}
62
+
"ifequal" => {
63
+
runtime_nodes.insert(node.id.clone(), ConditionalIfEqual::new(node));
64
+
}
65
+
66
+
"oscsendchatbox" => {
67
+
runtime_nodes.insert(node.id.clone(), OSCActionsSendChatbox::new(node));
68
+
}
69
+
70
+
"debug" => {
71
+
runtime_nodes.insert(node.id.clone(), Debug::new(node));
72
+
}
73
+
74
+
// #[cfg(target_os = "windows")]
75
+
"presskey" => {
76
+
runtime_nodes.insert(node.id.clone(), PressKey::new(node, enigo.clone()));
77
+
}
31
78
32
-
"oscsendchatbox" => { runtime_nodes.insert(node.id.clone(), OSCActionsSendChatbox::new(node)); }
79
+
"shellcommand" => {
80
+
runtime_nodes.insert(node.id.clone(), ShellCommand::new(node));
81
+
}
33
82
34
-
"debug" => { runtime_nodes.insert(node.id.clone(), Debug::new(node)); }
35
83
_ => {}
36
84
}
37
85
}
38
86
39
-
Self { nodes: runtime_nodes }
87
+
Self {
88
+
nodes: runtime_nodes,
89
+
}
40
90
}
41
91
}
42
92
43
-
pub trait RuntimeNode{
44
-
fn outputs( &self ) -> Vec<Vec<( String, isize, isize )>>; // Node ID, input index, output value type
45
-
fn execute_dry( &mut self, msg: &Vec<ParameterType> ) -> Option<Vec<ParameterType>>; // Only update values on the first loop through
46
-
fn execute( &mut self ) -> Option<Vec<ParameterType>>; // Then call functions on the second loop
47
-
fn update_arg( &mut self, index: usize, value: ParameterType ) -> bool;
48
-
fn is_entrypoint( &self ) -> bool;
93
+
pub trait RuntimeNode {
94
+
fn outputs(&self) -> Vec<Vec<(String, isize, isize)>>; // Node ID, input index, output value type
95
+
fn inputs(&self) -> Vec<Option<(String, isize, isize)>>; // Node ID, input index, output value type
96
+
97
+
fn execute(&mut self, args: Vec<ParameterType>) -> Vec<ParameterType>; // Then call functions on the second loop
98
+
fn is_entrypoint(&self) -> bool;
49
99
}
+65
-40
src-tauri/src/runtime.rs
+65
-40
src-tauri/src/runtime.rs
···
1
-
use anyhow::{ bail, Result };
1
+
use std::collections::HashMap;
2
+
3
+
use anyhow::{bail, Result};
2
4
3
-
use crate::{ runtime::nodes::RuntimeNodeTree, structs::parameter_types::ParameterType };
5
+
use crate::{runtime::nodes::RuntimeNodeTree, structs::parameter_types::ParameterType};
4
6
5
-
pub mod nodes;
6
7
pub mod commands;
8
+
pub mod nodes;
7
9
8
-
// This is horrible. I know, I'm sorry.
10
+
// TODO: Variables
9
11
10
-
pub fn runtime_dry( entry: String, parameters: &Vec<ParameterType>, tab: &mut RuntimeNodeTree ) -> Result<()>{
11
-
let node = tab.nodes.get_mut(&entry);
12
-
if node.is_none(){ bail!("Cannot find node"); }
12
+
pub fn recurse_runtime(entry: String, tab: &mut RuntimeNodeTree, args: Vec<ParameterType>) -> Result<()>{
13
+
let ( out_args, output_map ) = runtime(entry, tab, args)?;
13
14
14
-
let node = node.unwrap();
15
+
let mut next_node_args: HashMap<String, Vec<ParameterType>> = HashMap::new();
15
16
16
-
let output_map = node.outputs();
17
-
let args = node.execute_dry(parameters);
17
+
for i in 0..out_args.len(){
18
+
if output_map.len() <= i { break; }
19
+
let links = &output_map[i];
18
20
19
-
if args.is_some(){
20
-
let args = args.unwrap();
21
+
for ( id, link_index, _ ) in links{
22
+
let link_index = link_index.clone() as usize;
21
23
22
-
for i in 0..args.len(){
23
-
let arg = &args[i];
24
+
if next_node_args.contains_key(id){
25
+
let args: &mut _ = next_node_args.get_mut(id).unwrap();
26
+
while args.len() < link_index{ args.push(ParameterType::None); }
24
27
25
-
for output in &output_map[i]{
26
-
if output.2 == 5{ break; } // Ignore flow outputs
28
+
args.push(out_args[i].clone());
29
+
} else{
30
+
let mut args = vec![ParameterType::None; link_index];
31
+
args.push(out_args[i].clone());
27
32
28
-
let next_node = tab.nodes.get_mut(&output.0);
29
-
if next_node.is_none(){ bail!("Cannot find node {}", output.0) }
33
+
next_node_args.insert(id.clone(), args);
34
+
}
35
+
}
36
+
}
30
37
31
-
let next_node = next_node.unwrap();
32
-
let can_update = next_node.update_arg(output.1 as usize, arg.clone());
38
+
for i in 0..out_args.len(){
39
+
if let ParameterType::Flow(next) = out_args[i]{
40
+
if next{
41
+
let links = &output_map[i];
33
42
34
-
if can_update{
35
-
runtime_dry(output.0.clone(), &vec![], tab)?;
43
+
for ( id, _, _ ) in links{
44
+
let args = next_node_args.remove(id).unwrap();
45
+
recurse_runtime(id.clone(), tab, args)?;
36
46
}
37
47
}
38
48
}
···
41
51
Ok(())
42
52
}
43
53
44
-
pub fn runtime( entry: String, tab: &mut RuntimeNodeTree ) -> Result<()>{
54
+
pub fn runtime(entry: String, tab: &mut RuntimeNodeTree, mut args: Vec<ParameterType>) -> Result<(Vec<ParameterType>, Vec<Vec<(String, isize, isize)>>)> {
45
55
let node = tab.nodes.get_mut(&entry);
46
-
if node.is_none(){ bail!("Cannot find node"); }
56
+
if node.is_none() { bail!("Cannot find node"); }
47
57
48
58
let node = node.unwrap();
59
+
let inputs = node.inputs();
49
60
50
-
let next = node.execute();
51
-
if next.is_some(){
52
-
let next = next.unwrap();
61
+
let mut needed_input_nodes = HashMap::new();
53
62
54
-
let outputs = node.outputs();
63
+
for i in 0..inputs.len(){
64
+
if i >= args.len() || args[i] == ParameterType::None{
65
+
if let Some(input) = &inputs[i]{
66
+
if !needed_input_nodes.contains_key(&input.0){
67
+
needed_input_nodes.insert(input.0.clone(), vec![(input.1.clone(), i.clone())]);
68
+
} else{
69
+
needed_input_nodes.get_mut(&input.0).unwrap().push((input.1.clone(), i.clone()));
70
+
}
71
+
}
72
+
}
73
+
}
55
74
56
-
for i in 0..next.len(){
57
-
let arg = &next[i];
58
-
if i >= outputs.len() { break; }
75
+
for ( id, needed ) in needed_input_nodes{
76
+
let (out_args, _) = runtime(id, tab, vec![]).unwrap();
77
+
78
+
for ( output, input ) in needed{
79
+
let arg = &out_args[output as usize];
59
80
60
-
for output in &outputs[i]{
61
-
if let ParameterType::Flow(next) = arg{
62
-
if *next{
63
-
// This is a flow output, continue
64
-
runtime(output.0.clone(), tab)?;
65
-
}
66
-
}
81
+
if args.len() >= input{
82
+
while args.len() < input{ args.push(ParameterType::None); }
83
+
args.push(arg.clone());
84
+
} else{
85
+
args[input] = arg.clone();
67
86
}
68
87
}
69
88
}
70
89
71
-
Ok(())
72
-
}
90
+
let node = tab.nodes.get_mut(&entry); // TODO: Find a way to only do this lookup once
91
+
if node.is_none() { bail!("Cannot find node"); }
92
+
93
+
let node = node.unwrap();
94
+
95
+
let output = node.execute(args);
96
+
Ok((output, node.outputs()))
97
+
}
+62
-39
src-tauri/src/setup.rs
+62
-39
src-tauri/src/setup.rs
···
1
-
use std::{ collections::HashMap, fs::File, io::Read, sync::Mutex };
2
-
use crossbeam_channel::{ Receiver, bounded };
1
+
use crossbeam_channel::{bounded, Receiver};
2
+
use std::{
3
+
collections::HashMap,
4
+
fs::File,
5
+
io::Read,
6
+
sync::{Arc, Mutex},
7
+
};
3
8
4
9
use flate2::read::GzDecoder;
5
-
use serde_json::{ Map, Value };
6
-
use tauri::{ App, Emitter, Listener, Manager, WindowEvent };
10
+
use serde_json::{Map, Value};
11
+
use tauri::{App, Emitter, Listener, Manager, WindowEvent};
7
12
8
-
use crate::{ osc::{ self, OSCMessage }, runtime::{ commands::RuntimeCommand, nodes::RuntimeNodeTree, runtime, runtime_dry }, structs::parameter_types::ParameterType, utils::setup_traymenu::setup_traymenu };
13
+
use crate::{
14
+
osc::{self, OSCMessage}, runtime::{commands::RuntimeCommand, nodes::RuntimeNodeTree, recurse_runtime}, structs::parameter_types::ParameterType, utils::{setup_traymenu::setup_traymenu, vrchat_builtin_parameters}
15
+
};
9
16
10
17
pub fn setup(
11
18
app: &mut App,
12
19
addresses: &'static Mutex<Vec<OSCMessage>>,
13
-
runtime_command_receiver: Receiver<RuntimeCommand>
20
+
runtime_command_receiver: Receiver<RuntimeCommand>,
14
21
) {
15
22
let window = app.get_webview_window("main").unwrap();
16
23
window.hide().unwrap();
17
24
18
25
let win_handle = window.clone();
19
-
window.on_window_event(move | event | {
20
-
match event{
21
-
WindowEvent::CloseRequested { api, .. } => {
22
-
api.prevent_close();
23
26
27
+
window.on_window_event(move |event| match event {
28
+
WindowEvent::CloseRequested { api, .. } => {
29
+
api.prevent_close();
30
+
win_handle.emit("prompt_to_close", ()).unwrap();
31
+
}
32
+
WindowEvent::Resized(_) => {
33
+
let minimised = win_handle.is_minimized().unwrap();
34
+
if minimised{
24
35
win_handle.hide().unwrap();
25
36
win_handle.emit("hide-window", ()).unwrap();
37
+
win_handle.unminimize().unwrap();
26
38
}
27
-
_ => {}
28
39
}
40
+
_ => {}
29
41
});
30
42
31
43
setup_traymenu(app.handle());
···
49
61
handle.emit("load_new_tab", Value::Object(map)).unwrap();
50
62
});
51
63
52
-
let ( sender, receiver ) = bounded(1024);
64
+
let (sender, receiver) = bounded(1024);
53
65
54
66
tokio::spawn(async move {
55
67
osc::start_server(sender, "127.0.0.1:9001");
56
68
});
57
69
58
-
let ( runtime_sender, runtime_receiver ) = bounded(1024);
70
+
let (runtime_sender, runtime_receiver) = bounded(1024);
59
71
60
72
let runtime_sender_1 = runtime_sender.clone();
61
73
tokio::spawn(async move {
···
73
85
74
86
let msg = message.clone();
75
87
let mut addrs = addresses.lock().unwrap();
76
-
if !addrs.contains(&msg) { addrs.push(msg); }
88
+
if !addrs.contains(&msg) {
89
+
addrs.push(msg);
90
+
}
91
+
92
+
if message.address == "/avatar/change".to_owned(){
93
+
*addrs = vrchat_builtin_parameters::get_read_addresses();
77
94
78
-
runtime_sender.send(RuntimeCommand::OSCMessage(message)).unwrap();
95
+
// TODO: Read avatar paramaters from file
96
+
}
97
+
98
+
runtime_sender
99
+
.send(RuntimeCommand::OSCMessage(message))
100
+
.unwrap();
79
101
}
80
102
});
81
103
104
+
// TODO: Run tabs in seperate threads (really not looking forward to this... thanks rust)
105
+
82
106
tokio::spawn(async move {
83
107
let mut tabs: HashMap<String, RuntimeNodeTree> = HashMap::new();
84
108
109
+
// #[cfg(target_os = "windows")]
110
+
let enigo = Arc::new(Mutex::new(enigo::Enigo::new(&enigo::Settings::default()).unwrap()));
111
+
85
112
loop {
86
113
let cmd = runtime_receiver.recv().unwrap();
87
114
88
-
match cmd{
89
-
RuntimeCommand::OSCMessage( msg ) => {
90
-
for ( _, mut tab ) in &mut tabs{
91
-
let keys: Vec<String> = tab.nodes.keys().map(| x | { x.clone() }).collect();
115
+
match cmd {
116
+
RuntimeCommand::OSCMessage(msg) => {
117
+
for (_, mut tab) in &mut tabs {
118
+
let keys: Vec<String> = tab.nodes.keys().map(|x| x.clone()).collect();
92
119
93
-
for id in keys.clone(){
120
+
for id in keys {
94
121
let entry = tab.nodes[&id].is_entrypoint();
95
122
96
-
if entry{
97
-
let args = vec![
98
-
vec![ ParameterType::String(msg.address.clone()) ], msg.values.clone()
99
-
].concat();
100
-
101
-
runtime_dry(id.clone(), &args, &mut tab).unwrap();
102
-
}
103
-
}
104
-
105
-
for id in keys{
106
-
let entry = tab.nodes[&id].is_entrypoint();
123
+
if entry {
124
+
let mut args = vec![ ParameterType::String(msg.address.clone())];
125
+
let mut values = msg.values.clone();
107
126
108
-
if entry{
109
-
let _ = runtime(id.clone(), &mut tab);
127
+
args.append(&mut values);
128
+
let _ = recurse_runtime(id.clone(), &mut tab, args);
110
129
}
111
130
}
112
131
}
113
-
},
132
+
}
133
+
134
+
RuntimeCommand::AddTab(graph, id) => {
135
+
// #[cfg(target_os = "windows")]
136
+
tabs.insert(id, RuntimeNodeTree::from(graph, enigo.clone()));
114
137
115
-
RuntimeCommand::AddTab( graph, id ) => {
116
-
tabs.insert(id, RuntimeNodeTree::from(graph));
117
-
},
118
-
RuntimeCommand::RemoveTab( id ) => {
138
+
// #[cfg(target_os = "linux")]
139
+
// tabs.insert(id, RuntimeNodeTree::from(graph));
140
+
}
141
+
RuntimeCommand::RemoveTab(id) => {
119
142
tabs.remove(&id);
120
143
}
121
144
}
122
145
}
123
146
});
124
-
}
147
+
}
+1
-1
src-tauri/src/structs/mod.rs
+1
-1
src-tauri/src/structs/mod.rs
+13
-12
src-tauri/src/structs/nodes.rs
+13
-12
src-tauri/src/structs/nodes.rs
···
1
-
use serde::{ Deserialize, Serialize };
1
+
use serde::{Deserialize, Serialize};
2
2
use serde_json::Value;
3
3
4
4
#[derive(Serialize, Deserialize, Debug, Clone)]
5
-
pub struct Node{
5
+
pub struct Node {
6
6
pub id: String,
7
7
pub name: String,
8
-
pub outputs: Vec<NodeOutput>,
9
-
pub pos: [ f32; 2 ],
8
+
pub outputs: Vec<NodeIO>,
9
+
pub inputs: Vec<NodeIO>,
10
+
pub pos: [f32; 2],
10
11
pub statics: Vec<NodeStatic>,
11
12
12
13
#[serde(rename = "typeId")]
13
-
pub type_id: String
14
+
pub type_id: String,
14
15
}
15
16
16
17
#[derive(Serialize, Deserialize, Debug, Clone)]
17
-
pub struct NodeStatic{
18
+
pub struct NodeStatic {
18
19
pub name: String,
19
20
20
21
#[serde(rename = "type")]
21
22
pub value_type: isize,
22
-
pub value: Value
23
+
pub value: Value,
23
24
}
24
25
25
26
#[derive(Serialize, Deserialize, Debug, Clone)]
26
-
pub struct NodeOutput{
27
+
pub struct NodeIO {
27
28
pub name: String,
28
29
29
30
#[serde(rename = "type")]
30
31
pub value_type: isize,
31
-
pub connections: Vec<NodeOutputConnections>
32
+
pub connections: Vec<NodeOutputConnections>,
32
33
}
33
34
34
35
#[derive(Serialize, Deserialize, Debug, Clone)]
35
-
pub struct NodeOutputConnections{
36
+
pub struct NodeOutputConnections {
36
37
pub name: String,
37
38
pub node: String,
38
39
pub index: isize,
39
40
40
41
#[serde(rename = "type")]
41
-
pub value_type: isize
42
-
}
42
+
pub value_type: isize,
43
+
}
+41
-29
src-tauri/src/structs/parameter_types.rs
+41
-29
src-tauri/src/structs/parameter_types.rs
···
1
-
use anyhow::{ Result, bail };
1
+
use anyhow::{bail, Result};
2
2
use serde::Serialize;
3
3
4
4
#[derive(Serialize, Clone, Debug, PartialEq)]
···
13
13
String(String),
14
14
Flow(bool),
15
15
16
-
None
16
+
None,
17
17
}
18
18
19
-
impl ParameterType{
20
-
pub fn as_bool( &self ) -> Result<bool>{
21
-
match self{
22
-
ParameterType::Boolean( val ) => Ok(val.clone()),
23
-
ParameterType::Int( val ) => if *val == 0{ Ok(false) } else { Ok(true) },
24
-
_ => bail!("Cannot cast to bool.")
19
+
impl ParameterType {
20
+
pub fn as_bool(&self) -> Result<bool> {
21
+
match self {
22
+
ParameterType::Boolean(val) => Ok(val.clone()),
23
+
ParameterType::Int(val) => {
24
+
if *val == 0 {
25
+
Ok(false)
26
+
} else {
27
+
Ok(true)
28
+
}
29
+
}
30
+
_ => bail!("Cannot cast to bool."),
25
31
}
26
32
}
27
33
28
-
pub fn as_int( &self ) -> Result<i32>{
29
-
match self{
30
-
ParameterType::Boolean( val ) => if *val{ Ok(1) } else { Ok(0) },
31
-
ParameterType::Int( val ) => Ok(val.clone()),
32
-
ParameterType::Float( val ) => Ok(val.round().clone() as i32),
33
-
ParameterType::String( val ) => Ok(val.clone().parse()?),
34
-
_ => bail!("Cannot cast to int.")
34
+
pub fn as_int(&self) -> Result<i32> {
35
+
match self {
36
+
ParameterType::Boolean(val) => {
37
+
if *val {
38
+
Ok(1)
39
+
} else {
40
+
Ok(0)
41
+
}
42
+
}
43
+
ParameterType::Int(val) => Ok(val.clone()),
44
+
ParameterType::Float(val) => Ok(val.round().clone() as i32),
45
+
ParameterType::String(val) => Ok(val.clone().parse()?),
46
+
_ => bail!("Cannot cast to int."),
35
47
}
36
48
}
37
49
38
-
pub fn as_float( &self ) -> Result<f32>{
39
-
match self{
40
-
ParameterType::Int( val ) => Ok(val.clone() as f32),
41
-
ParameterType::Float( val ) => Ok(val.clone()),
42
-
ParameterType::String( val ) => Ok(val.clone().parse()?),
43
-
_ => bail!("Cannot cast to float.")
50
+
pub fn as_float(&self) -> Result<f32> {
51
+
match self {
52
+
ParameterType::Int(val) => Ok(val.clone() as f32),
53
+
ParameterType::Float(val) => Ok(val.clone()),
54
+
ParameterType::String(val) => Ok(val.clone().parse()?),
55
+
_ => bail!("Cannot cast to float."),
44
56
}
45
57
}
46
58
47
-
pub fn as_string( &self ) -> Result<String>{
48
-
match self{
49
-
ParameterType::Boolean( val ) => Ok(val.clone().to_string()),
50
-
ParameterType::Int( val ) => Ok(val.clone().to_string()),
51
-
ParameterType::Float( val ) => Ok(val.clone().to_string()),
52
-
ParameterType::String( val ) => Ok(val.clone()),
53
-
_ => bail!("Cannot cast to string.")
59
+
pub fn as_string(&self) -> Result<String> {
60
+
match self {
61
+
ParameterType::Boolean(val) => Ok(val.clone().to_string()),
62
+
ParameterType::Int(val) => Ok(val.clone().to_string()),
63
+
ParameterType::Float(val) => Ok(val.clone().to_string()),
64
+
ParameterType::String(val) => Ok(val.clone()),
65
+
_ => bail!("Cannot cast to string."),
54
66
}
55
67
}
56
-
}
68
+
}
+26
-10
src-tauri/src/utils/config.rs
+26
-10
src-tauri/src/utils/config.rs
···
1
-
use std::{ collections::HashMap, fs::File, io::{ Read, Write }, path::PathBuf, sync::Mutex };
1
+
use std::{
2
+
collections::HashMap,
3
+
fs::File,
4
+
io::{Read, Write},
5
+
path::PathBuf,
6
+
sync::{Mutex, MutexGuard},
7
+
};
2
8
3
-
use flate2::{ read::GzDecoder, write::GzEncoder, Compression };
4
-
use serde::{ Deserialize, Serialize };
9
+
use chrono::Utc;
10
+
use flate2::{read::GzDecoder, write::GzEncoder, Compression};
11
+
use serde::{Deserialize, Serialize};
5
12
6
13
use crate::structs::nodes::Node;
7
14
8
15
#[derive(Clone, Serialize, Deserialize, Debug)]
9
-
pub struct ConfigValues{
16
+
pub struct ConfigValues {
10
17
#[serde(default)]
11
-
pub loaded_tabs: HashMap<String, ( Vec<Node>, String, Option<String> )>,
18
+
pub loaded_tabs: HashMap<String, (Vec<Node>, String, Option<String>, bool)>,
12
19
13
20
#[serde(default)]
14
-
pub hide_editor_on_start: bool
21
+
pub hide_editor_on_start: bool,
15
22
}
16
23
17
24
pub struct Config {
···
33
40
34
41
let json: ConfigValues = serde_json::from_str(&json_string).unwrap();
35
42
36
-
dbg!(&json);
37
-
38
43
Config {
39
44
store: Mutex::new(json),
40
45
path: path,
···
42
47
}
43
48
44
49
pub fn save(&self) {
45
-
let dat = serde_json::to_string(&self.store.lock().unwrap().clone()).unwrap();
46
-
dbg!(&dat);
50
+
let mut dat = self.store.lock().unwrap();
51
+
52
+
let dat = serde_json::to_string(&*dat).unwrap();
53
+
54
+
let file = File::create(&self.path).unwrap();
55
+
let mut encoder = GzEncoder::new(file, Compression::default());
56
+
57
+
encoder.write_all(dat.as_bytes()).unwrap();
58
+
encoder.finish().unwrap();
59
+
}
60
+
61
+
pub fn save_prelocked(&self, mut dat: MutexGuard<'_, ConfigValues>) {
62
+
let dat = serde_json::to_string(&*dat).unwrap();
47
63
48
64
let file = File::create(&self.path).unwrap();
49
65
let mut encoder = GzEncoder::new(file, Compression::default());
+2
-1
src-tauri/src/utils/mod.rs
+2
-1
src-tauri/src/utils/mod.rs
+315
src-tauri/src/utils/vrchat_builtin_parameters.rs
+315
src-tauri/src/utils/vrchat_builtin_parameters.rs
···
1
+
use crate::{osc::OSCMessage, structs::parameter_types::ParameterType};
2
+
3
+
pub fn get_read_addresses() -> Vec<OSCMessage>{
4
+
vec![
5
+
// Avatar Parameters
6
+
OSCMessage {
7
+
address: "/avatar/change".into(),
8
+
values: vec![ ParameterType::String("".into()) ]
9
+
},
10
+
11
+
OSCMessage {
12
+
address: "/avatar/parameters/VRCEmote".into(),
13
+
values: vec![ ParameterType::Int(0) ]
14
+
},
15
+
OSCMessage {
16
+
address: "/avatar/parameters/VRCFaceBlendV".into(),
17
+
values: vec![ ParameterType::Float(0.0) ]
18
+
},
19
+
OSCMessage {
20
+
address: "/avatar/parameters/VRCFaceBlendH".into(),
21
+
values: vec![ ParameterType::Float(0.0) ]
22
+
},
23
+
OSCMessage {
24
+
address: "/avatar/parameters/PreviewMode".into(),
25
+
values: vec![ ParameterType::Int(0) ]
26
+
},
27
+
OSCMessage {
28
+
address: "/avatar/parameters/IsAnimatorEnabled".into(),
29
+
values: vec![ ParameterType::Boolean(true) ]
30
+
},
31
+
32
+
OSCMessage {
33
+
address: "/avatar/parameters/GestureRightWeight".into(),
34
+
values: vec![ ParameterType::Float(0.0) ]
35
+
},
36
+
OSCMessage {
37
+
address: "/avatar/parameters/GestureLeftWeight".into(),
38
+
values: vec![ ParameterType::Float(0.0) ]
39
+
},
40
+
OSCMessage {
41
+
address: "/avatar/parameters/GestureRight".into(),
42
+
values: vec![ ParameterType::Int(0) ]
43
+
},
44
+
OSCMessage {
45
+
address: "/avatar/parameters/GestureLeft".into(),
46
+
values: vec![ ParameterType::Int(0) ]
47
+
},
48
+
49
+
OSCMessage {
50
+
address: "/avatar/parameters/ScaleModified".into(),
51
+
values: vec![ ParameterType::Boolean(true) ]
52
+
},
53
+
OSCMessage {
54
+
address: "/avatar/parameters/ScaleFactor".into(),
55
+
values: vec![ ParameterType::Float(1.0) ]
56
+
},
57
+
OSCMessage {
58
+
address: "/avatar/parameters/ScaleFactorInverse".into(),
59
+
values: vec![ ParameterType::Float(1.0) ]
60
+
},
61
+
OSCMessage {
62
+
address: "/avatar/parameters/EyeHeightAsPercent".into(),
63
+
values: vec![ ParameterType::Float(1.0) ]
64
+
},
65
+
66
+
OSCMessage {
67
+
address: "/avatar/parameters/Viseme".into(),
68
+
values: vec![ ParameterType::Int(0) ]
69
+
},
70
+
OSCMessage {
71
+
address: "/avatar/parameters/Voice".into(),
72
+
values: vec![ ParameterType::Float(0.0) ]
73
+
},
74
+
OSCMessage {
75
+
address: "/avatar/parameters/Earmuffs".into(),
76
+
values: vec![ ParameterType::Boolean(true) ]
77
+
},
78
+
OSCMessage {
79
+
address: "/avatar/parameters/MuteSelf".into(),
80
+
values: vec![ ParameterType::Boolean(true) ]
81
+
},
82
+
83
+
OSCMessage {
84
+
address: "/avatar/parameters/AFK".into(),
85
+
values: vec![ ParameterType::Boolean(true) ]
86
+
},
87
+
OSCMessage {
88
+
address: "/avatar/parameters/InStation".into(),
89
+
values: vec![ ParameterType::Boolean(true) ]
90
+
},
91
+
OSCMessage {
92
+
address: "/avatar/parameters/Seated".into(),
93
+
values: vec![ ParameterType::Boolean(true) ]
94
+
},
95
+
OSCMessage {
96
+
address: "/avatar/parameters/VRMode".into(),
97
+
values: vec![ ParameterType::Int(0) ]
98
+
},
99
+
OSCMessage {
100
+
address: "/avatar/parameters/TrackingType".into(),
101
+
values: vec![ ParameterType::Int(0) ]
102
+
},
103
+
104
+
OSCMessage {
105
+
address: "/avatar/parameters/Grounded".into(),
106
+
values: vec![ ParameterType::Boolean(true) ]
107
+
},
108
+
OSCMessage {
109
+
address: "/avatar/parameters/Upright".into(),
110
+
values: vec![ ParameterType::Float(1.0) ]
111
+
},
112
+
OSCMessage {
113
+
address: "/avatar/parameters/AngularY".into(),
114
+
values: vec![ ParameterType::Float(1.0) ]
115
+
},
116
+
OSCMessage {
117
+
address: "/avatar/parameters/VelocityX".into(),
118
+
values: vec![ ParameterType::Float(1.0) ]
119
+
},
120
+
OSCMessage {
121
+
address: "/avatar/parameters/VelocityY".into(),
122
+
values: vec![ ParameterType::Float(1.0) ]
123
+
},
124
+
OSCMessage {
125
+
address: "/avatar/parameters/VelocityZ".into(),
126
+
values: vec![ ParameterType::Float(1.0) ]
127
+
},
128
+
OSCMessage {
129
+
address: "/avatar/parameters/VelocityMagnitude".into(),
130
+
values: vec![ ParameterType::Float(1.0) ]
131
+
},
132
+
133
+
// User Camera
134
+
OSCMessage {
135
+
address: "/usercamera/Mode".into(),
136
+
values: vec![ ParameterType::Int(0) ]
137
+
},
138
+
OSCMessage {
139
+
address: "/usercamera/Pose".into(),
140
+
values: vec![
141
+
ParameterType::Float(0.0),
142
+
ParameterType::Float(0.0),
143
+
ParameterType::Float(0.0),
144
+
145
+
ParameterType::Float(0.0),
146
+
ParameterType::Float(0.0),
147
+
ParameterType::Float(0.0)
148
+
]
149
+
},
150
+
151
+
OSCMessage {
152
+
address: "/usercamera/ShowUIInCamera".into(),
153
+
values: vec![ ParameterType::Boolean(true) ]
154
+
},
155
+
OSCMessage {
156
+
address: "/usercamera/LocalPlayer".into(),
157
+
values: vec![ ParameterType::Boolean(true) ]
158
+
},
159
+
OSCMessage {
160
+
address: "/usercamera/RemotePlayer".into(),
161
+
values: vec![ ParameterType::Boolean(true) ]
162
+
},
163
+
OSCMessage {
164
+
address: "/usercamera/Environment".into(),
165
+
values: vec![ ParameterType::Boolean(true) ]
166
+
},
167
+
OSCMessage {
168
+
address: "/usercamera/GreenScreen".into(),
169
+
values: vec![ ParameterType::Boolean(true) ]
170
+
},
171
+
OSCMessage {
172
+
address: "/usercamera/Lock".into(),
173
+
values: vec![ ParameterType::Boolean(true) ]
174
+
},
175
+
OSCMessage {
176
+
address: "/usercamera/SmoothMovement".into(),
177
+
values: vec![ ParameterType::Boolean(true) ]
178
+
},
179
+
OSCMessage {
180
+
address: "/usercamera/LookAtMe".into(),
181
+
values: vec![ ParameterType::Boolean(true) ]
182
+
},
183
+
OSCMessage {
184
+
address: "/usercamera/AutoLevelRoll".into(),
185
+
values: vec![ ParameterType::Boolean(true) ]
186
+
},
187
+
OSCMessage {
188
+
address: "/usercamera/AutoLevelPitch".into(),
189
+
values: vec![ ParameterType::Boolean(true) ]
190
+
},
191
+
OSCMessage {
192
+
address: "/usercamera/Flying".into(),
193
+
values: vec![ ParameterType::Boolean(true) ]
194
+
},
195
+
OSCMessage {
196
+
address: "/usercamera/TriggerTakesPhotos".into(),
197
+
values: vec![ ParameterType::Boolean(true) ]
198
+
},
199
+
OSCMessage {
200
+
address: "/usercamera/DollyPathsStayVisible".into(),
201
+
values: vec![ ParameterType::Boolean(true) ]
202
+
},
203
+
OSCMessage {
204
+
address: "/usercamera/AudioFromCamera".into(),
205
+
values: vec![ ParameterType::Boolean(true) ]
206
+
},
207
+
OSCMessage {
208
+
address: "/usercamera/ShowFocus".into(),
209
+
values: vec![ ParameterType::Boolean(true) ]
210
+
},
211
+
OSCMessage {
212
+
address: "/usercamera/Streaming".into(),
213
+
values: vec![ ParameterType::Boolean(true) ]
214
+
},
215
+
OSCMessage {
216
+
address: "/usercamera/RollWhileFlying".into(),
217
+
values: vec![ ParameterType::Boolean(true) ]
218
+
},
219
+
OSCMessage {
220
+
address: "/usercamera/OrientationIsLandscape".into(),
221
+
values: vec![ ParameterType::Boolean(true) ]
222
+
},
223
+
224
+
OSCMessage {
225
+
address: "/usercamera/Zoom".into(),
226
+
values: vec![ ParameterType::Float(0.0) ]
227
+
},
228
+
OSCMessage {
229
+
address: "/usercamera/Exposure".into(),
230
+
values: vec![ ParameterType::Float(0.0) ]
231
+
},
232
+
OSCMessage {
233
+
address: "/usercamera/Aperture".into(),
234
+
values: vec![ ParameterType::Float(0.0) ]
235
+
},
236
+
OSCMessage {
237
+
address: "/usercamera/Hue".into(),
238
+
values: vec![ ParameterType::Float(0.0) ]
239
+
},
240
+
OSCMessage {
241
+
address: "/usercamera/Saturation".into(),
242
+
values: vec![ ParameterType::Float(0.0) ]
243
+
},
244
+
OSCMessage {
245
+
address: "/usercamera/Lightness".into(),
246
+
values: vec![ ParameterType::Float(0.0) ]
247
+
},
248
+
OSCMessage {
249
+
address: "/usercamera/LookAtMeXOffset".into(),
250
+
values: vec![ ParameterType::Float(0.0) ]
251
+
},
252
+
OSCMessage {
253
+
address: "/usercamera/LookAtMeYOffset".into(),
254
+
values: vec![ ParameterType::Float(0.0) ]
255
+
},
256
+
OSCMessage {
257
+
address: "/usercamera/FlySpeed".into(),
258
+
values: vec![ ParameterType::Float(0.0) ]
259
+
},
260
+
OSCMessage {
261
+
address: "/usercamera/TurnSpeed".into(),
262
+
values: vec![ ParameterType::Float(0.0) ]
263
+
},
264
+
OSCMessage {
265
+
address: "/usercamera/SmoothStrength".into(),
266
+
values: vec![ ParameterType::Float(0.0) ]
267
+
},
268
+
OSCMessage {
269
+
address: "/usercamera/PhotoRate".into(),
270
+
values: vec![ ParameterType::Float(0.0) ]
271
+
},
272
+
OSCMessage {
273
+
address: "/usercamera/Duration".into(),
274
+
values: vec![ ParameterType::Float(0.0) ]
275
+
},
276
+
277
+
// Trackers
278
+
OSCMessage {
279
+
address: "/tracking/vrsystem/head/pose".into(),
280
+
values: vec![
281
+
ParameterType::Float(0.0),
282
+
ParameterType::Float(0.0),
283
+
ParameterType::Float(0.0),
284
+
285
+
ParameterType::Float(0.0),
286
+
ParameterType::Float(0.0),
287
+
ParameterType::Float(0.0),
288
+
]
289
+
},
290
+
OSCMessage {
291
+
address: "/tracking/vrsystem/rightwrist/pose".into(),
292
+
values: vec![
293
+
ParameterType::Float(0.0),
294
+
ParameterType::Float(0.0),
295
+
ParameterType::Float(0.0),
296
+
297
+
ParameterType::Float(0.0),
298
+
ParameterType::Float(0.0),
299
+
ParameterType::Float(0.0),
300
+
]
301
+
},
302
+
OSCMessage {
303
+
address: "/tracking/vrsystem/leftwrist/pose".into(),
304
+
values: vec![
305
+
ParameterType::Float(0.0),
306
+
ParameterType::Float(0.0),
307
+
ParameterType::Float(0.0),
308
+
309
+
ParameterType::Float(0.0),
310
+
ParameterType::Float(0.0),
311
+
ParameterType::Float(0.0),
312
+
]
313
+
},
314
+
]
315
+
}