fix: gravity node
All checks were successful
Deploy to GitHub Pages / build_site (push) Successful in 2m35s

This commit is contained in:
2024-05-02 18:49:08 +02:00
parent dca4469f55
commit 26d3f6a2f1
31 changed files with 1557 additions and 536 deletions

View File

@ -13,12 +13,13 @@
"@nodes/ui": "link:../packages/ui",
"@nodes/utils": "link:../packages/utils",
"@sveltejs/kit": "^2.5.7",
"@threlte/core": "next",
"@threlte/extras": "next",
"@threlte/core": "^7.3.0",
"@threlte/extras": "^8.11.2",
"@types/three": "^0.164.0",
"@unocss/reset": "^0.59.4",
"comlink": "^4.4.1",
"file-saver": "^2.0.5",
"idb": "^8.0.0",
"jsondiffpatch": "^0.6.0",
"three": "^0.164.1"
},
@ -26,11 +27,11 @@
"@iconify-json/tabler": "^1.1.110",
"@nodes/types": "link:../packages/types",
"@sveltejs/adapter-static": "^3.0.1",
"@sveltejs/vite-plugin-svelte": "next",
"@sveltejs/vite-plugin-svelte": "^3.1.0",
"@tsconfig/svelte": "^5.0.4",
"@types/file-saver": "^2.0.7",
"@unocss/preset-icons": "^0.59.4",
"svelte": "5.0.0-next.118",
"svelte": "^4.2.15",
"svelte-check": "^3.7.0",
"tslib": "^2.6.2",
"typescript": "^5.4.5",

View File

@ -407,7 +407,10 @@ export class GraphManager extends EventEmitter<{ "save": Graph, "result": any, "
// check if socket types match
const fromSocketType = from.tmp?.type?.outputs?.[fromSocket];
const toSocketType = to.tmp?.type?.inputs?.[toSocket]?.type;
const toSocketType = [to.tmp?.type?.inputs?.[toSocket]?.type];
if (to.tmp?.type?.inputs?.[toSocket]?.accepts) {
toSocketType.push(...(to?.tmp?.type?.inputs?.[toSocket]?.accepts || []));
}
if (!areSocketsCompatible(fromSocketType, toSocketType)) {
logger.error(`Socket types do not match: ${fromSocketType} !== ${toSocketType}`);
@ -534,7 +537,11 @@ export class GraphManager extends EventEmitter<{ "save": Graph, "result": any, "
const inputs = node?.tmp?.type?.inputs;
if (!inputs) continue;
for (const key in inputs) {
if (areSocketsCompatible(ownType, inputs[key].type) && edges.get(node.id) !== key) {
const otherType = [inputs[key].type];
otherType.push(...(inputs[key].accepts || []));
if (areSocketsCompatible(ownType, otherType) && edges.get(node.id) !== key) {
sockets.push([node, key]);
}
}

View File

@ -12,7 +12,7 @@
import Camera from "../Camera.svelte";
import GraphView from "./GraphView.svelte";
import type { Node, NodeId, Node as NodeType, Socket } from "@nodes/types";
import { GraphSchema, NodeDefinitionSchema } from "@nodes/types";
import { GraphSchema } from "@nodes/types";
import FloatingEdge from "../edges/FloatingEdge.svelte";
import {
activeNodeId,
@ -25,7 +25,6 @@
import { createKeyMap } from "../../helpers/createKeyMap";
import BoxSelection from "../BoxSelection.svelte";
import AddMenu from "../AddMenu.svelte";
import { createWasmWrapper } from "@nodes/utils";
import HelpView from "../HelpView.svelte";
import FileSaver from "file-saver";
@ -819,11 +818,10 @@
isDragging = false;
if (!event.dataTransfer) return;
const nodeId = event.dataTransfer.getData("data/node-id") as NodeId;
let mx = event.clientX - rect.x;
let my = event.clientY - rect.y;
if (nodeId) {
let mx = event.clientX - rect.x;
let my = event.clientY - rect.y;
let nodeOffsetX = event.dataTransfer.getData("data/node-offset-x");
let nodeOffsetY = event.dataTransfer.getData("data/node-offset-y");
if (nodeOffsetX && nodeOffsetY) {
@ -852,13 +850,16 @@
if (file.type === "application/wasm") {
const reader = new FileReader();
reader.onload = (e) => {
const buffer = e.target?.result as Buffer;
if (buffer) {
const wrapper = createWasmWrapper(buffer);
const definition = wrapper.get_definition();
const res = NodeDefinitionSchema.parse(definition);
console.log(res);
reader.onload = async (e) => {
const buffer = e.target?.result;
if (buffer?.constructor === ArrayBuffer) {
const nodeType = await manager.registry.register(buffer);
manager.createNode({
type: nodeType.id,
props: {},
position: projectScreenToWorld(mx, my),
});
}
};
reader.readAsArrayBuffer(file);

View File

@ -0,0 +1,38 @@
import type { RuntimeCache } from '@nodes/types';
import { openDB, type IDBPDatabase } from 'idb';
export class IndexDBCache implements RuntimeCache<ArrayBuffer> {
size: number = 100;
db: Promise<IDBPDatabase<ArrayBuffer>>;
private _cache = new Map<string, ArrayBuffer>();
constructor(id: string) {
this.db = openDB<ArrayBuffer>('cache/' + id, 1, {
upgrade(db) {
db.createObjectStore('keyval');
},
});
}
async get(key: string) {
let res = this._cache.get(key);
if (!res) {
res = await (await this.db).get('keyval', key);
}
if (res) {
this._cache.set(key, res);
}
return res;
}
async set(key: string, value: ArrayBuffer) {
this._cache.set(key, value);
const db = await this.db;
await db.put('keyval', value, key);
}
clear() {
this.db.then(db => db.clear('keyval'));
}
}

View File

@ -1,4 +1,4 @@
import type { NodeRegistry, NodeDefinition } from "@nodes/types";
import { type NodeRegistry, type NodeDefinition, NodeDefinitionSchema, type RuntimeCache } from "@nodes/types";
import { createWasmWrapper } from "@nodes/utils";
import { createLogger } from "./helpers";
@ -10,6 +10,8 @@ export class RemoteNodeRegistry implements NodeRegistry {
status: "loading" | "ready" | "error" = "loading";
private nodes: Map<string, NodeDefinition> = new Map();
cache?: RuntimeCache<ArrayBuffer>;
fetch: typeof fetch = globalThis.fetch.bind(globalThis);
constructor(private url: string) { }
@ -46,6 +48,22 @@ export class RemoteNodeRegistry implements NodeRegistry {
return response.json()
}
private async fetchNodeWasm(nodeId: `${string}/${string}/${string}`) {
const response = await this.fetch(`${this.url}/nodes/${nodeId}.wasm`);
if (!response.ok) {
if (this.cache) {
let value = await this.cache.get(nodeId);
if (value) {
return value;
}
}
throw new Error(`Failed to load node wasm ${nodeId}`);
}
return response.arrayBuffer();
}
async load(nodeIds: `${string}/${string}/${string}`[]) {
const a = performance.now();
@ -55,26 +73,12 @@ export class RemoteNodeRegistry implements NodeRegistry {
return this.nodes.get(id)!;
}
const response = await this.fetch(`${this.url}/nodes/${id}.wasm`);
if (!response.ok) {
throw new Error(`Failed to load node wasm ${id}`);
}
const wasmBuffer = await this.fetchNodeWasm(id);
const wasmBuffer = await response.arrayBuffer();
return this.register(wasmBuffer);
const wrapper = createWasmWrapper(wasmBuffer);
const definition = wrapper.get_definition();
return {
...definition,
execute: wrapper.execute
};
}));
for (const node of nodes) {
this.nodes.set(node.id, node);
}
const duration = performance.now() - a;
@ -87,6 +91,31 @@ export class RemoteNodeRegistry implements NodeRegistry {
return nodes
}
async register(wasmBuffer: ArrayBuffer) {
const wrapper = createWasmWrapper(wasmBuffer);
const definition = NodeDefinitionSchema.safeParse(wrapper.get_definition());
if (definition.error) {
console.error(definition.error);
throw definition.error;
}
if (this.cache) {
await this.cache.set(definition.data.id, wasmBuffer);
}
let node = {
...definition.data,
execute: wrapper.execute
}
this.nodes.set(definition.data.id, node);
return node;
}
getNode(id: string) {
return this.nodes.get(id);
}

View File

@ -17,7 +17,7 @@
camera: Vector3Tuple;
target: Vector3Tuple;
}>("nodes.camera.transform", {
camera: [0, 0, 10],
camera: [10, 10, 10],
target: [0, 0, 0],
});

View File

@ -11,20 +11,43 @@
import { AppSettings } from "../settings/app-settings";
import Camera from "./Camera.svelte";
const d = useThrelte();
const threlte = useThrelte();
export const invalidate = d.invalidate;
export const invalidate = function () {
if (scene) {
geometries = scene.children
.filter(
(child) => "geometry" in child && child.isObject3D && child.geometry,
)
.map((child) => {
return child.geometry;
});
}
export let geometries: BufferGeometry[];
if (geometries && scene && centerCamera) {
const aabb = new Box3().setFromObject(scene);
center = aabb
.getCenter(new Vector3())
.max(new Vector3(-4, -4, -4))
.min(new Vector3(4, 4, 4));
}
threlte.invalidate();
};
let geometries: BufferGeometry[] = [];
export let lines: Vector3[][];
export let scene;
let geos: Group;
$: scene = geos;
export let geoGroup: Group;
export let scene: Group;
export let centerCamera: boolean = true;
let center = new Vector3(0, 4, 0);
$: if ($AppSettings && scene) {
scene.children.forEach((child) => {
child.material.wireframe = $AppSettings.wireframe;
threlte.invalidate();
});
}
function getPosition(geo: BufferGeometry, i: number) {
return [
geo.attributes.position.array[i],
@ -32,14 +55,6 @@
geo.attributes.position.array[i + 2],
] as Vector3Tuple;
}
$: if (geometries && geos && centerCamera) {
const aabb = new Box3().setFromObject(geos);
center = aabb
.getCenter(new Vector3())
.max(new Vector3(-4, -4, -4))
.min(new Vector3(4, 4, 4));
}
</script>
<Camera {center} {centerCamera} />
@ -48,7 +63,7 @@
<T.GridHelper args={[20, 20]} />
{/if}
<T.Group bind:ref={geos}>
<T.Group>
{#each geometries as geo}
{#if $AppSettings.showIndices}
{#each geo.attributes.position.array as _, i}
@ -66,7 +81,7 @@
{/if}
{/each}
<T.Group bind:ref={geoGroup}></T.Group>
<T.Group bind:ref={scene}></T.Group>
</T.Group>
{#if $AppSettings.showStemLines && lines}

View File

@ -1,7 +1,7 @@
<script lang="ts">
import { Canvas } from "@threlte/core";
import Scene from "./Scene.svelte";
import { BufferGeometry, Group, Vector3 } from "three";
import { Group, Vector3 } from "three";
import { updateGeometries } from "./updateGeometries";
import { decodeFloat, splitNestedArray } from "@nodes/utils";
@ -12,9 +12,6 @@
export let perf: PerformanceStore;
export let scene: Group;
let geoGroup: Group;
let geometries: BufferGeometry[] = [];
let lines: Vector3[][] = [];
let invalidate: () => void;
@ -53,7 +50,7 @@
perf?.addPoint("update-geometries");
const { totalVertices, totalFaces } = updateGeometries(inputs, geoGroup);
const { totalVertices, totalFaces } = updateGeometries(inputs, scene);
perf?.endPoint();
perf?.addPoint("total-vertices", totalVertices);
@ -63,12 +60,5 @@
</script>
<Canvas>
<Scene
bind:scene
bind:geoGroup
bind:invalidate
{geometries}
{lines}
{centerCamera}
/>
<Scene bind:scene bind:invalidate {lines} {centerCamera} />
</Canvas>

View File

@ -1,6 +1,6 @@
import localStore from "$lib/helpers/localStore";
export const AppSettings = localStore("node-settings", {
export const AppSettings = localStore("node.settings", {
theme: 0,
showGrid: true,
showNodeGrid: true,

View File

@ -2,9 +2,12 @@ import { MemoryRuntimeExecutor, MemoryRuntimeCache } from "./runtime-executor";
import { RemoteNodeRegistry } from "./node-registry-client";
import type { Graph } from "@nodes/types";
import { createPerformanceStore } from "./performance/store";
import { IndexDBCache } from "./node-registry-cache";
const cache = new MemoryRuntimeCache();
const indexDbCache = new IndexDBCache("node-registry");
const nodeRegistry = new RemoteNodeRegistry("");
nodeRegistry.cache = indexDbCache;
const executor = new MemoryRuntimeExecutor(nodeRegistry, cache);
const performanceStore = createPerformanceStore("worker");

View File

@ -13,7 +13,6 @@
import { createKeyMap } from "$lib/helpers/createKeyMap";
import NodeStore from "$lib/node-store/NodeStore.svelte";
import type { GraphManager } from "$lib/graph-interface/graph-manager";
import { setContext } from "svelte";
import ActiveNodeSettings from "$lib/settings/panels/ActiveNodeSettings.svelte";
import PerformanceViewer from "$lib/performance/PerformanceViewer.svelte";
import Panel from "$lib/settings/Panel.svelte";
@ -26,12 +25,15 @@
MemoryRuntimeCache,
MemoryRuntimeExecutor,
} from "$lib/runtime-executor";
import { IndexDBCache } from "$lib/node-registry-cache";
import { decodeNestedArray, fastHashString } from "@nodes/utils";
import BenchmarkPanel from "$lib/settings/panels/BenchmarkPanel.svelte";
let performanceStore = createPerformanceStore("page");
const registryCache = new IndexDBCache("node-registry");
const nodeRegistry = new RemoteNodeRegistry("");
nodeRegistry.cache = registryCache;
const workerRuntime = new WorkerRuntimeExecutor();
const runtimeCache = new MemoryRuntimeCache();
const memoryRuntime = new MemoryRuntimeExecutor(nodeRegistry, runtimeCache);
@ -39,6 +41,15 @@
globalThis.decode = decodeNestedArray;
globalThis.clearCache = () => {
registryCache.clear();
runtimeCache.clear();
localStorage.clear();
setTimeout(() => {
window.location.reload();
}, 500);
};
$: runtime = $AppSettings.useWorker ? workerRuntime : memoryRuntime;
let activeNode: Node | undefined;
@ -51,6 +62,9 @@
let manager: GraphManager;
let managerStatus: Writable<"loading" | "error" | "idle">;
$: if (manager) {
managerStatus = manager.status;
}
async function randomGenerate() {
const g = manager.serialize();
@ -82,6 +96,7 @@
async function handleResult(_graph: Graph, _settings: Record<string, any>) {
if (!_settings) return;
if ($managerStatus !== "idle") return;
const inputHash = fastHashString(
JSON.stringify(_graph) + JSON.stringify(_settings),
);
@ -131,6 +146,10 @@
return true;
}
$: if ($managerStatus === "idle") {
handleResult(manager.serialize(), $graphSettings);
}
$: if (AppSettings) {
//@ts-ignore
AppSettingTypes.debug.stressTest.loadGrid.callback = () => {