Compare commits

...

2 Commits

Author SHA1 Message Date
Max Richter
ef479d0557 chore: update
All checks were successful
Deploy to GitHub Pages / build_site (push) Successful in 3m50s
2025-12-02 17:31:58 +01:00
Max Richter
a1c926c3cf fix: better handle randomGeneration 2025-12-02 17:27:34 +01:00
7 changed files with 102 additions and 89 deletions

View File

@@ -4,6 +4,12 @@
import type { Node, NodeType } from "@nodarium/types"; import type { Node, NodeType } from "@nodarium/types";
import { getGraphManager, getGraphState } from "../graph/state.svelte"; import { getGraphManager, getGraphState } from "../graph/state.svelte";
type Props = {
onnode: (n: Node) => void;
};
const { onnode }: Props = $props();
const graph = getGraphManager(); const graph = getGraphManager();
const graphState = getGraphState(); const graphState = getGraphState();
@@ -35,24 +41,12 @@
function handleNodeCreation(nodeType: Node["type"]) { function handleNodeCreation(nodeType: Node["type"]) {
if (!graphState.addMenuPosition) return; if (!graphState.addMenuPosition) return;
onnode?.({
const newNode = graph.createNode({ id: -1,
type: nodeType, type: nodeType,
position: graphState.addMenuPosition, position: [...graphState.addMenuPosition],
props: {}, props: {},
}); });
const edgeInputSocket = graphState.activeSocket;
if (edgeInputSocket && newNode) {
if (typeof edgeInputSocket.index === "number") {
graph.smartConnect(edgeInputSocket.node, newNode);
} else {
graph.smartConnect(newNode, edgeInputSocket.node);
}
}
graphState.activeSocket = null;
graphState.addMenuPosition = null;
} }
function handleKeyDown(event: KeyboardEvent) { function handleKeyDown(event: KeyboardEvent) {

View File

@@ -34,12 +34,14 @@
import { appSettings } from "$lib/settings/app-settings.svelte"; import { appSettings } from "$lib/settings/app-settings.svelte";
type Props = { type Props = {
from: { x: number; y: number }; x1: number;
to: { x: number; y: number }; y1: number;
x2: number;
y2: number;
z: number; z: number;
}; };
const { from, to, z }: Props = $props(); const { x1, y1, x2, y2, z }: Props = $props();
const thickness = $derived(Math.max(0.001, 0.00082 * Math.exp(0.055 * z))); const thickness = $derived(Math.max(0.001, 0.00082 * Math.exp(0.055 * z)));
@@ -48,10 +50,9 @@
let lastId: string | null = null; let lastId: string | null = null;
function update() { function update() {
const new_x = to.x - from.x; const new_x = x2 - x1;
const new_y = to.y - from.y; const new_y = y2 - y1;
const curveId = `${from.x}-${from.y}-${to.x}-${to.y}`; const curveId = `${x1}-${y1}-${x2}-${y2}`;
if (lastId === curveId) { if (lastId === curveId) {
return; return;
} }
@@ -79,15 +80,15 @@
} }
$effect(() => { $effect(() => {
if (from || to) { if (x1 || x2 || y1 || y2) {
update(); update();
} }
}); });
</script> </script>
<T.Mesh <T.Mesh
position.x={from.x} position.x={x1}
position.z={from.y} position.z={y1}
position.y={0.8} position.y={0.8}
rotation.x={-Math.PI / 2} rotation.x={-Math.PI / 2}
material={circleMaterial} material={circleMaterial}
@@ -96,8 +97,8 @@
</T.Mesh> </T.Mesh>
<T.Mesh <T.Mesh
position.x={to.x} position.x={x2}
position.z={to.y} position.z={y2}
position.y={0.8} position.y={0.8}
rotation.x={-Math.PI / 2} rotation.x={-Math.PI / 2}
material={circleMaterial} material={circleMaterial}
@@ -105,11 +106,6 @@
<T.CircleGeometry args={[0.5, 16]} /> <T.CircleGeometry args={[0.5, 16]} />
</T.Mesh> </T.Mesh>
<T.Mesh <T.Mesh bind:ref={mesh} position.x={x1} position.z={y1} position.y={0.1}>
bind:ref={mesh}
position.x={from.x}
position.z={from.y}
position.y={0.1}
>
<MeshLineMaterial width={thickness} color={lineColor} /> <MeshLineMaterial width={thickness} color={lineColor} />
</T.Mesh> </T.Mesh>

View File

@@ -1,12 +0,0 @@
<script lang="ts">
import Edge from "./Edge.svelte";
type Props = {
from: { x: number; y: number };
to: { x: number; y: number };
z: number;
};
const { from, to, z }: Props = $props();
</script>
<Edge {from} {to} {z} />

View File

@@ -1,5 +1,5 @@
<script lang="ts"> <script lang="ts">
import type { Edge } from "@nodarium/types"; import type { Edge, Node } from "@nodarium/types";
import { onMount } from "svelte"; import { onMount } from "svelte";
import { createKeyMap } from "../../helpers/createKeyMap"; import { createKeyMap } from "../../helpers/createKeyMap";
import AddMenu from "../components/AddMenu.svelte"; import AddMenu from "../components/AddMenu.svelte";
@@ -8,7 +8,6 @@
import EdgeEl from "../edges/Edge.svelte"; import EdgeEl from "../edges/Edge.svelte";
import NodeEl from "../node/Node.svelte"; import NodeEl from "../node/Node.svelte";
import Camera from "../components/Camera.svelte"; import Camera from "../components/Camera.svelte";
import FloatingEdge from "../edges/FloatingEdge.svelte";
import { Canvas } from "@threlte/core"; import { Canvas } from "@threlte/core";
import HelpView from "../components/HelpView.svelte"; import HelpView from "../components/HelpView.svelte";
import { getGraphManager, getGraphState } from "./state.svelte"; import { getGraphManager, getGraphState } from "./state.svelte";
@@ -27,10 +26,6 @@
const fileDropEvents = new FileDropEventManager(graph, graphState); const fileDropEvents = new FileDropEventManager(graph, graphState);
const mouseEvents = new MouseEventManager(graph, graphState); const mouseEvents = new MouseEventManager(graph, graphState);
function getEdgeId(edge: Edge) {
return `${edge[0].id}-${edge[1]}-${edge[2].id}-${edge[3]}`;
}
function getEdgePosition(edge: Edge) { function getEdgePosition(edge: Edge) {
const fromNode = graph.nodes.get(edge[0].id); const fromNode = graph.nodes.get(edge[0].id);
const toNode = graph.nodes.get(edge[2].id); const toNode = graph.nodes.get(edge[2].id);
@@ -45,6 +40,60 @@
return [pos1[0], pos1[1], pos2[0], pos2[1]]; return [pos1[0], pos1[1], pos2[0], pos2[1]];
} }
function handleNodeCreation(node: Node) {
const newNode = graph.createNode({
type: node.type,
position: node.position,
props: node.props,
});
if (!newNode) return;
if (graphState.activeSocket) {
if (typeof graphState.activeSocket.index === "number") {
const socketType =
graphState.activeSocket.node.tmp?.type?.outputs?.[
graphState.activeSocket.index
];
const input = Object.entries(newNode?.tmp?.type?.inputs || {}).find(
(inp) => inp[1].type === socketType,
);
if (input) {
graph.createEdge(
graphState.activeSocket.node,
graphState.activeSocket.index,
newNode,
input[0],
);
}
} else {
const socketType =
graphState.activeSocket.node.tmp?.type?.inputs?.[
graphState.activeSocket.index
];
const output = newNode.tmp?.type?.outputs?.find((out) => {
if (socketType?.type === out) return true;
if (socketType?.accepts?.includes(out as any)) return true;
return false;
});
if (output) {
graph.createEdge(
newNode,
output.indexOf(output),
graphState.activeSocket.node,
graphState.activeSocket.index,
);
}
}
}
graphState.activeSocket = null;
graphState.addMenuPosition = null;
}
onMount(() => { onMount(() => {
if (localStorage.getItem("cameraPosition")) { if (localStorage.getItem("cameraPosition")) {
const cPosition = JSON.parse(localStorage.getItem("cameraPosition")!); const cPosition = JSON.parse(localStorage.getItem("cameraPosition")!);
@@ -120,36 +169,22 @@
{#if graph.status === "idle"} {#if graph.status === "idle"}
{#if graphState.addMenuPosition} {#if graphState.addMenuPosition}
<AddMenu /> <AddMenu onnode={handleNodeCreation} />
{/if} {/if}
{#if graphState.activeSocket} {#if graphState.activeSocket}
<FloatingEdge <EdgeEl
z={graphState.cameraPosition[2]} z={graphState.cameraPosition[2]}
from={{ x1={graphState.activeSocket.position[0]}
x: graphState.activeSocket.position[0], y1={graphState.activeSocket.position[1]}
y: graphState.activeSocket.position[1], x2={graphState.edgeEndPosition?.[0] ?? graphState.mousePosition[0]}
}} y2={graphState.edgeEndPosition?.[1] ?? graphState.mousePosition[1]}
to={{
x: graphState.edgeEndPosition?.[0] ?? graphState.mousePosition[0],
y: graphState.edgeEndPosition?.[1] ?? graphState.mousePosition[1],
}}
/> />
{/if} {/if}
{#each graph.edges as edge (getEdgeId(edge))} {#each graph.edges as edge}
{@const [x1, y1, x2, y2] = getEdgePosition(edge)} {@const [x1, y1, x2, y2] = getEdgePosition(edge)}
<EdgeEl <EdgeEl z={graphState.cameraPosition[2]} {x1} {y1} {x2} {y2} />
z={graphState.cameraPosition[2]}
from={{
x: x1,
y: y1,
}}
to={{
x: x2,
y: y2,
}}
/>
{/each} {/each}
<HTML transform={false}> <HTML transform={false}>

View File

@@ -1,7 +1,7 @@
<script lang="ts"> <script lang="ts">
import type { Node } from "@nodarium/types"; import type { Node } from "@nodarium/types";
import { onMount } from "svelte"; import { onMount } from "svelte";
import { getGraphManager, getGraphState } from "../graph/state.svelte"; import { getGraphState } from "../graph/state.svelte";
import { T } from "@threlte/core"; import { T } from "@threlte/core";
import { type Mesh } from "three"; import { type Mesh } from "three";
import NodeFrag from "./Node.frag"; import NodeFrag from "./Node.frag";
@@ -21,15 +21,14 @@
const isActive = $derived(graphState.activeNodeId === node.id); const isActive = $derived(graphState.activeNodeId === node.id);
const isSelected = $derived(graphState.selectedNodes.has(node.id)); const isSelected = $derived(graphState.selectedNodes.has(node.id));
let strokeColor = $state(colors.selected); const strokeColor = $derived(
$effect(() => { appSettings.value.theme &&
appSettings.value.theme; (isSelected
strokeColor = isSelected
? colors.selected ? colors.selected
: isActive : isActive
? colors.active ? colors.active
: colors.outline; : colors.outline),
}); );
let meshRef: Mesh | undefined = $state(); let meshRef: Mesh | undefined = $state();

View File

@@ -58,7 +58,7 @@ function getValue(input: NodeInput, value?: unknown) {
export class MemoryRuntimeExecutor implements RuntimeExecutor { export class MemoryRuntimeExecutor implements RuntimeExecutor {
private definitionMap: Map<string, NodeDefinition> = new Map(); private definitionMap: Map<string, NodeDefinition> = new Map();
private randomSeed = Math.floor(Math.random() * 100000000); private seed = Math.floor(Math.random() * 100000000);
perf?: PerformanceStore; perf?: PerformanceStore;
@@ -181,6 +181,10 @@ 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> = {}; const results: Record<string, Int32Array> = {};
if (settings["randomSeed"]) {
this.seed = Math.floor(Math.random() * 100000000);
}
for (const node of sortedNodes) { for (const node of sortedNodes) {
const node_type = this.definitionMap.get(node.type)!; const node_type = this.definitionMap.get(node.type)!;
@@ -195,10 +199,7 @@ export class MemoryRuntimeExecutor implements RuntimeExecutor {
const inputs = Object.entries(node_type.inputs || {}).map( const inputs = Object.entries(node_type.inputs || {}).map(
([key, input]) => { ([key, input]) => {
if (input.type === "seed") { if (input.type === "seed") {
if (settings["randomSeed"] === true) { return this.seed;
this.randomSeed = Math.floor(Math.random() * 100000000);
}
return this.randomSeed;
} }
// If the input is linked to a setting, we use that value // If the input is linked to a setting, we use that value

View File

@@ -69,7 +69,7 @@
{ {
key: "r", key: "r",
description: "Regenerate the plant model", description: "Regenerate the plant model",
callback: randomGenerate, callback: () => randomGenerate(),
}, },
]); ]);
let graphSettings = $state<Record<string, any>>({}); let graphSettings = $state<Record<string, any>>({});