feat: add "*"/any type input for dev page
This commit is contained in:
@@ -25,7 +25,7 @@ const clone = 'structuredClone' in self
|
|||||||
? self.structuredClone
|
? self.structuredClone
|
||||||
: (args: unknown) => JSON.parse(JSON.stringify(args));
|
: (args: unknown) => JSON.parse(JSON.stringify(args));
|
||||||
|
|
||||||
function areSocketsCompatible(
|
export function areSocketsCompatible(
|
||||||
output: string | undefined,
|
output: string | undefined,
|
||||||
inputs: string | (string | undefined)[] | undefined
|
inputs: string | (string | undefined)[] | undefined
|
||||||
) {
|
) {
|
||||||
@@ -33,7 +33,7 @@ function areSocketsCompatible(
|
|||||||
if (Array.isArray(inputs) && output) {
|
if (Array.isArray(inputs) && output) {
|
||||||
return inputs.includes('*') || inputs.includes(output);
|
return inputs.includes('*') || inputs.includes(output);
|
||||||
}
|
}
|
||||||
return inputs === output;
|
return inputs === output || inputs === '*';
|
||||||
}
|
}
|
||||||
|
|
||||||
function areEdgesEqual(firstEdge: Edge, secondEdge: Edge) {
|
function areEdgesEqual(firstEdge: Edge, secondEdge: Edge) {
|
||||||
@@ -269,14 +269,7 @@ export class GraphManager extends EventEmitter<{
|
|||||||
private _init(graph: Graph) {
|
private _init(graph: Graph) {
|
||||||
const nodes = new SvelteMap(
|
const nodes = new SvelteMap(
|
||||||
graph.nodes.map((node) => {
|
graph.nodes.map((node) => {
|
||||||
const nodeType = this.registry.getNode(node.type);
|
return [node.id, node as NodeInstance];
|
||||||
const n = node as NodeInstance;
|
|
||||||
if (nodeType) {
|
|
||||||
n.state = {
|
|
||||||
type: nodeType
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return [node.id, n];
|
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -301,6 +294,30 @@ export class GraphManager extends EventEmitter<{
|
|||||||
this.execute();
|
this.execute();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async loadAllCollections() {
|
||||||
|
// Fetch all nodes from all collections of the loaded nodes
|
||||||
|
const nodeIds = Array.from(new Set([...this.graph.nodes.map((n) => n.type)]));
|
||||||
|
const allCollections = new Set<`${string}/${string}`>();
|
||||||
|
for (const id of nodeIds) {
|
||||||
|
const [user, collection] = id.split('/');
|
||||||
|
allCollections.add(`${user}/${collection}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const allCollectionIds = await Promise
|
||||||
|
.all([...allCollections]
|
||||||
|
.map(async (collection) =>
|
||||||
|
remoteRegistry
|
||||||
|
.fetchCollection(collection)
|
||||||
|
.then((collection: { nodes: { id: NodeId }[] }) => {
|
||||||
|
return collection.nodes.map(n => n.id.replace(/\.wasm$/, '') as NodeId);
|
||||||
|
})
|
||||||
|
));
|
||||||
|
|
||||||
|
const missingNodeIds = [...new Set(allCollectionIds.flat())];
|
||||||
|
|
||||||
|
this.registry.load(missingNodeIds);
|
||||||
|
}
|
||||||
|
|
||||||
async load(graph: Graph) {
|
async load(graph: Graph) {
|
||||||
const a = performance.now();
|
const a = performance.now();
|
||||||
|
|
||||||
@@ -385,7 +402,9 @@ export class GraphManager extends EventEmitter<{
|
|||||||
|
|
||||||
this.loaded = true;
|
this.loaded = true;
|
||||||
logger.log(`Graph loaded in ${performance.now() - a}ms`);
|
logger.log(`Graph loaded in ${performance.now() - a}ms`);
|
||||||
|
|
||||||
setTimeout(() => this.execute(), 100);
|
setTimeout(() => this.execute(), 100);
|
||||||
|
this.loadAllCollections(); // lazily load all nodes from all collections
|
||||||
}
|
}
|
||||||
|
|
||||||
getAllNodes() {
|
getAllNodes() {
|
||||||
@@ -492,10 +511,10 @@ export class GraphManager extends EventEmitter<{
|
|||||||
const inputs = Object.entries(to.state?.type?.inputs ?? {});
|
const inputs = Object.entries(to.state?.type?.inputs ?? {});
|
||||||
const outputs = from.state?.type?.outputs ?? [];
|
const outputs = from.state?.type?.outputs ?? [];
|
||||||
for (let i = 0; i < inputs.length; i++) {
|
for (let i = 0; i < inputs.length; i++) {
|
||||||
const [inputName, input] = inputs[0];
|
const [inputName, input] = inputs[i];
|
||||||
for (let o = 0; o < outputs.length; o++) {
|
for (let o = 0; o < outputs.length; o++) {
|
||||||
const output = outputs[0];
|
const output = outputs[o];
|
||||||
if (input.type === output) {
|
if (input.type === output || input.type === '*') {
|
||||||
return this.createEdge(from, o, to, inputName);
|
return this.createEdge(from, o, to, inputName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -725,6 +744,7 @@ export class GraphManager extends EventEmitter<{
|
|||||||
|
|
||||||
getPossibleSockets({ node, index }: Socket): [NodeInstance, string | number][] {
|
getPossibleSockets({ node, index }: Socket): [NodeInstance, string | number][] {
|
||||||
const nodeType = node?.state?.type;
|
const nodeType = node?.state?.type;
|
||||||
|
console.log({ node: $state.snapshot(node), index, nodeType });
|
||||||
if (!nodeType) return [];
|
if (!nodeType) return [];
|
||||||
|
|
||||||
const sockets: [NodeInstance, string | number][] = [];
|
const sockets: [NodeInstance, string | number][] = [];
|
||||||
@@ -788,6 +808,7 @@ export class GraphManager extends EventEmitter<{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.log(`Found ${sockets.length} possible sockets`, sockets);
|
||||||
return sockets;
|
return sockets;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -259,7 +259,7 @@ export class GraphState {
|
|||||||
|
|
||||||
let { node, index, position } = socket;
|
let { node, index, position } = socket;
|
||||||
|
|
||||||
// remove existing edge
|
// if the socket is an input socket -> remove existing edges
|
||||||
if (typeof index === 'string') {
|
if (typeof index === 'string') {
|
||||||
const edges = this.graph.getEdgesToNode(node);
|
const edges = this.graph.getEdgesToNode(node);
|
||||||
for (const edge of edges) {
|
for (const edge of edges) {
|
||||||
|
|||||||
@@ -33,6 +33,7 @@
|
|||||||
backgroundType = $bindable('grid'),
|
backgroundType = $bindable('grid'),
|
||||||
snapToGrid = $bindable(true),
|
snapToGrid = $bindable(true),
|
||||||
showHelp = $bindable(false),
|
showHelp = $bindable(false),
|
||||||
|
settings = $bindable(),
|
||||||
settingTypes = $bindable(),
|
settingTypes = $bindable(),
|
||||||
onsave,
|
onsave,
|
||||||
onresult
|
onresult
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import {
|
|||||||
type AsyncCache,
|
type AsyncCache,
|
||||||
type NodeDefinition,
|
type NodeDefinition,
|
||||||
NodeDefinitionSchema,
|
NodeDefinitionSchema,
|
||||||
|
type NodeId,
|
||||||
type NodeRegistry
|
type NodeRegistry
|
||||||
} from '@nodarium/types';
|
} from '@nodarium/types';
|
||||||
import { createLogger, createWasmWrapper } from '@nodarium/utils';
|
import { createLogger, createWasmWrapper } from '@nodarium/utils';
|
||||||
@@ -170,6 +171,13 @@ export class RemoteNodeRegistry implements NodeRegistry {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getAllNodes() {
|
getAllNodes() {
|
||||||
return [...this.nodes.values()];
|
const allNodes = [...this.nodes.values()];
|
||||||
|
log.info('getting all nodes', allNodes);
|
||||||
|
return allNodes;
|
||||||
|
}
|
||||||
|
|
||||||
|
async overwriteNode(nodeId: NodeId, node: NodeDefinition) {
|
||||||
|
log.info('Overwritten node', { nodeId, node });
|
||||||
|
this.nodes.set(nodeId, node);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -52,6 +52,7 @@ function getValue(input: NodeInput, value?: unknown) {
|
|||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.error({ input, value });
|
||||||
throw new Error(`Unknown input type ${input.type}`);
|
throw new Error(`Unknown input type ${input.type}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -63,6 +64,8 @@ export class MemoryRuntimeExecutor implements RuntimeExecutor {
|
|||||||
|
|
||||||
perf?: PerformanceStore;
|
perf?: PerformanceStore;
|
||||||
|
|
||||||
|
private results: Record<string, Int32Array> = {};
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private registry: NodeRegistry,
|
private registry: NodeRegistry,
|
||||||
public cache?: SyncCache<Int32Array>
|
public cache?: SyncCache<Int32Array>
|
||||||
@@ -186,7 +189,7 @@ 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, Int32Array> = {};
|
this.results = {};
|
||||||
|
|
||||||
if (settings['randomSeed']) {
|
if (settings['randomSeed']) {
|
||||||
this.seed = Math.floor(Math.random() * 100000000);
|
this.seed = Math.floor(Math.random() * 100000000);
|
||||||
@@ -217,12 +220,12 @@ export class MemoryRuntimeExecutor implements RuntimeExecutor {
|
|||||||
// check if the input is connected to another node
|
// check if the input is connected to another node
|
||||||
const inputNode = node.state.inputNodes[key];
|
const inputNode = node.state.inputNodes[key];
|
||||||
if (inputNode) {
|
if (inputNode) {
|
||||||
if (results[inputNode.id] === undefined) {
|
if (this.results[inputNode.id] === undefined) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Node ${node.type} is missing input from node ${inputNode.type}`
|
`Node ${node.type} is missing input from node ${inputNode.type}`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return results[inputNode.id];
|
return this.results[inputNode.id];
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the value is stored in the node itself, we use that value
|
// If the value is stored in the node itself, we use that value
|
||||||
@@ -277,7 +280,7 @@ export class MemoryRuntimeExecutor implements RuntimeExecutor {
|
|||||||
b = performance.now();
|
b = performance.now();
|
||||||
|
|
||||||
if (this.cache && node.id !== outputNode.id) {
|
if (this.cache && node.id !== outputNode.id) {
|
||||||
this.cache.set(inputHash, results[node.id]);
|
this.cache.set(inputHash, this.results[node.id]);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.perf?.addPoint('node/' + node_type.id, b - a);
|
this.perf?.addPoint('node/' + node_type.id, b - a);
|
||||||
@@ -290,7 +293,7 @@ export class MemoryRuntimeExecutor implements RuntimeExecutor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// return the result of the parent of the output node
|
// return the result of the parent of the output node
|
||||||
const res = results[outputNode.id];
|
const res = this.results[outputNode.id];
|
||||||
|
|
||||||
if (this.cache) {
|
if (this.cache) {
|
||||||
this.cache.size = sortedNodes.length * 2;
|
this.cache.size = sortedNodes.length * 2;
|
||||||
|
|||||||
@@ -90,11 +90,6 @@
|
|||||||
let graphSettingTypes = $state({
|
let graphSettingTypes = $state({
|
||||||
randomSeed: { type: 'boolean', value: false }
|
randomSeed: { type: 'boolean', value: false }
|
||||||
});
|
});
|
||||||
$effect(() => {
|
|
||||||
if (graphSettings && graphSettingTypes) {
|
|
||||||
manager?.setSettings($state.snapshot(graphSettings));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
async function update(
|
async function update(
|
||||||
g: Graph,
|
g: Graph,
|
||||||
|
|||||||
@@ -3,6 +3,6 @@
|
|||||||
const { children } = $props<{ children?: Snippet }>();
|
const { children } = $props<{ children?: Snippet }>();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<main class="w-screen overflow-x-hidden">
|
<main class="w-screen h-screen overflow-x-hidden">
|
||||||
{@render children()}
|
{@render children()}
|
||||||
</main>
|
</main>
|
||||||
|
|||||||
@@ -44,8 +44,9 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$effect(() => {
|
let graphSettings = $state<Record<string, any>>({});
|
||||||
fetchNodeData(activeNode.value);
|
let graphSettingTypes = $state({
|
||||||
|
randomSeed: { type: "boolean", value: false },
|
||||||
});
|
});
|
||||||
|
|
||||||
$effect(() => {
|
$effect(() => {
|
||||||
@@ -61,19 +62,11 @@
|
|||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="node-wrapper absolute bottom-8 left-8">
|
|
||||||
{#if nodeInstance}
|
|
||||||
<NodeHTML inView position="relative" z={5} bind:node={nodeInstance} />
|
|
||||||
{/if}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<Grid.Row>
|
<Grid.Row>
|
||||||
<Grid.Cell>
|
<Grid.Cell>
|
||||||
<pre>
|
{#if result}
|
||||||
<code>
|
<pre><code>{JSON.stringify(decodeNestedArray(result))}</code></pre>
|
||||||
{JSON.stringify(nodeInstance?.props)}
|
{/if}
|
||||||
</code>
|
|
||||||
</pre>
|
|
||||||
</Grid.Cell>
|
</Grid.Cell>
|
||||||
|
|
||||||
<Grid.Cell>
|
<Grid.Cell>
|
||||||
|
|||||||
53
app/src/routes/dev/dev-graph.json
Normal file
53
app/src/routes/dev/dev-graph.json
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
{
|
||||||
|
"settings": {
|
||||||
|
"resolution.circle": 26,
|
||||||
|
"resolution.curve": 39
|
||||||
|
},
|
||||||
|
"nodes": [
|
||||||
|
{
|
||||||
|
"id": 9,
|
||||||
|
"position": [
|
||||||
|
220,
|
||||||
|
80
|
||||||
|
],
|
||||||
|
"type": "max/plantarium/output",
|
||||||
|
"props": {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 10,
|
||||||
|
"position": [
|
||||||
|
195,
|
||||||
|
80
|
||||||
|
],
|
||||||
|
"type": "max/plantarium/math",
|
||||||
|
"props": {
|
||||||
|
"op_type": 0,
|
||||||
|
"a": 2,
|
||||||
|
"b": 2
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 11,
|
||||||
|
"position": [
|
||||||
|
170,
|
||||||
|
80
|
||||||
|
],
|
||||||
|
"type": "max/plantarium/float",
|
||||||
|
"props": {
|
||||||
|
"value": 0.1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 12,
|
||||||
|
"position": [
|
||||||
|
170,
|
||||||
|
100
|
||||||
|
],
|
||||||
|
"type": "max/plantarium/float",
|
||||||
|
"props": {
|
||||||
|
"value": 0.1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"edges": []
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user