feat: make node definitions type safe with zod
This commit is contained in:
parent
4c7c4cac2c
commit
ad197db873
@ -6,6 +6,7 @@
|
|||||||
<link rel="icon" href="%sveltekit.assets%/svelte.svg" />
|
<link rel="icon" href="%sveltekit.assets%/svelte.svg" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
%sveltekit.head%
|
%sveltekit.head%
|
||||||
|
<title>Nodes</title>
|
||||||
<script>
|
<script>
|
||||||
var store = localStorage.getItem("node-settings");
|
var store = localStorage.getItem("node-settings");
|
||||||
if (store) {
|
if (store) {
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
let value: string = "";
|
let value: string = "";
|
||||||
let activeNodeId: string = "";
|
let activeNodeId: string = "";
|
||||||
|
|
||||||
const allNodes = graph.getNodeTypes();
|
const allNodes = graph.getNodeDefinitions();
|
||||||
|
|
||||||
function filterNodes() {
|
function filterNodes() {
|
||||||
return allNodes.filter((node) => node.id.includes(value));
|
return allNodes.filter((node) => node.id.includes(value));
|
||||||
|
@ -51,7 +51,7 @@ export class GraphManager extends EventEmitter<{ "save": Graph, "result": any, "
|
|||||||
}
|
}
|
||||||
this.inputSockets.set(s);
|
this.inputSockets.set(s);
|
||||||
});
|
});
|
||||||
this.execute = throttle(() => this._execute(), 50);
|
this.execute = throttle(() => this._execute(), 10);
|
||||||
}
|
}
|
||||||
|
|
||||||
serialize(): Graph {
|
serialize(): Graph {
|
||||||
@ -83,7 +83,7 @@ export class GraphManager extends EventEmitter<{ "save": Graph, "result": any, "
|
|||||||
this.emit("result", this.serialize());
|
this.emit("result", this.serialize());
|
||||||
}
|
}
|
||||||
|
|
||||||
getNodeTypes() {
|
getNodeDefinitions() {
|
||||||
return this.registry.getAllNodes();
|
return this.registry.getAllNodes();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -184,7 +184,7 @@ export class GraphManager extends EventEmitter<{ "save": Graph, "result": any, "
|
|||||||
// load settings
|
// load settings
|
||||||
const settingTypes: Record<string, NodeInput> = {};
|
const settingTypes: Record<string, NodeInput> = {};
|
||||||
const settingValues = graph.settings || {};
|
const settingValues = graph.settings || {};
|
||||||
const types = this.getNodeTypes();
|
const types = this.getNodeDefinitions();
|
||||||
for (const type of types) {
|
for (const type of types) {
|
||||||
if (type.inputs) {
|
if (type.inputs) {
|
||||||
for (const key in type.inputs) {
|
for (const key in type.inputs) {
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
import Camera from "../Camera.svelte";
|
import Camera from "../Camera.svelte";
|
||||||
import GraphView from "./GraphView.svelte";
|
import GraphView from "./GraphView.svelte";
|
||||||
import type { Node, Node as NodeType, Socket } from "@nodes/types";
|
import type { Node, Node as NodeType, Socket } from "@nodes/types";
|
||||||
|
import { NodeDefinitionSchema } from "@nodes/types";
|
||||||
import FloatingEdge from "../edges/FloatingEdge.svelte";
|
import FloatingEdge from "../edges/FloatingEdge.svelte";
|
||||||
import {
|
import {
|
||||||
activeNodeId,
|
activeNodeId,
|
||||||
@ -20,7 +21,7 @@
|
|||||||
import { createKeyMap } from "../../helpers/createKeyMap";
|
import { createKeyMap } from "../../helpers/createKeyMap";
|
||||||
import BoxSelection from "../BoxSelection.svelte";
|
import BoxSelection from "../BoxSelection.svelte";
|
||||||
import AddMenu from "../AddMenu.svelte";
|
import AddMenu from "../AddMenu.svelte";
|
||||||
import { get } from "svelte/store";
|
import { createWasmWrapper } from "@nodes/utils";
|
||||||
|
|
||||||
export let graph: GraphManager;
|
export let graph: GraphManager;
|
||||||
|
|
||||||
@ -759,9 +760,15 @@
|
|||||||
addMenuPosition = null;
|
addMenuPosition = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let isDragging = false;
|
||||||
|
|
||||||
function handleDrop(event: DragEvent) {
|
function handleDrop(event: DragEvent) {
|
||||||
|
event.preventDefault();
|
||||||
|
isDragging = false;
|
||||||
if (!event.dataTransfer) return;
|
if (!event.dataTransfer) return;
|
||||||
const nodeId = event.dataTransfer.getData("data/node-id");
|
const nodeId = event.dataTransfer.getData("data/node-id");
|
||||||
|
|
||||||
|
if (nodeId) {
|
||||||
let mx = event.clientX - rect.x;
|
let mx = event.clientX - rect.x;
|
||||||
let my = event.clientY - rect.y;
|
let my = event.clientY - rect.y;
|
||||||
|
|
||||||
@ -780,9 +787,36 @@
|
|||||||
position: pos,
|
position: pos,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
} else if (event.dataTransfer.files.length) {
|
||||||
|
const files = event.dataTransfer.files;
|
||||||
|
const reader = new FileReader();
|
||||||
|
reader.onload = (e) => {
|
||||||
|
const buffer = e.target?.result;
|
||||||
|
if (buffer) {
|
||||||
|
const wrapper = createWasmWrapper(buffer);
|
||||||
|
const definition = wrapper.get_definition();
|
||||||
|
const res = NodeDefinitionSchema.parse(definition);
|
||||||
|
console.log(wrapper, res);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
reader.readAsArrayBuffer(files[0]);
|
||||||
|
console.log({ files });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleDragEnter(e: DragEvent) {
|
||||||
|
e.preventDefault();
|
||||||
|
isDragging = true;
|
||||||
|
console.log(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
function handlerDragOver(e: DragEvent) {
|
function handlerDragOver(e: DragEvent) {
|
||||||
|
isDragging = true;
|
||||||
|
e.preventDefault();
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleDragEnd(e: DragEvent) {
|
||||||
|
isDragging = false;
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -807,11 +841,20 @@
|
|||||||
tabindex="0"
|
tabindex="0"
|
||||||
bind:clientWidth={width}
|
bind:clientWidth={width}
|
||||||
bind:clientHeight={height}
|
bind:clientHeight={height}
|
||||||
|
on:dragenter={handleDragEnter}
|
||||||
on:dragover={handlerDragOver}
|
on:dragover={handlerDragOver}
|
||||||
on:drop={handleDrop}
|
on:drop={handleDrop}
|
||||||
on:keydown={keymap.handleKeyboardEvent}
|
on:keydown={keymap.handleKeyboardEvent}
|
||||||
on:mousedown={handleMouseDown}
|
on:mousedown={handleMouseDown}
|
||||||
>
|
>
|
||||||
|
<input
|
||||||
|
type="file"
|
||||||
|
accept="application/wasm"
|
||||||
|
disabled={!isDragging}
|
||||||
|
on:dragend={handleDragEnd}
|
||||||
|
on:dragleave={handleDragEnd}
|
||||||
|
/>
|
||||||
|
|
||||||
<Canvas shadows={false} renderMode="on-demand" colorManagementEnabled={false}>
|
<Canvas shadows={false} renderMode="on-demand" colorManagementEnabled={false}>
|
||||||
<Camera bind:camera position={cameraPosition} />
|
<Camera bind:camera position={cameraPosition} />
|
||||||
|
|
||||||
@ -856,4 +899,15 @@
|
|||||||
transition: opacity 0.3s ease;
|
transition: opacity 0.3s ease;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
input {
|
||||||
|
position: absolute;
|
||||||
|
z-index: 1;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background: red;
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
input:disabled {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import type { NodeRegistry, NodeType } from "@nodes/types";
|
import type { NodeRegistry, NodeDefinition } from "@nodes/types";
|
||||||
import { createWasmWrapper } from "@nodes/utils";
|
import { createWasmWrapper } from "@nodes/utils";
|
||||||
import { createLogger } from "./helpers";
|
import { createLogger } from "./helpers";
|
||||||
|
|
||||||
@ -6,18 +6,14 @@ const log = createLogger("node-registry");
|
|||||||
export class RemoteNodeRegistry implements NodeRegistry {
|
export class RemoteNodeRegistry implements NodeRegistry {
|
||||||
|
|
||||||
status: "loading" | "ready" | "error" = "loading";
|
status: "loading" | "ready" | "error" = "loading";
|
||||||
private nodes: Map<string, NodeType> = new Map();
|
private nodes: Map<string, NodeDefinition> = new Map();
|
||||||
|
|
||||||
constructor(private url: string) { }
|
constructor(private url: string) { }
|
||||||
|
|
||||||
async loadNode(id: `${string}/${string}/${string}`) {
|
async loadNode(id: `${string}/${string}/${string}`) {
|
||||||
const wasmResponse = await this.fetchNode(id);
|
const wasmResponse = await this.fetchNode(id);
|
||||||
|
|
||||||
// Setup Wasm wrapper
|
const wrapper = createWasmWrapper(wasmResponse);
|
||||||
const wrapper = createWasmWrapper();
|
|
||||||
const module = new WebAssembly.Module(wasmResponse);
|
|
||||||
const instance = new WebAssembly.Instance(module, { ["./index_bg.js"]: wrapper });
|
|
||||||
wrapper.setInstance(instance);
|
|
||||||
|
|
||||||
const definition = wrapper.get_definition();
|
const definition = wrapper.get_definition();
|
||||||
|
|
||||||
|
@ -13,9 +13,7 @@ export async function getWasm(id: `${string}/${string}/${string}`) {
|
|||||||
|
|
||||||
const file = await fs.readFile(filePath);
|
const file = await fs.readFile(filePath);
|
||||||
|
|
||||||
const bytes = new Uint8Array(file);
|
return new Uint8Array(file);
|
||||||
|
|
||||||
return bytes;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -24,10 +22,7 @@ export async function getNodeWasm(id: `${string}/${string}/${string}`) {
|
|||||||
const wasmBytes = await getWasm(id);
|
const wasmBytes = await getWasm(id);
|
||||||
if (!wasmBytes) return null;
|
if (!wasmBytes) return null;
|
||||||
|
|
||||||
const wrapper = createWasmWrapper();
|
const wrapper = createWasmWrapper(wasmBytes);
|
||||||
const module = new WebAssembly.Module(wasmBytes);
|
|
||||||
const instance = new WebAssembly.Instance(module, { ["./index_bg.js"]: wrapper });
|
|
||||||
wrapper.setInstance(instance)
|
|
||||||
|
|
||||||
return wrapper;
|
return wrapper;
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import NodeHtml from "$lib/graph-interface/node/NodeHTML.svelte";
|
import NodeHtml from "$lib/graph-interface/node/NodeHTML.svelte";
|
||||||
import type { NodeType } from "@nodes/types";
|
import type { NodeDefinitions } from "@nodes/types";
|
||||||
|
|
||||||
export let node: NodeType;
|
export let node: NodeDefinitions;
|
||||||
|
|
||||||
let dragging = false;
|
let dragging = false;
|
||||||
|
|
||||||
|
@ -1,19 +1,10 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import type { GraphManager } from "$lib/graph-interface/graph-manager";
|
|
||||||
import Node from "$lib/graph-interface/node/Node.svelte";
|
|
||||||
import localStore from "$lib/helpers/localStore";
|
import localStore from "$lib/helpers/localStore";
|
||||||
import type { RemoteNodeRegistry } from "$lib/node-registry-client";
|
import type { RemoteNodeRegistry } from "$lib/node-registry-client";
|
||||||
import { Canvas } from "@threlte/core";
|
|
||||||
import BreadCrumbs from "./BreadCrumbs.svelte";
|
import BreadCrumbs from "./BreadCrumbs.svelte";
|
||||||
import NodeHtml from "$lib/graph-interface/node/NodeHTML.svelte";
|
|
||||||
import DraggableNode from "./DraggableNode.svelte";
|
import DraggableNode from "./DraggableNode.svelte";
|
||||||
|
|
||||||
export let nodeRegistry: RemoteNodeRegistry;
|
export let nodeRegistry: RemoteNodeRegistry;
|
||||||
export let manager: GraphManager;
|
|
||||||
|
|
||||||
function handleImport() {
|
|
||||||
nodeRegistry.load([$activeId]);
|
|
||||||
}
|
|
||||||
|
|
||||||
const activeId = localStore<
|
const activeId = localStore<
|
||||||
`${string}` | `${string}/${string}` | `${string}/${string}/${string}`
|
`${string}` | `${string}/${string}` | `${string}/${string}/${string}`
|
||||||
|
@ -1,21 +1,21 @@
|
|||||||
import type { Graph, NodeRegistry, NodeType, RuntimeExecutor } from "@nodes/types";
|
import type { Graph, NodeRegistry, NodeDefinition, RuntimeExecutor } from "@nodes/types";
|
||||||
import { fastHash, concat_encoded, encodeFloat, encode } from "@nodes/utils"
|
import { fastHash, concat_encoded, encodeFloat, encode } from "@nodes/utils"
|
||||||
|
|
||||||
export class MemoryRuntimeExecutor implements RuntimeExecutor {
|
export class MemoryRuntimeExecutor implements RuntimeExecutor {
|
||||||
|
|
||||||
private typeMap: Map<string, NodeType> = new Map();
|
private definitionMap: Map<string, NodeDefinition> = new Map();
|
||||||
|
|
||||||
private cache: Record<string, { eol: number, value: any }> = {};
|
private cache: Record<string, { eol: number, value: any }> = {};
|
||||||
|
|
||||||
constructor(private registry: NodeRegistry) { }
|
constructor(private registry: NodeRegistry) { }
|
||||||
|
|
||||||
private getNodeTypes(graph: Graph) {
|
private getNodeDefinitions(graph: Graph) {
|
||||||
|
|
||||||
if (this.registry.status !== "ready") {
|
if (this.registry.status !== "ready") {
|
||||||
throw new Error("Node registry is not ready");
|
throw new Error("Node registry is not ready");
|
||||||
}
|
}
|
||||||
|
|
||||||
const typeMap = new Map<string, NodeType>();
|
const typeMap = new Map<string, NodeDefinition>();
|
||||||
for (const node of graph.nodes) {
|
for (const node of graph.nodes) {
|
||||||
if (!typeMap.has(node.type)) {
|
if (!typeMap.has(node.type)) {
|
||||||
const type = this.registry.getNode(node.type);
|
const type = this.registry.getNode(node.type);
|
||||||
@ -29,8 +29,8 @@ export class MemoryRuntimeExecutor implements RuntimeExecutor {
|
|||||||
|
|
||||||
private addMetaData(graph: Graph) {
|
private addMetaData(graph: Graph) {
|
||||||
|
|
||||||
// First, lets check if all nodes have a type
|
// First, lets check if all nodes have a definition
|
||||||
this.typeMap = this.getNodeTypes(graph);
|
this.definitionMap = this.getNodeDefinitions(graph);
|
||||||
|
|
||||||
const outputNode = graph.nodes.find(node => node.type.endsWith("/output"));
|
const outputNode = graph.nodes.find(node => node.type.endsWith("/output"));
|
||||||
if (!outputNode) {
|
if (!outputNode) {
|
||||||
@ -115,7 +115,7 @@ export class MemoryRuntimeExecutor implements RuntimeExecutor {
|
|||||||
|
|
||||||
for (const node of sortedNodes) {
|
for (const node of sortedNodes) {
|
||||||
|
|
||||||
const node_type = this.typeMap.get(node.type)!;
|
const node_type = this.definitionMap.get(node.type)!;
|
||||||
|
|
||||||
if (node?.tmp && node_type?.execute) {
|
if (node?.tmp && node_type?.execute) {
|
||||||
const inputs: Record<string, string | number | boolean> = {};
|
const inputs: Record<string, string | number | boolean> = {};
|
||||||
|
@ -105,6 +105,7 @@
|
|||||||
top: 0px;
|
top: 0px;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
display: grid;
|
display: grid;
|
||||||
|
z-index: 2;
|
||||||
grid-template-columns: 30px 1fr;
|
grid-template-columns: 30px 1fr;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
right: 0px;
|
right: 0px;
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
{
|
{
|
||||||
|
"id": "max/plantarium/array",
|
||||||
"outputs": [
|
"outputs": [
|
||||||
"float"
|
"float"
|
||||||
],
|
],
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
{
|
{
|
||||||
|
"id": "max/plantarium/box",
|
||||||
"outputs": [
|
"outputs": [
|
||||||
"model"
|
"model"
|
||||||
],
|
],
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
{
|
{
|
||||||
|
"id": "max/plantarium/float",
|
||||||
"outputs": [
|
"outputs": [
|
||||||
"float"
|
"float"
|
||||||
],
|
],
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
{
|
{
|
||||||
|
"id": "max/plantarium/math",
|
||||||
"outputs": [
|
"outputs": [
|
||||||
"float"
|
"float"
|
||||||
],
|
],
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
{
|
{
|
||||||
|
"id": "max/plantarium/output",
|
||||||
"outputs": [],
|
"outputs": [],
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"input": {
|
"input": {
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
{
|
{
|
||||||
|
"id": "max/plantarium/random",
|
||||||
"outputs": [
|
"outputs": [
|
||||||
"float"
|
"float"
|
||||||
],
|
],
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
{
|
{
|
||||||
|
"id": "max/plantarium/stem",
|
||||||
"outputs": [
|
"outputs": [
|
||||||
"plant"
|
"plant"
|
||||||
],
|
],
|
||||||
|
@ -31,9 +31,9 @@ pub fn execute(input: &[i32]) -> Vec<i32> {
|
|||||||
|
|
||||||
for i in 0..res_curve {
|
for i in 0..res_curve {
|
||||||
let a = i as f32 / (res_curve - 1) as f32;
|
let a = i as f32 / (res_curve - 1) as f32;
|
||||||
path_p[i * 4] = origin[0] + (a * 8.0).sin() * 0.2;
|
path_p[i * 4] = origin[0];
|
||||||
path_p[i * 4 + 1] = origin[1] + a * length;
|
path_p[i * 4 + 1] = origin[1] + a * length;
|
||||||
path_p[i * 4 + 2] = origin[2] + ((a + 2.0) * 8.0).sin() * 0.2;
|
path_p[i * 4 + 2] = origin[2];
|
||||||
path_p[i * 4 + 3] = thickness * (1.0 - a);
|
path_p[i * 4 + 3] = thickness * (1.0 - a);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
{
|
{
|
||||||
|
"id": "max/plantarium/sum",
|
||||||
"outputs": [
|
"outputs": [
|
||||||
"float"
|
"float"
|
||||||
],
|
],
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
{
|
{
|
||||||
|
"id": "max/plantarium/triangle",
|
||||||
"outputs": [
|
"outputs": [
|
||||||
"model"
|
"model"
|
||||||
],
|
],
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use macros::include_definition_file;
|
use macros::include_definition_file;
|
||||||
use utils::{decode_float, encode_float, evaluate_arg, evaluate_float, get_args, wrap_arg};
|
use utils::{decode_float, encode_float, evaluate_arg, get_args, wrap_arg};
|
||||||
use wasm_bindgen::prelude::*;
|
use wasm_bindgen::prelude::*;
|
||||||
use web_sys::console;
|
use web_sys::console;
|
||||||
|
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
{
|
{
|
||||||
|
"id": "max/plantarium/vec3",
|
||||||
"outputs": [
|
"outputs": [
|
||||||
"vec3"
|
"vec3"
|
||||||
],
|
],
|
||||||
|
@ -5,14 +5,14 @@ use std::env;
|
|||||||
use std::fs;
|
use std::fs;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use syn::{parse_macro_input, LitStr};
|
use syn::{parse_macro_input, LitStr};
|
||||||
use types::NodeType;
|
use types::NodeDefinition;
|
||||||
|
|
||||||
#[proc_macro]
|
#[proc_macro]
|
||||||
pub fn define_node(input: TokenStream) -> TokenStream {
|
pub fn node_definition(input: TokenStream) -> TokenStream {
|
||||||
let input_string = parse_macro_input!(input as LitStr).value();
|
let input_string = parse_macro_input!(input as LitStr).value();
|
||||||
|
|
||||||
// Validate JSON format
|
// Validate JSON format
|
||||||
let json: NodeType = match serde_json::from_str(&input_string) {
|
let json: NodeDefinition = match serde_json::from_str(&input_string) {
|
||||||
Ok(json) => json,
|
Ok(json) => json,
|
||||||
Err(e) => panic!("Invalid JSON input: {}", e),
|
Err(e) => panic!("Invalid JSON input: {}", e),
|
||||||
};
|
};
|
||||||
@ -49,7 +49,7 @@ pub fn include_definition_file(input: TokenStream) -> TokenStream {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Optionally, validate that the content is valid JSON
|
// Optionally, validate that the content is valid JSON
|
||||||
let _: NodeType = serde_json::from_str(&json_content)
|
let _: NodeDefinition = serde_json::from_str(&json_content)
|
||||||
.unwrap_or_else(|err| panic!("JSON file contains invalid JSON: {}", err));
|
.unwrap_or_else(|err| panic!("JSON file contains invalid JSON: {}", err));
|
||||||
|
|
||||||
// Generate the function that returns the JSON string
|
// Generate the function that returns the JSON string
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import type { NodeInput } from "./inputs";
|
import { z } from "zod";
|
||||||
|
import { NodeInputSchema } from "./inputs";
|
||||||
export type { NodeInput } from "./inputs";
|
export type { NodeInput } from "./inputs";
|
||||||
|
|
||||||
export type Node = {
|
export type Node = {
|
||||||
@ -12,7 +13,7 @@ export type Node = {
|
|||||||
parents?: Node[],
|
parents?: Node[],
|
||||||
children?: Node[],
|
children?: Node[],
|
||||||
inputNodes?: Record<string, Node>
|
inputNodes?: Record<string, Node>
|
||||||
type?: NodeType;
|
type?: NodeDefinition;
|
||||||
downX?: number;
|
downX?: number;
|
||||||
downY?: number;
|
downY?: number;
|
||||||
x?: number;
|
x?: number;
|
||||||
@ -28,15 +29,19 @@ export type Node = {
|
|||||||
position: [x: number, y: number]
|
position: [x: number, y: number]
|
||||||
}
|
}
|
||||||
|
|
||||||
export type NodeType = {
|
export const NodeDefinitionSchema = z.object({
|
||||||
id: string;
|
id: z.string(),
|
||||||
inputs?: Record<string, NodeInput>
|
inputs: z.record(NodeInputSchema).optional(),
|
||||||
outputs?: string[];
|
outputs: z.array(z.string()).optional(),
|
||||||
meta?: {
|
meta: z.object({
|
||||||
title?: string;
|
description: z.string().optional(),
|
||||||
},
|
title: z.string().optional(),
|
||||||
execute?: (args: Int32Array) => Int32Array;
|
}).optional(),
|
||||||
}
|
});
|
||||||
|
|
||||||
|
export type NodeDefinition = z.infer<typeof NodeDefinitionSchema> & {
|
||||||
|
execute(input: Int32Array): Int32Array;
|
||||||
|
};
|
||||||
|
|
||||||
export type Socket = {
|
export type Socket = {
|
||||||
node: Node;
|
node: Node;
|
||||||
@ -63,12 +68,12 @@ export interface NodeRegistry {
|
|||||||
* @param id - The id of the node to get
|
* @param id - The id of the node to get
|
||||||
* @returns The node with the given id, or undefined if no such node exists
|
* @returns The node with the given id, or undefined if no such node exists
|
||||||
*/
|
*/
|
||||||
getNode: (id: string) => NodeType | undefined;
|
getNode: (id: string) => NodeDefinition | undefined;
|
||||||
/**
|
/**
|
||||||
* Get all nodes
|
* Get all nodes
|
||||||
* @returns An array of all nodes
|
* @returns An array of all nodes
|
||||||
*/
|
*/
|
||||||
getAllNodes: () => NodeType[];
|
getAllNodes: () => NodeDefinition[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface RuntimeExecutor {
|
export interface RuntimeExecutor {
|
||||||
|
@ -1,65 +1,78 @@
|
|||||||
type NodeInputFloat = {
|
import { z } from "zod";
|
||||||
type: "float";
|
|
||||||
element?: "slider";
|
|
||||||
value?: number;
|
|
||||||
min?: number;
|
|
||||||
max?: number;
|
|
||||||
step?: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
type NodeInputInteger = {
|
const DefaultOptionsSchema = z.object({
|
||||||
type: "integer";
|
internal: z.boolean().optional(),
|
||||||
element?: "slider";
|
external: z.boolean().optional(),
|
||||||
value?: number;
|
setting: z.string().optional(),
|
||||||
min?: number;
|
label: z.string().optional(),
|
||||||
max?: number;
|
description: z.string().optional(),
|
||||||
}
|
accepts: z.array(z.string()).optional(),
|
||||||
|
});
|
||||||
type NodeInputBoolean = {
|
|
||||||
type: "boolean";
|
|
||||||
value?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
type NodeInputSelect = {
|
|
||||||
type: "select";
|
|
||||||
options: string[];
|
|
||||||
value?: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
type NodeInputSeed = {
|
|
||||||
type: "seed"
|
|
||||||
value?: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
type NodeInputVec3 = {
|
|
||||||
type: "vec3";
|
|
||||||
value?: number[];
|
|
||||||
}
|
|
||||||
|
|
||||||
type NodeInputModel = {
|
|
||||||
type: "model";
|
|
||||||
}
|
|
||||||
|
|
||||||
type NodeInputPlant = {
|
|
||||||
type: "plant"
|
|
||||||
}
|
|
||||||
|
|
||||||
type InputTypes = (NodeInputSeed | NodeInputBoolean | NodeInputFloat | NodeInputInteger | NodeInputSelect | NodeInputSeed | NodeInputVec3 | NodeInputModel | NodeInputPlant);
|
|
||||||
|
|
||||||
type InputId = InputTypes["type"];
|
|
||||||
|
|
||||||
type DefaultOptions = {
|
|
||||||
internal?: boolean;
|
|
||||||
external?: boolean;
|
|
||||||
setting?: string;
|
|
||||||
label?: string | false;
|
|
||||||
}
|
|
||||||
|
|
||||||
export type NodeInput = InputTypes & {
|
|
||||||
type: InputId | InputId[];
|
|
||||||
} & DefaultOptions;
|
|
||||||
|
|
||||||
|
|
||||||
export type NodeInputType<T extends Record<string, NodeInput>> = {
|
export const NodeInputFloatSchema = z.object({
|
||||||
[K in keyof T]: T[K]["value"]
|
...DefaultOptionsSchema.shape,
|
||||||
};
|
type: z.literal("float"),
|
||||||
|
element: z.literal("slider").optional(),
|
||||||
|
value: z.number().optional(),
|
||||||
|
min: z.number().optional(),
|
||||||
|
max: z.number().optional(),
|
||||||
|
step: z.number().optional(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const NodeInputIntegerSchema = z.object({
|
||||||
|
...DefaultOptionsSchema.shape,
|
||||||
|
type: z.literal("integer"),
|
||||||
|
element: z.literal("slider").optional(),
|
||||||
|
value: z.number().optional(),
|
||||||
|
min: z.number().optional(),
|
||||||
|
max: z.number().optional(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const NodeInputBooleanSchema = z.object({
|
||||||
|
...DefaultOptionsSchema.shape,
|
||||||
|
type: z.literal("boolean"),
|
||||||
|
vale: z.boolean().optional(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const NodeInputSelectSchema = z.object({
|
||||||
|
...DefaultOptionsSchema.shape,
|
||||||
|
type: z.literal("select"),
|
||||||
|
value: z.number().optional(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const NodeInputSeedSchema = z.object({
|
||||||
|
...DefaultOptionsSchema.shape,
|
||||||
|
type: z.literal("seed"),
|
||||||
|
value: z.number().optional(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const NodeInputVec3Schema = z.object({
|
||||||
|
...DefaultOptionsSchema.shape,
|
||||||
|
type: z.literal("vec3"),
|
||||||
|
value: z.array(z.number()).optional(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const NodeInputModelSchema = z.object({
|
||||||
|
...DefaultOptionsSchema.shape,
|
||||||
|
type: z.literal("model"),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const NodeInputPlantSchema = z.object({
|
||||||
|
...DefaultOptionsSchema.shape,
|
||||||
|
type: z.literal("plant"),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const NodeInputSchema = z.union([
|
||||||
|
NodeInputSeedSchema,
|
||||||
|
NodeInputBooleanSchema,
|
||||||
|
NodeInputFloatSchema,
|
||||||
|
NodeInputIntegerSchema,
|
||||||
|
NodeInputSelectSchema,
|
||||||
|
NodeInputSeedSchema,
|
||||||
|
NodeInputVec3Schema,
|
||||||
|
NodeInputModelSchema,
|
||||||
|
NodeInputPlantSchema
|
||||||
|
]);
|
||||||
|
|
||||||
|
export type NodeInput = z.infer<typeof InputSchema>;
|
||||||
|
@ -90,7 +90,7 @@ pub struct DefaultOptions {
|
|||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
#[serde(untagged)]
|
#[serde(untagged)]
|
||||||
pub enum NodeTypeOrArray {
|
pub enum NodeDefinitionOrArray {
|
||||||
Single(InputTypes),
|
Single(InputTypes),
|
||||||
Multiple(Vec<String>),
|
Multiple(Vec<String>),
|
||||||
}
|
}
|
||||||
@ -124,10 +124,10 @@ impl<'de> Deserialize<'de> for NodeInput {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Debug, Serialize)]
|
#[derive(Deserialize, Debug, Serialize)]
|
||||||
pub struct NodeType {
|
pub struct NodeDefinition {
|
||||||
|
pub id: String,
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
pub inputs: Option<HashMap<String, NodeInput>>,
|
pub inputs: Option<HashMap<String, NodeInput>>,
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
pub outputs: Option<Vec<String>>,
|
pub outputs: Option<Vec<String>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use crate::{decode_float, log};
|
use crate::decode_float;
|
||||||
|
|
||||||
pub fn get_args(args: &[i32]) -> Vec<&[i32]> {
|
pub fn get_args(args: &[i32]) -> Vec<&[i32]> {
|
||||||
let mut idx: usize = 0;
|
let mut idx: usize = 0;
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { NodeType } from "@nodes/types";
|
import { NodeDefinition } from "@nodes/types";
|
||||||
|
|
||||||
const cachedTextDecoder = new TextDecoder('utf-8', { ignoreBOM: true, fatal: true });
|
const cachedTextDecoder = new TextDecoder('utf-8', { ignoreBOM: true, fatal: true });
|
||||||
const cachedTextEncoder = new TextEncoder();
|
const cachedTextEncoder = new TextEncoder();
|
||||||
@ -17,12 +17,9 @@ const encodeString = (typeof cachedTextEncoder.encodeInto === 'function'
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
export function createWasmWrapper() {
|
function createWrapper() {
|
||||||
let wasm: any;
|
let wasm: any;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
let cachedUint8Memory0: Uint8Array | null = null;
|
let cachedUint8Memory0: Uint8Array | null = null;
|
||||||
let cachedInt32Memory0: Int32Array | null = null;
|
let cachedInt32Memory0: Int32Array | null = null;
|
||||||
let cachedUint32Memory0: Uint32Array | null = null;
|
let cachedUint32Memory0: Uint32Array | null = null;
|
||||||
@ -120,7 +117,7 @@ export function createWasmWrapper() {
|
|||||||
deferred1_0 = r0;
|
deferred1_0 = r0;
|
||||||
deferred1_1 = r1;
|
deferred1_1 = r1;
|
||||||
const string = getStringFromWasm0(r0, r1);
|
const string = getStringFromWasm0(r0, r1);
|
||||||
return JSON.parse(string) as Omit<NodeType, "id">;
|
return JSON.parse(string) as NodeDefinition;
|
||||||
} finally {
|
} finally {
|
||||||
wasm.__wbindgen_add_to_stack_pointer(16);
|
wasm.__wbindgen_add_to_stack_pointer(16);
|
||||||
wasm.__wbindgen_free(deferred1_0, deferred1_1, 1);
|
wasm.__wbindgen_free(deferred1_0, deferred1_1, 1);
|
||||||
@ -226,9 +223,12 @@ export function createWasmWrapper() {
|
|||||||
wasm = instance.exports;
|
wasm = instance.exports;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
|
exports: {
|
||||||
// Expose other methods that interact with the wasm instance
|
// Expose other methods that interact with the wasm instance
|
||||||
execute,
|
execute,
|
||||||
get_definition,
|
get_definition,
|
||||||
|
},
|
||||||
|
|
||||||
__wbindgen_string_new,
|
__wbindgen_string_new,
|
||||||
__wbindgen_object_drop_ref,
|
__wbindgen_object_drop_ref,
|
||||||
@ -240,3 +240,11 @@ export function createWasmWrapper() {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export function createWasmWrapper(wasmBuffer: ArrayBuffer) {
|
||||||
|
const wrapper = createWrapper();
|
||||||
|
const module = new WebAssembly.Module(wasmBuffer);
|
||||||
|
const instance = new WebAssembly.Instance(module, { ["./index_bg.js"]: wrapper });
|
||||||
|
wrapper.setInstance(instance);
|
||||||
|
return wrapper.exports;
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user