feat: some moving around

This commit is contained in:
max_richter 2024-04-15 22:13:43 +02:00
parent 0254bc1ae5
commit dec205b234
86 changed files with 505 additions and 409 deletions

50
Cargo.lock generated
View File

@ -7,11 +7,13 @@ name = "array"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"console_error_panic_hook", "console_error_panic_hook",
"macros",
"serde", "serde",
"serde-wasm-bindgen", "serde-wasm-bindgen",
"utils", "utils",
"wasm-bindgen", "wasm-bindgen",
"wasm-bindgen-test", "wasm-bindgen-test",
"web-sys",
] ]
[[package]] [[package]]
@ -41,6 +43,7 @@ name = "float"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"console_error_panic_hook", "console_error_panic_hook",
"macros",
"serde", "serde",
"serde-wasm-bindgen", "serde-wasm-bindgen",
"utils", "utils",
@ -69,11 +72,21 @@ version = "0.4.21"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c"
[[package]]
name = "macros"
version = "0.1.0"
dependencies = [
"quote",
"serde_json",
"syn 1.0.109",
]
[[package]] [[package]]
name = "math" name = "math"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"console_error_panic_hook", "console_error_panic_hook",
"macros",
"serde", "serde",
"serde-wasm-bindgen", "serde-wasm-bindgen",
"utils", "utils",
@ -172,7 +185,7 @@ checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn 2.0.59",
] ]
[[package]] [[package]]
@ -187,15 +200,42 @@ dependencies = [
] ]
[[package]] [[package]]
name = "sum" name = "stem"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"console_error_panic_hook", "console_error_panic_hook",
"macros",
"serde", "serde",
"serde-wasm-bindgen", "serde-wasm-bindgen",
"utils", "utils",
"wasm-bindgen", "wasm-bindgen",
"wasm-bindgen-test", "wasm-bindgen-test",
"web-sys",
]
[[package]]
name = "sum"
version = "0.1.0"
dependencies = [
"console_error_panic_hook",
"macros",
"serde",
"serde-wasm-bindgen",
"utils",
"wasm-bindgen",
"wasm-bindgen-test",
"web-sys",
]
[[package]]
name = "syn"
version = "1.0.109"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
] ]
[[package]] [[package]]
@ -258,7 +298,7 @@ dependencies = [
"once_cell", "once_cell",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn 2.0.59",
"wasm-bindgen-shared", "wasm-bindgen-shared",
] ]
@ -292,7 +332,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn 2.0.59",
"wasm-bindgen-backend", "wasm-bindgen-backend",
"wasm-bindgen-shared", "wasm-bindgen-shared",
] ]
@ -325,7 +365,7 @@ checksum = "b7f89739351a2e03cb94beb799d47fb2cac01759b40ec441f7de39b00cbf7ef0"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn 2.0.59",
] ]
[[package]] [[package]]

View File

@ -1,6 +1,6 @@
[workspace] [workspace]
resolver = "2" resolver = "2"
members = ["nodes/max/plantarium/*", "packages/utils"] members = ["nodes/max/plantarium/*", "packages/macros", "packages/utils"]
[profile.release] [profile.release]

View File

@ -1,9 +1,10 @@
import { writable, type Writable } from "svelte/store"; import { get, writable, type Writable } from "svelte/store";
import type { Graph, Node, Edge, Socket, NodeRegistry, } from "@nodes/types"; import type { Graph, Node, Edge, Socket, NodeRegistry, } from "@nodes/types";
import { HistoryManager } from "./history-manager.js" import { HistoryManager } from "./history-manager.js"
import EventEmitter from "./helpers/EventEmitter.js"; import EventEmitter from "./helpers/EventEmitter.js";
import throttle from "./helpers/throttle.js"; import throttle from "./helpers/throttle.js";
import { createLogger } from "./helpers/index.js"; import { createLogger } from "./helpers/index.js";
import type { NodeInput } from "@nodes/types";
const logger = createLogger("graph-manager"); const logger = createLogger("graph-manager");
@ -17,6 +18,8 @@ export class GraphManager extends EventEmitter<{ "save": Graph, "result": any }>
private _nodes: Map<number, Node> = new Map(); private _nodes: Map<number, Node> = new Map();
nodes: Writable<Map<number, Node>> = writable(new Map()); nodes: Writable<Map<number, Node>> = writable(new Map());
settingTypes: NodeInput[] = [];
settings: Writable<Record<string, any>> = writable({});
private _edges: Edge[] = []; private _edges: Edge[] = [];
edges: Writable<Edge[]> = writable([]); edges: Writable<Edge[]> = writable([]);
@ -51,7 +54,8 @@ export class GraphManager extends EventEmitter<{ "save": Graph, "result": any }>
props: node.props, props: node.props,
})) as Node[]; })) as Node[];
const edges = this._edges.map(edge => [edge[0].id, edge[1], edge[2].id, edge[3]]) as Graph["edges"]; const edges = this._edges.map(edge => [edge[0].id, edge[1], edge[2].id, edge[3]]) as Graph["edges"];
return { id: this.graph.id, nodes, edges }; const settings = get(this.settings);
return { id: this.graph.id, settings, nodes, edges };
} }
execute() { } execute() { }
@ -141,6 +145,12 @@ export class GraphManager extends EventEmitter<{ "save": Graph, "result": any }>
this.status.set("loading"); this.status.set("loading");
this.id.set(graph.id); this.id.set(graph.id);
if (graph.settings) {
this.settings.set(graph.settings);
} else {
this.settings.set({});
}
const nodeIds = Array.from(new Set([...graph.nodes.map(n => n.type)])); const nodeIds = Array.from(new Set([...graph.nodes.map(n => n.type)]));
await this.nodeRegistry.load(nodeIds); await this.nodeRegistry.load(nodeIds);
@ -155,6 +165,20 @@ export class GraphManager extends EventEmitter<{ "save": Graph, "result": any }>
node.tmp.type = nodeType; node.tmp.type = nodeType;
} }
let settings: Record<string, NodeInput> = {};
const types = this.getNodeTypes();
for (const type of types) {
if (type.inputs) {
for (const key in type.inputs) {
let settingId = type.inputs[key].setting;
if (settingId) {
settings[settingId] = type.inputs[key];
}
}
}
}
console.log(settings);
this.history.reset(); this.history.reset();
this._init(this.graph); this._init(this.graph);

View File

@ -106,7 +106,12 @@
if (!node?.inputs) { if (!node?.inputs) {
return 5; return 5;
} }
const height = 5 + 10 * Object.keys(node.inputs).filter((i) => i !== 'seed').length; const height =
5 +
10 *
Object.keys(node.inputs)
.filter((i) => i !== 'seed')
.filter((p) => node?.inputs && !('setting' in node?.inputs?.[p])).length;
nodeHeightCache[nodeTypeId] = height; nodeHeightCache[nodeTypeId] = height;
return height; return height;
} }

View File

@ -22,7 +22,9 @@
const type = node?.tmp?.type; const type = node?.tmp?.type;
const parameters = Object.entries(type?.inputs || {}).filter((p) => p[1].type !== 'seed'); const parameters = Object.entries(type?.inputs || {})
.filter((p) => p[1].type !== 'seed')
.filter((p) => !('setting' in p[1]));
let ref: HTMLDivElement; let ref: HTMLDivElement;
let meshRef: Mesh; let meshRef: Mesh;

View File

@ -0,0 +1,25 @@
<script lang="ts">
import type { Node, NodeInput } from "@nodes/types";
import { getGraphManager } from "../graph/context.js";
import { Input } from "@nodes/ui";
export let node: Node;
export let input: NodeInput;
export let id: string;
export let label: string | undefined;
const graph = getGraphManager();
let value = node?.props?.[id] ?? input.value;
let elementId = Math.random().toString(36).substring(7);
$: if (node?.props?.[id] !== value) {
node.props = { ...node.props, [id]: value };
graph.save();
graph.execute();
}
</script>
<label for="input-{elementId}">{label || id}</label>
<Input id="input-{elementId}" {input} bind:value />

View File

@ -0,0 +1,11 @@
import { test, expect } from 'vitest';
import fastHash from './fastHash';
test('Hashes dont clash', () => {
const hashA = fastHash('abcdef');
const hashB = fastHash('abcde');
const hashC = fastHash('abcde');
expect(hashA).not.toEqual(hashB);
expect(hashB).toEqual(hashC);
});

View File

@ -0,0 +1,14 @@
// Shamelessly copied from
// https://stackoverflow.com/a/8831937
export default function (input: string) {
if (input.length === 0) return 0;
let hash = 0;
for (let i = 0; i < input.length; i++) {
hash = (hash << 5) - hash + input.charCodeAt(i);
hash = hash & hash;
}
return hash;
}

View File

@ -1,12 +1,23 @@
import type { Graph, NodeRegistry, NodeType, RuntimeExecutor } from "@nodes/types"; import type { Graph, NodeRegistry, NodeType, RuntimeExecutor } from "@nodes/types";
import { encodeFloat } from "./helpers/encode"; import { encodeFloat } from "./helpers/encode";
import { concat_encoded, encode } from "./helpers/flat_tree"; import { concat_encoded, encode } from "./helpers/flat_tree";
import fastHash from "./helpers/fastHash";
async function hashIntArray(arr: Int32Array): Promise<string> {
const hashBuffer = await crypto.subtle.digest('SHA-256', arr.buffer);
const hashArray = Array.from(new Uint8Array(hashBuffer));
const hashHex = hashArray.map(byte => byte.toString(16).padStart(2, '0')).join('');
return hashHex;
}
export class MemoryRuntimeExecutor implements RuntimeExecutor { export class MemoryRuntimeExecutor implements RuntimeExecutor {
private typeMap: Map<string, NodeType> = new Map(); private typeMap: Map<string, NodeType> = new Map();
private cache: Record<string, { eol: number, value: any }> = {};
constructor(private registry: NodeRegistry) { } constructor(private registry: NodeRegistry) { }
private getNodeTypes(graph: Graph) { private getNodeTypes(graph: Graph) {
@ -111,6 +122,8 @@ export class MemoryRuntimeExecutor implements RuntimeExecutor {
// here we store the intermediate results of the nodes // here we store the intermediate results of the nodes
const results: Record<string, string | boolean | number> = {}; const results: Record<string, string | boolean | number> = {};
console.log(this.cache);
for (const node of sortedNodes) { for (const node of sortedNodes) {
const node_type = this.typeMap.get(node.type)!; const node_type = this.typeMap.get(node.type)!;
@ -139,24 +152,65 @@ export class MemoryRuntimeExecutor implements RuntimeExecutor {
} }
console.log(" ");
console.log("--> EXECUTING NODE " + node_type.id, node.id);
// execute the node and store the result // execute the node and store the result
try { try {
const a0 = performance.now();
const node_inputs = Object.entries(inputs); const node_inputs = Object.entries(inputs);
const cacheKey = `${node.id}/${fastHash(node_inputs.map(([_, value]: [string, any]) => {
if (value instanceof Int32Array) {
return hashIntArray(value);
}
console.log(value);
return `${value}`
}).join("/"))}`;
const a1 = performance.now();
console.log(`${a1 - a0}ms hashed inputs: ${node.id} -> ${cacheKey}`);
if (this.cache[cacheKey] && this.cache[cacheKey].eol > Date.now()) {
results[node.id] = this.cache[cacheKey].value;
console.log(`Using cached value`);
continue;
}
const transformed_inputs = node_inputs.map(([key, value]: [string, any]) => { const transformed_inputs = node_inputs.map(([key, value]: [string, any]) => {
const input_type = node_type.inputs?.[key]!; const input_type = node_type.inputs?.[key]!;
if (value instanceof Int32Array) { if (value instanceof Int32Array) {
return [...value.slice(0, value.length)]; let _v = new Array(value.length);
for (let i = 0; i < value.length; i++) {
_v[i] = value[i];
}
return _v;
} }
if (input_type.type === "float") { if (input_type.type === "float") {
return encode(encodeFloat(value as number)); return encode(encodeFloat(value as number));
} }
return value; return value;
}); });
const a2 = performance.now();
console.log(`${a2 - a1}ms TRANSFORMED_INPUTS`);
const _inputs = concat_encoded(transformed_inputs); const _inputs = concat_encoded(transformed_inputs);
// console.log(`Executing node ${node_type.id || node.id}`, { _inputs, inputs, node_type }); const a3 = performance.now();
results[node.id] = node_type.execute(_inputs) as number; results[node.id] = node_type.execute(_inputs) as number;
// console.log("--> result", results[node.id]); const duration = performance.now() - a3;
if (duration > 5) {
this.cache[cacheKey] = { eol: Date.now() + 10_000, value: results[node.id] };
console.log(`Caching for 10 seconds`);
}
console.log(`${duration}ms Executed`);
const a4 = performance.now();
console.log(`${a4 - a0}ms e2e duration`);
} catch (e) { } catch (e) {
console.error(`Error executing node ${node_type.id || node.id}`, e); console.error(`Error executing node ${node_type.id || node.id}`, e);
} }

View File

@ -1,6 +1,6 @@
<script lang="ts"> <script lang="ts">
import Grid from "$lib/grid"; import Grid from "$lib/grid";
import GraphInterface from "@nodes/graph-interface"; import GraphInterface from "$lib/graph-interface";
import { MemoryRuntimeExecutor } from "$lib/runtime-executor"; import { MemoryRuntimeExecutor } from "$lib/runtime-executor";
import { RemoteNodeRegistry } from "$lib/node-registry"; import { RemoteNodeRegistry } from "$lib/node-registry";
import * as templates from "$lib/graph-templates"; import * as templates from "$lib/graph-templates";
@ -25,7 +25,8 @@
let a = performance.now(); let a = performance.now();
let _res: any = runtimeExecutor.execute(event.detail); let _res: any = runtimeExecutor.execute(event.detail);
if (_res instanceof Int32Array) { if (_res instanceof Int32Array) {
res = decodeFloat(_res[0], _res[1]); const f = decodeFloat(_res[0], _res[1]);
res = Math.round(f * 100_000) / 100_000;
} else { } else {
res = _res; res = _res;
} }

View File

@ -18,6 +18,8 @@ wasm-bindgen = "0.2.84"
# all the `std::fmt` and `std::panicking` infrastructure, so isn't great for # all the `std::fmt` and `std::panicking` infrastructure, so isn't great for
# code size when deploying. # code size when deploying.
utils = { version = "0.1.0", path = "../../../../packages/utils" } utils = { version = "0.1.0", path = "../../../../packages/utils" }
macros = { version = "0.1.0", path = "../../../../packages/macros" }
web-sys = { version = "0.3.69", features = ["console"] }
serde = { version = "1.0", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }
serde-wasm-bindgen = "0.4" serde-wasm-bindgen = "0.4"
console_error_panic_hook = { version = "0.1.7", optional = true } console_error_panic_hook = { version = "0.1.7", optional = true }

View File

@ -0,0 +1,10 @@
{
"value": {
"type": "float",
"value": 4.2
},
"amount": {
"type": "integer",
"value": 2
}
}

View File

@ -1,39 +1,33 @@
use macros::generate_input_types_file;
use utils::{evaluate_args, generate_outputs, get_args};
use wasm_bindgen::prelude::*; use wasm_bindgen::prelude::*;
// use web_sys::console;
// lifted from the `console_log` example generate_outputs!(["float"]);
#[wasm_bindgen]
extern "C" { generate_input_types_file!("src/input.json");
#[wasm_bindgen(js_namespace = console)]
fn log(s: &str);
}
#[wasm_bindgen] #[wasm_bindgen]
pub fn get_outputs() -> Vec<String> { pub fn execute(input: &[i32]) -> Vec<i32> {
vec!["float".to_string()] utils::set_panic_hook();
}
#[wasm_bindgen] let args = get_args(input);
pub fn get_input_types() -> String {
r#"{
"length": { "type": "float", "value": 2 }
}"#
.to_string()
}
#[rustfmt::skip] let value_encoded = evaluate_args(args[0]);
#[wasm_bindgen] // let value = decode_float(value_encoded[0], value_encoded[1]);
pub fn execute(var_length: i32) -> Vec<f64> { let length = args[1];
let length = var_length;//evaluate_parameters(var_length); // console::log_1(&format!("WASM(array_node): args {:?} ", args).into());
// console::log_1(&format!("WASM(array_node): value: {:?} length: {:?}", value, length).into());
// construct array of length // construct array of length
let mut res = Vec::new(); let mut res: Vec<i32> = Vec::with_capacity(length[0] as usize * 2 + 2);
for _ in 0..length as usize { res.push(0);
res.push(2.0); res.push((length[0]) * 2 + 2);
for _ in 0..length[0] as usize {
res.push(value_encoded[0]);
res.push(value_encoded[1])
} }
log("executing array");
res res
} }

View File

@ -18,6 +18,7 @@ wasm-bindgen = "0.2.84"
# all the `std::fmt` and `std::panicking` infrastructure, so isn't great for # all the `std::fmt` and `std::panicking` infrastructure, so isn't great for
# code size when deploying. # code size when deploying.
utils = { version = "0.1.0", path = "../../../../packages/utils" } utils = { version = "0.1.0", path = "../../../../packages/utils" }
macros = { version = "0.1.0", path = "../../../../packages/macros" }
serde = { version = "1.0", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }
serde-wasm-bindgen = "0.4" serde-wasm-bindgen = "0.4"
console_error_panic_hook = { version = "0.1.7", optional = true } console_error_panic_hook = { version = "0.1.7", optional = true }

View File

@ -0,0 +1,7 @@
{
"value": {
"type": "float",
"value": 0.1,
"internal": true
}
}

View File

@ -1,17 +1,9 @@
use macros::generate_input_types_file;
use utils::generate_outputs;
use wasm_bindgen::prelude::*; use wasm_bindgen::prelude::*;
#[wasm_bindgen] generate_outputs!(["float"]);
pub fn get_outputs() -> Vec<String> { generate_input_types_file!("src/input.json");
vec!["float".to_string()]
}
#[wasm_bindgen]
pub fn get_input_types() -> String {
r#"{
"value": { "type": "float", "value": 0.1, "internal": true }
}"#
.to_string()
}
#[wasm_bindgen] #[wasm_bindgen]
pub fn execute(args: &[i32]) -> Vec<i32> { pub fn execute(args: &[i32]) -> Vec<i32> {

View File

@ -16,6 +16,7 @@ serde = { version = "1.0", features = ["derive"] }
serde-wasm-bindgen = "0.4" serde-wasm-bindgen = "0.4"
console_error_panic_hook = { version = "0.1.7", optional = true } console_error_panic_hook = { version = "0.1.7", optional = true }
utils = { version = "0.1.0", path = "../../../../packages/utils" } utils = { version = "0.1.0", path = "../../../../packages/utils" }
macros = { version = "0.1.0", path = "../../../../packages/macros" }
web-sys = { version = "0.3.69", features = ["console"] } web-sys = { version = "0.3.69", features = ["console"] }
[dev-dependencies] [dev-dependencies]

View File

@ -0,0 +1,27 @@
{
"op_type": {
"label": "type",
"type": "select",
"labels": [
"add",
"subtract",
"multiply",
"divide"
],
"internal": true,
"value": 0
},
"a": {
"type": "float",
"value": 2
},
"b": {
"type": "float",
"value": 2
},
"clip": {
"type": "boolean",
"value": 0,
"setting": "math.clipping"
}
}

View File

@ -1,29 +1,13 @@
use macros::generate_input_types_file;
use utils::generate_outputs;
use wasm_bindgen::prelude::*; use wasm_bindgen::prelude::*;
// use web_sys::console;
#[wasm_bindgen] generate_outputs!(["float"]);
pub fn get_outputs() -> Vec<String> {
vec!["float".to_string()]
}
#[wasm_bindgen] generate_input_types_file!("src/input.json");
pub fn get_input_types() -> String {
r#"{
"op_type": { "label": "type", "type": "select", "labels": ["add", "subtract", "multiply", "divide"], "internal": true, "value": 0 },
"a": { "type": "float", "value": 2 },
"b": { "type": "float", "value": 2 }
}"#.to_string()
}
#[wasm_bindgen] #[wasm_bindgen]
pub fn execute(args: &[i32]) -> Vec<i32> { pub fn execute(args: &[i32]) -> Vec<i32> {
// let d = args
// .iter()
// .map(|&num| num.to_string()) // Convert each integer to a String
// .collect::<Vec<String>>() // Collect all Strings into a Vec
// .join(","); // Join all Strings in the Vec with a dot
// console::log_1(&format!("Math: {:?}", d).into());
let mut result = Vec::with_capacity(args.len() + 3); let mut result = Vec::with_capacity(args.len() + 3);
result.push(0); // encoding the [ bracket result.push(0); // encoding the [ bracket
result.push(args[1] + 1); result.push(args[1] + 1);
@ -31,6 +15,5 @@ pub fn execute(args: &[i32]) -> Vec<i32> {
result.extend_from_slice(&args[2..]); result.extend_from_slice(&args[2..]);
result.push(1); result.push(1);
result.push(1); // closing bracket result.push(1); // closing bracket
result result
} }

View File

@ -17,7 +17,7 @@ pub fn get_input_types() -> String {
} }
#[wasm_bindgen] #[wasm_bindgen]
pub fn execute(args: &[i32]) -> Vec<i32> { pub fn execute(args: &[i32]) -> Vec<i32> {
// utils::set_panic_hook(); utils::set_panic_hook();
// console::log_1(&format!("WASM(output_node): input: {:?}", args).into()); // console::log_1(&format!("WASM(output_node): input: {:?}", args).into());

6
nodes/max/plantarium/stem/.gitignore vendored Normal file
View File

@ -0,0 +1,6 @@
/target
**/*.rs.bk
Cargo.lock
bin/
pkg/
wasm-pack.log

View File

@ -0,0 +1,23 @@
[package]
name = "stem"
version = "0.1.0"
authors = ["Max Richter <jim-x@web.de>"]
edition = "2018"
[lib]
crate-type = ["cdylib", "rlib"]
[features]
default = ["console_error_panic_hook"]
[dependencies]
wasm-bindgen = "0.2.84"
serde = { version = "1.0", features = ["derive"] }
serde-wasm-bindgen = "0.4"
console_error_panic_hook = { version = "0.1.7", optional = true }
utils = { version = "0.1.0", path = "../../../../packages/utils" }
macros = { version = "0.1.0", path = "../../../../packages/macros" }
web-sys = { version = "0.3.69", features = ["console"] }
[dev-dependencies]
wasm-bindgen-test = "0.3.34"

View File

@ -0,0 +1,5 @@
{
"scripts": {
"build": "wasm-pack build --release --out-name index --no-default-features"
}
}

View File

@ -0,0 +1,27 @@
{
"op_type": {
"label": "type",
"type": "select",
"labels": [
"add",
"subtract",
"multiply",
"divide"
],
"internal": true,
"value": 0
},
"a": {
"type": "float",
"value": 2
},
"b": {
"type": "float",
"value": 2
},
"clip": {
"type": "boolean",
"value": 0,
"setting": "math.clipping"
}
}

View File

@ -0,0 +1,20 @@
use macros::generate_input_types_file;
use utils::generate_outputs;
use wasm_bindgen::prelude::*;
generate_outputs!(["stem"]);
generate_input_types_file!("src/input.json");
#[wasm_bindgen]
pub fn execute(args: &[i32]) -> Vec<i32> {
let mut result = Vec::with_capacity(args.len() + 3);
result.push(0); // encoding the [ bracket
result.push(args[1] + 1);
result.push(0); // adding the node-type, math: 0
result.extend_from_slice(&args[2..]);
result.push(1);
result.push(1); // closing bracket
result
}

View File

@ -0,0 +1,13 @@
//! Test suite for the Web and headless browsers.
#![cfg(target_arch = "wasm32")]
extern crate wasm_bindgen_test;
use wasm_bindgen_test::*;
wasm_bindgen_test_configure!(run_in_browser);
#[wasm_bindgen_test]
fn pass() {
assert_eq!(1 + 1, 2);
}

View File

@ -21,6 +21,8 @@ utils = { version = "0.1.0", path = "../../../../packages/utils" }
serde = { version = "1.0", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }
serde-wasm-bindgen = "0.4" serde-wasm-bindgen = "0.4"
console_error_panic_hook = { version = "0.1.7", optional = true } console_error_panic_hook = { version = "0.1.7", optional = true }
macros = { version = "0.1.0", path = "../../../../packages/macros" }
web-sys = { version = "0.3.69", features = ["console"] }
[dev-dependencies] [dev-dependencies]
wasm-bindgen-test = "0.3.34" wasm-bindgen-test = "0.3.34"

View File

@ -0,0 +1,7 @@
{
"array": {
"type": "float",
"value": 2,
"external": true
}
}

View File

@ -1,29 +1,31 @@
use macros::generate_input_types_file;
use utils::{decode_float, encode_float, generate_outputs};
use wasm_bindgen::prelude::*; use wasm_bindgen::prelude::*;
// lifted from the `console_log` example generate_outputs!(["float"]);
#[wasm_bindgen]
extern "C" { generate_input_types_file!("src/input.json");
#[wasm_bindgen(js_namespace = console)]
fn log(s: &str);
}
#[wasm_bindgen] #[wasm_bindgen]
pub fn get_outputs() -> Vec<String> { pub fn execute(input: &[i32]) -> Vec<i32> {
vec!["float".to_string()] utils::set_panic_hook();
}
#[wasm_bindgen] let mut sum = 0.0;
pub fn get_input_types() -> String {
r#"{
"array": { "type": "float", "value": 2, "external": true }
}"#
.to_string()
}
#[rustfmt::skip] // console::log_1(&format!("WASM(sum_node): args: {:?}", input).into());
#[wasm_bindgen]
pub fn execute(array: &[i32]) -> Vec<i32> { let length = (input.len() - 2) / 2;
let mut sum = 0;
array.iter().for_each(|x| sum += x); (0..length).for_each(|i| {
vec![1, sum] let mantissa = input[2 + i * 2];
let exponent = input[2 + i * 2 + 1];
// console::log_1(&format!("WASM(sum_node): i: {} sum: {:?}", i, sum).into());
sum += decode_float(mantissa, exponent);
});
let encoded_sum = encode_float(sum);
// console::log_1(&format!("WASM(sum_node): result: {:?}", sum).into());
vec![0, 3, encoded_sum.0, encoded_sum.1]
} }

View File

@ -1,13 +0,0 @@
.DS_Store
node_modules
/build
/.svelte-kit
/package
.env
.env.*
!.env.example
# Ignore files for PNPM, NPM and YARN
pnpm-lock.yaml
package-lock.json
yarn.lock

View File

@ -1,31 +0,0 @@
/** @type { import("eslint").Linter.Config } */
module.exports = {
root: true,
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/recommended',
'plugin:svelte/recommended',
'prettier'
],
parser: '@typescript-eslint/parser',
plugins: ['@typescript-eslint'],
parserOptions: {
sourceType: 'module',
ecmaVersion: 2020,
extraFileExtensions: ['.svelte']
},
env: {
browser: true,
es2017: true,
node: true
},
overrides: [
{
files: ['*.svelte'],
parser: 'svelte-eslint-parser',
parserOptions: {
parser: '@typescript-eslint/parser'
}
}
]
};

View File

@ -1,11 +0,0 @@
.DS_Store
node_modules
/build
/dist
/.svelte-kit
/package
.env
.env.*
!.env.example
vite.config.js.timestamp-*
vite.config.ts.timestamp-*

View File

@ -1 +0,0 @@
engine-strict=true

View File

@ -1,4 +0,0 @@
# Ignore files for PNPM, NPM and YARN
pnpm-lock.yaml
package-lock.json
yarn.lock

View File

@ -1,8 +0,0 @@
{
"useTabs": true,
"singleQuote": true,
"trailingComma": "none",
"printWidth": 100,
"plugins": ["prettier-plugin-svelte"],
"overrides": [{ "files": "*.svelte", "options": { "parser": "svelte" } }]
}

View File

@ -1,58 +0,0 @@
# create-svelte
Everything you need to build a Svelte library, powered by [`create-svelte`](https://github.com/sveltejs/kit/tree/main/packages/create-svelte).
Read more about creating a library [in the docs](https://kit.svelte.dev/docs/packaging).
## Creating a project
If you're seeing this, you've probably already done this step. Congrats!
```bash
# create a new project in the current directory
npm create svelte@latest
# create a new project in my-app
npm create svelte@latest my-app
```
## Developing
Once you've created a project and installed dependencies with `npm install` (or `pnpm install` or `yarn`), start a development server:
```bash
npm run dev
# or start the server and open the app in a new browser tab
npm run dev -- --open
```
Everything inside `src/lib` is part of your library, everything inside `src/routes` can be used as a showcase or preview app.
## Building
To build your library:
```bash
npm run package
```
To create a production version of your showcase app:
```bash
npm run build
```
You can preview the production build with `npm run preview`.
> To deploy your app, you may need to install an [adapter](https://kit.svelte.dev/docs/adapters) for your target environment.
## Publishing
Go into the `package.json` and give your package the desired name through the `"name"` option. Also consider adding a `"license"` field and point it to a `LICENSE` file which you can create from a template (one popular option is the [MIT license](https://opensource.org/license/mit/)).
To publish your library to [npm](https://www.npmjs.com):
```bash
npm publish
```

View File

@ -1,65 +0,0 @@
{
"name": "@nodes/graph-interface",
"version": "0.0.1",
"scripts": {
"dev": "svelte-package --watch",
"build": "vite build && npm run package",
"postbuild": "pnpm run package",
"preview": "vite preview",
"package": "svelte-kit sync && svelte-package && publint",
"prepublishOnly": "npm run package",
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
"lint": "prettier --check . && eslint .",
"format": "prettier --write ."
},
"exports": {
".": {
"types": "./dist/index.d.ts",
"svelte": "./dist/index.js"
}
},
"files": [
"dist",
"!dist/**/*.test.*",
"!dist/**/*.spec.*"
],
"dependencies": {
"@nodes/ui": "link:../ui",
"@nodes/types": "link:../types",
"@threlte/core": "^7.1.0",
"@threlte/extras": "^8.7.5",
"@threlte/flex": "^1.0.1",
"@types/three": "^0.159.0",
"jsondiffpatch": "^0.6.0",
"three": "^0.159.0",
"vite-plugin-glsl": "^1.2.1"
},
"peerDependencies": {
"svelte": "^4.0.0"
},
"devDependencies": {
"@histoire/plugin-svelte": "^0.17.9",
"@sveltejs/adapter-auto": "^3.0.0",
"@sveltejs/kit": "^2.0.0",
"@sveltejs/package": "^2.0.0",
"@sveltejs/vite-plugin-svelte": "^3.0.0",
"@types/eslint": "^8.56.0",
"@typescript-eslint/eslint-plugin": "^7.0.0",
"@typescript-eslint/parser": "^7.0.0",
"eslint": "^8.56.0",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-svelte": "^2.35.1",
"prettier": "^3.1.1",
"prettier-plugin-svelte": "^3.1.2",
"publint": "^0.1.9",
"svelte": "^4.2.7",
"svelte-check": "^3.6.0",
"tslib": "^2.4.1",
"typescript": "^5.0.0",
"vite": "^5.0.11"
},
"svelte": "./dist/index.js",
"types": "./dist/index.d.ts",
"type": "module"
}

View File

@ -1,13 +0,0 @@
// See https://kit.svelte.dev/docs/types#app
// for information about these interfaces
declare global {
namespace App {
// interface Error {}
// interface Locals {}
// interface PageData {}
// interface PageState {}
// interface Platform {}
}
}
export {};

View File

@ -1,12 +0,0 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" href="%sveltekit.assets%/favicon.png" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
%sveltekit.head%
</head>
<body data-sveltekit-preload-data="hover">
<div>%sveltekit.body%</div>
</body>
</html>

View File

@ -1,23 +0,0 @@
<script lang="ts">
import type { Node, NodeInput } from '@nodes/types';
import { getGraphManager } from '../graph/context.js';
import { Input } from '@nodes/ui';
export let node: Node;
export let input: NodeInput;
export let id: string;
export let label: string | undefined;
const graph = getGraphManager();
let value = node?.props?.[id] ?? input.value;
$: if (node?.props?.[id] !== value) {
node.props = { ...node.props, [id]: value };
graph.save();
graph.execute();
}
</script>
<label for="asd">{label || id}</label>
<Input {input} bind:value />

View File

@ -1,3 +0,0 @@
<h1>Welcome to your library project</h1>
<p>Create your package using @sveltejs/package and preview/showcase your work with SvelteKit</p>
<p>Visit <a href="https://kit.svelte.dev">kit.svelte.dev</a> to read the documentation</p>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

View File

@ -1,18 +0,0 @@
import adapter from '@sveltejs/adapter-auto';
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';
/** @type {import('@sveltejs/kit').Config} */
const config = {
// Consult https://kit.svelte.dev/docs/integrations#preprocessors
// for more information about preprocessors
preprocess: vitePreprocess(),
kit: {
// adapter-auto only supports some environments, see https://kit.svelte.dev/docs/adapter-auto for a list.
// If your environment is not supported or you settled on a specific environment, switch out the adapter.
// See https://kit.svelte.dev/docs/adapters for more information about adapters.
adapter: adapter()
}
};
export default config;

View File

@ -1,18 +0,0 @@
{
"extends": "./.svelte-kit/tsconfig.json",
"compilerOptions": {
"allowJs": true,
"checkJs": true,
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true,
"skipLibCheck": true,
"sourceMap": true,
"strict": true,
"module": "NodeNext",
"moduleResolution": "NodeNext",
"types": [
"vite-plugin-glsl/ext"
]
}
}

View File

@ -1,18 +0,0 @@
import { sveltekit } from '@sveltejs/kit/vite';
import glsl from "vite-plugin-glsl";
import { defineConfig } from 'vite';
import { exec } from 'child_process';
const dev = import.meta.env;
export default defineConfig({
plugins: [sveltekit(), glsl(), {
name: 'postbuild-commands', // the name of your custom plugin. Could be anything.
closeBundle: async () => {
return;
// run pnpm run package
exec('pnpm run package', (err, stdout, stderr) => {
console.log(stdout);
});
}
},]
});

View File

@ -0,0 +1,12 @@
[package]
name = "macros"
version = "0.1.0"
edition = "2021"
[lib]
proc-macro = true
[dependencies]
syn = { version = "1.0", features = ["full"] }
serde_json = "1.0"
quote = "1.0"

View File

@ -0,0 +1,65 @@
extern crate proc_macro;
use proc_macro::TokenStream;
use quote::quote;
use serde_json::Value;
use std::env;
use std::fs;
use std::path::Path;
use syn::{parse_macro_input, LitStr};
#[proc_macro]
pub fn generate_input_types(input: TokenStream) -> TokenStream {
let input_string = parse_macro_input!(input as LitStr).value();
// Validate JSON format
let json: Value = match serde_json::from_str(&input_string) {
Ok(json) => json,
Err(e) => panic!("Invalid JSON input: {}", e),
};
// Convert the validated JSON back to a pretty-printed string
let formatted_json = serde_json::to_string_pretty(&json).expect("Failed to serialize JSON");
// Generate the output function
let expanded = quote! {
#[wasm_bindgen]
pub fn get_input_types() -> String {
String::from(#formatted_json)
}
};
// Convert the generated code back to a TokenStream
TokenStream::from(expanded)
}
#[proc_macro]
pub fn generate_input_types_file(input: TokenStream) -> TokenStream {
let file_path = syn::parse_macro_input!(input as syn::LitStr).value();
// Retrieve the directory containing the Cargo.toml file
let project_dir = env::var("CARGO_MANIFEST_DIR").unwrap();
let full_path = Path::new(&project_dir).join(&file_path);
// Read the JSON file content
let json_content = fs::read_to_string(full_path).unwrap_or_else(|err| {
panic!(
"Failed to read JSON file at '{}/{}': {}",
project_dir, file_path, err
)
});
// Optionally, validate that the content is valid JSON
let _: Value = serde_json::from_str(&json_content)
.unwrap_or_else(|_| panic!("JSON file contains invalid JSON"));
// Generate the function that returns the JSON string
let expanded = quote! {
#[wasm_bindgen]
pub fn get_input_types() -> String {
String::from(#json_content)
}
};
// Convert the generated code back to a TokenStream
TokenStream::from(expanded)
}

View File

@ -8,7 +8,7 @@ export async function getNodeWrapper(id: `${string}/${string}/${string}`) {
let wrapperCode = await wrapperReponse.text(); let wrapperCode = await wrapperReponse.text();
wrapperCode = wrapperCode.replace("wasm = val;", `if(wasm) return; wrapperCode = wrapperCode.replace("wasm = val;", `if(wasm) return;
wasm = val;`); wasm = val;`);
const wasmWrapper = await import(/*@vite-ignore*/`data:text/javascript;base64,${btoa(wrapperCode)}?id=${id}`); const wasmWrapper = await import(/*@vite-ignore*/`data:text/javascript;base64,${btoa(wrapperCode)}#${id}${Math.random().toString().slice(2)}`);
return wasmWrapper; return wasmWrapper;
} }
@ -41,6 +41,7 @@ export async function getNode(id: `${string}/${string}/${string}`) {
const inputTypes = JSON.parse(rawInputs); const inputTypes = JSON.parse(rawInputs);
return { id, outputs, inputs: inputTypes } return { id, outputs, inputs: inputTypes }
} catch (e) { } catch (e) {
console.log(rawInputs);
console.log("Failed to parse input types for node", { id, rawInputs }); console.log("Failed to parse input types for node", { id, rawInputs });
} }

View File

@ -89,6 +89,7 @@ export type Graph = {
title?: string; title?: string;
lastModified?: string; lastModified?: string;
}, },
settings?: Record<string, any>,
nodes: Node[]; nodes: Node[];
edges: [number, number, number, string][]; edges: [number, number, number, string][];
} }

View File

@ -26,11 +26,13 @@ type NodeInputSelect = {
type NodeInputSeed = { type NodeInputSeed = {
type: "seed" type: "seed"
value?: number;
} }
type DefaultOptions = { type DefaultOptions = {
internal?: boolean; internal?: boolean;
external?: boolean; external?: boolean;
setting?: string;
label?: string; label?: string;
} }

View File

@ -1,7 +0,0 @@
import { describe, it, expect } from 'vitest';
describe('sum test', () => {
it('adds 1 + 2 to equal 3', () => {
expect(1 + 2).toBe(3);
});
});

View File

@ -8,14 +8,15 @@
export let input: NodeInput; export let input: NodeInput;
export let value: any; export let value: any;
export let id: string;
</script> </script>
{#if input.type === "float"} {#if input.type === "float"}
<Float bind:value /> <Float {id} bind:value />
{:else if input.type === "integer"} {:else if input.type === "integer"}
<Integer bind:value /> <Integer {id} bind:value />
{:else if input.type === "boolean"} {:else if input.type === "boolean"}
<Checkbox bind:value /> <Checkbox {id} bind:value />
{:else if input.type === "select"} {:else if input.type === "select"}
<Select bind:value labels={input.labels} /> <Select {id} bind:value labels={input.labels} />
{/if} {/if}

View File

@ -1,8 +1,10 @@
<script lang="ts"> <script lang="ts">
export let value: boolean; export let value: boolean;
export let id: string;
</script> </script>
<input type="checkbox" bind:checked={value} /> <input {id} type="checkbox" bind:checked={value} />
<style> <style>
input[type="checkbox"] { input[type="checkbox"] {

View File

@ -3,9 +3,10 @@
export let min = 0; export let min = 0;
export let max = 10; export let max = 10;
export let step = 0.1; export let step = 0.1;
export let id: string;
</script> </script>
<input type="number" bind:value {min} {max} {step} /> <input {id} type="number" bind:value {min} {max} {step} />
<style> <style>
input { input {

View File

@ -3,9 +3,10 @@
export let min = 0; export let min = 0;
export let max = 10; export let max = 10;
export let step = 1; export let step = 1;
export let id: string;
</script> </script>
<input type="number" bind:value {min} {max} {step} /> <input {id} type="number" bind:value {min} {max} {step} />
<style> <style>
input { input {

View File

@ -1,9 +1,10 @@
<script lang="ts"> <script lang="ts">
export let labels: string[] = []; export let labels: string[] = [];
export let value: number = 0; export let value: number = 0;
export let id: string;
</script> </script>
<select bind:value> <select {id} bind:value>
{#each labels as label, i} {#each labels as label, i}
<option value={i}>{label}</option> <option value={i}>{label}</option>
{/each} {/each}

View File

@ -16,3 +16,13 @@ pub fn set_panic_hook() {
#[cfg(feature = "console_error_panic_hook")] #[cfg(feature = "console_error_panic_hook")]
console_error_panic_hook::set_once(); console_error_panic_hook::set_once();
} }
#[macro_export]
macro_rules! generate_outputs {
([$($item:expr),* $(,)?]) => {
#[wasm_bindgen]
pub fn get_outputs() -> Vec<String> {
vec![$($item.to_string()),*]
}
};
}

View File

@ -67,6 +67,10 @@ pub fn evaluate_node(input_args: &[i32]) -> (i32, i32) {
} }
pub fn evaluate_args(input_args: &[i32]) -> Vec<i32> { pub fn evaluate_args(input_args: &[i32]) -> Vec<i32> {
if input_args.len() == 4 && input_args[0] == 0 && input_args[1] == 3 {
return vec![input_args[2], input_args[3]];
}
let args = get_args(input_args); let args = get_args(input_args);
let mut resolved: Vec<i32> = Vec::new(); let mut resolved: Vec<i32> = Vec::new();