feat: improve zoom performance by adding a tiny rand offset to nodes
This commit is contained in:
parent
06ba3a8fe9
commit
c33e2642e1
@ -119,6 +119,7 @@ export class GraphManager extends EventEmitter<{ "save": Graph, "result": any }>
|
|||||||
const nodeType = this.nodeRegistry.getNode(node.type);
|
const nodeType = this.nodeRegistry.getNode(node.type);
|
||||||
if (nodeType) {
|
if (nodeType) {
|
||||||
node.tmp = {
|
node.tmp = {
|
||||||
|
random: (Math.random() - 0.5) * 2,
|
||||||
type: nodeType
|
type: nodeType
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -173,6 +174,7 @@ export class GraphManager extends EventEmitter<{ "save": Graph, "result": any }>
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
node.tmp = node.tmp || {};
|
node.tmp = node.tmp || {};
|
||||||
|
node.tmp.random = (Math.random() - 0.5) * 2;
|
||||||
node.tmp.type = nodeType;
|
node.tmp.type = nodeType;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,78 +1,80 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import type { Edge as EdgeType, Node as NodeType } from '@nodes/types';
|
import type { Edge as EdgeType, Node as NodeType } from "@nodes/types";
|
||||||
import { HTML } from '@threlte/extras';
|
import { HTML } from "@threlte/extras";
|
||||||
import Edge from '../edges/Edge.svelte';
|
import Edge from "../edges/Edge.svelte";
|
||||||
import Node from '../node/Node.svelte';
|
import Node from "../node/Node.svelte";
|
||||||
import { getContext, onMount } from 'svelte';
|
import { getContext, onMount } from "svelte";
|
||||||
import type { Writable } from 'svelte/store';
|
import type { Writable } from "svelte/store";
|
||||||
import { activeSocket } from './stores.js';
|
import { activeSocket } from "./stores.js";
|
||||||
|
|
||||||
export let nodes: Writable<Map<number, NodeType>>;
|
export let nodes: Writable<Map<number, NodeType>>;
|
||||||
export let edges: Writable<EdgeType[]>;
|
export let edges: Writable<EdgeType[]>;
|
||||||
|
|
||||||
export let cameraPosition = [0, 0, 4];
|
export let cameraPosition = [0, 0, 4];
|
||||||
|
|
||||||
const isNodeInView = getContext<(n: NodeType) => boolean>('isNodeInView');
|
const isNodeInView = getContext<(n: NodeType) => boolean>("isNodeInView");
|
||||||
|
|
||||||
const getSocketPosition =
|
const getSocketPosition =
|
||||||
getContext<(node: NodeType, index: string | number) => [number, number]>('getSocketPosition');
|
getContext<(node: NodeType, index: string | number) => [number, number]>(
|
||||||
|
"getSocketPosition",
|
||||||
|
);
|
||||||
|
|
||||||
function getEdgePosition(edge: EdgeType) {
|
function getEdgePosition(edge: EdgeType) {
|
||||||
const pos1 = getSocketPosition(edge[0], edge[1]);
|
const pos1 = getSocketPosition(edge[0], edge[1]);
|
||||||
const pos2 = getSocketPosition(edge[2], edge[3]);
|
const pos2 = getSocketPosition(edge[2], edge[3]);
|
||||||
|
|
||||||
return [pos1[0], pos1[1], pos2[0], pos2[1]];
|
return [pos1[0], pos1[1], pos2[0], pos2[1]];
|
||||||
}
|
}
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
for (const node of $nodes.values()) {
|
for (const node of $nodes.values()) {
|
||||||
if (node?.tmp?.ref) {
|
if (node?.tmp?.ref) {
|
||||||
node.tmp.ref.style.setProperty('--nx', `${node.position[0] * 10}px`);
|
node.tmp.ref.style.setProperty("--nx", `${node.position[0] * 10}px`);
|
||||||
node.tmp.ref.style.setProperty('--ny', `${node.position[1] * 10}px`);
|
node.tmp.ref.style.setProperty("--ny", `${node.position[1] * 10}px`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#each $edges as edge (`${edge[0].id}-${edge[1]}-${edge[2].id}-${edge[3]}`)}
|
{#each $edges as edge (`${edge[0].id}-${edge[1]}-${edge[2].id}-${edge[3]}`)}
|
||||||
{@const pos = getEdgePosition(edge)}
|
{@const pos = getEdgePosition(edge)}
|
||||||
{@const [x1, y1, x2, y2] = pos}
|
{@const [x1, y1, x2, y2] = pos}
|
||||||
<Edge
|
<Edge
|
||||||
from={{
|
from={{
|
||||||
x: x1,
|
x: x1,
|
||||||
y: y1
|
y: y1,
|
||||||
}}
|
}}
|
||||||
to={{
|
to={{
|
||||||
x: x2,
|
x: x2,
|
||||||
y: y2
|
y: y2,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
{/each}
|
{/each}
|
||||||
|
|
||||||
<HTML transform={false}>
|
<HTML transform={false}>
|
||||||
<div
|
<div
|
||||||
role="tree"
|
role="tree"
|
||||||
id="graph"
|
id="graph"
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
class="wrapper"
|
class="wrapper"
|
||||||
class:zoom-small={cameraPosition[2] < 2}
|
style:transform={`scale(${cameraPosition[2] * 0.1})`}
|
||||||
class:hovering-sockets={activeSocket}
|
class:hovering-sockets={activeSocket}
|
||||||
style={`--cz: ${cameraPosition[2]}; --node-display: ${cameraPosition[2] < 2 ? 'none' : 'block'};`}
|
>
|
||||||
>
|
{#each $nodes.values() as node (node.id)}
|
||||||
{#each $nodes.values() as node (node.id)}
|
<Node
|
||||||
<Node {node} inView={cameraPosition && isNodeInView(node)} z={cameraPosition[2]} />
|
{node}
|
||||||
{/each}
|
inView={cameraPosition && isNodeInView(node)}
|
||||||
</div>
|
z={cameraPosition[2]}
|
||||||
|
/>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
</HTML>
|
</HTML>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.wrapper {
|
.wrapper {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
z-index: 100;
|
z-index: 100;
|
||||||
width: 0px;
|
width: 0px;
|
||||||
height: 0px;
|
height: 0px;
|
||||||
transform: scale(calc(var(--cz) * 0.1));
|
}
|
||||||
display: var(--node-display, block);
|
|
||||||
opacity: calc((var(--cz) - 2.5) / 3.5);
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
@ -1,129 +1,138 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import type { Node } from '@nodes/types';
|
import type { Node } from "@nodes/types";
|
||||||
import { getContext, onMount } from 'svelte';
|
import { getContext, onMount } from "svelte";
|
||||||
import NodeHeader from './NodeHeader.svelte';
|
import NodeHeader from "./NodeHeader.svelte";
|
||||||
import NodeParameter from './NodeParameter.svelte';
|
import NodeParameter from "./NodeParameter.svelte";
|
||||||
import { activeNodeId, selectedNodes } from '../graph/stores.js';
|
import { activeNodeId, selectedNodes } from "../graph/stores.js";
|
||||||
import { T } from '@threlte/core';
|
import { T } from "@threlte/core";
|
||||||
import { Color, type Mesh } from 'three';
|
import { Color, type Mesh } from "three";
|
||||||
import NodeFrag from './Node.frag';
|
import NodeFrag from "./Node.frag";
|
||||||
import NodeVert from './Node.vert';
|
import NodeVert from "./Node.vert";
|
||||||
|
|
||||||
export let node: Node;
|
export let node: Node;
|
||||||
export let inView = true;
|
export let inView = true;
|
||||||
export let z = 2;
|
export let z = 2;
|
||||||
|
|
||||||
$: isActive = $activeNodeId === node.id;
|
$: isActive = $activeNodeId === node.id;
|
||||||
$: isSelected = !!$selectedNodes?.has(node.id);
|
$: isSelected = !!$selectedNodes?.has(node.id);
|
||||||
|
|
||||||
const updateNodePosition = getContext<(n: Node) => void>('updateNodePosition');
|
const updateNodePosition =
|
||||||
|
getContext<(n: Node) => void>("updateNodePosition");
|
||||||
|
|
||||||
const getNodeHeight = getContext<(n: string) => number>('getNodeHeight');
|
const getNodeHeight = getContext<(n: string) => number>("getNodeHeight");
|
||||||
|
|
||||||
const type = node?.tmp?.type;
|
const type = node?.tmp?.type;
|
||||||
|
|
||||||
const parameters = Object.entries(type?.inputs || {})
|
const zOffset = (node.tmp?.random || 0) * 0.5;
|
||||||
.filter((p) => p[1].type !== 'seed')
|
const zLimit = 2 - zOffset;
|
||||||
.filter((p) => !('setting' in p[1]));
|
$: visible = inView && z > zLimit;
|
||||||
|
|
||||||
let ref: HTMLDivElement;
|
const parameters = Object.entries(type?.inputs || {})
|
||||||
let meshRef: Mesh;
|
.filter((p) => p[1].type !== "seed")
|
||||||
|
.filter((p) => !("setting" in p[1]));
|
||||||
|
|
||||||
const height = getNodeHeight(node.type);
|
let ref: HTMLDivElement;
|
||||||
|
let meshRef: Mesh;
|
||||||
|
|
||||||
$: if (node && ref && meshRef) {
|
const height = getNodeHeight(node.type);
|
||||||
node.tmp = node.tmp || {};
|
|
||||||
node.tmp.ref = ref;
|
|
||||||
node.tmp.mesh = meshRef;
|
|
||||||
updateNodePosition(node);
|
|
||||||
}
|
|
||||||
|
|
||||||
onMount(() => {
|
$: if (node && ref && meshRef) {
|
||||||
node.tmp = node.tmp || {};
|
node.tmp = node.tmp || {};
|
||||||
node.tmp.ref = ref;
|
node.tmp.ref = ref;
|
||||||
node.tmp.mesh = meshRef;
|
node.tmp.mesh = meshRef;
|
||||||
updateNodePosition(node);
|
updateNodePosition(node);
|
||||||
});
|
}
|
||||||
|
|
||||||
const colorDark = new Color();
|
onMount(() => {
|
||||||
colorDark.setStyle('#151515');
|
node.tmp = node.tmp || {};
|
||||||
//colorDark.();
|
node.tmp.ref = ref;
|
||||||
|
node.tmp.mesh = meshRef;
|
||||||
|
updateNodePosition(node);
|
||||||
|
});
|
||||||
|
|
||||||
const colorBright = new Color('#202020');
|
const colorDark = new Color();
|
||||||
//colorBright.convertLinearToSRGB();
|
colorDark.setStyle("#151515");
|
||||||
|
//colorDark.();
|
||||||
|
|
||||||
|
const colorBright = new Color("#202020");
|
||||||
|
//colorBright.convertLinearToSRGB();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<T.Mesh
|
<T.Mesh
|
||||||
position.x={node.position[0] + 10}
|
position.x={node.position[0] + 10}
|
||||||
position.z={node.position[1] + height / 2}
|
position.z={node.position[1] + height / 2}
|
||||||
position.y={0.8}
|
position.y={0.8}
|
||||||
rotation.x={-Math.PI / 2}
|
rotation.x={-Math.PI / 2}
|
||||||
bind:ref={meshRef}
|
bind:ref={meshRef}
|
||||||
visible={z < 7}
|
visible={inView && z < 7}
|
||||||
>
|
>
|
||||||
<T.PlaneGeometry args={[20, height]} radius={1} />
|
<T.PlaneGeometry args={[20, height]} radius={1} />
|
||||||
<T.ShaderMaterial
|
<T.ShaderMaterial
|
||||||
vertexShader={NodeVert}
|
vertexShader={NodeVert}
|
||||||
fragmentShader={NodeFrag}
|
fragmentShader={NodeFrag}
|
||||||
transparent
|
transparent
|
||||||
uniforms={{
|
uniforms={{
|
||||||
uColorBright: { value: colorBright },
|
uColorBright: { value: colorBright },
|
||||||
uColorDark: { value: colorDark },
|
uColorDark: { value: colorDark },
|
||||||
uSelectedColor: { value: new Color('#9d5f28') },
|
uSelectedColor: { value: new Color("#9d5f28") },
|
||||||
uActiveColor: { value: new Color('white') },
|
uActiveColor: { value: new Color("white") },
|
||||||
uSelected: { value: false },
|
uSelected: { value: false },
|
||||||
uActive: { value: false },
|
uActive: { value: false },
|
||||||
uStrokeWidth: { value: 1.0 },
|
uStrokeWidth: { value: 1.0 },
|
||||||
uWidth: { value: 20 },
|
uWidth: { value: 20 },
|
||||||
uHeight: { value: height }
|
uHeight: { value: height },
|
||||||
}}
|
}}
|
||||||
uniforms.uSelected.value={isSelected}
|
uniforms.uSelected.value={isSelected}
|
||||||
uniforms.uActive.value={isActive}
|
uniforms.uActive.value={isActive}
|
||||||
uniforms.uStrokeWidth.value={(7 - z) / 3}
|
uniforms.uStrokeWidth.value={(7 - z) / 3}
|
||||||
/>
|
/>
|
||||||
</T.Mesh>
|
</T.Mesh>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
class="node"
|
class="node"
|
||||||
class:active={isActive}
|
class:active={isActive}
|
||||||
class:selected={isSelected}
|
style:--cz={z + zOffset}
|
||||||
class:out-of-view={!inView}
|
style:display={inView && z > zLimit ? "block" : "none"}
|
||||||
data-node-id={node.id}
|
class:selected={isSelected}
|
||||||
bind:this={ref}
|
class:out-of-view={!inView}
|
||||||
|
data-node-id={node.id}
|
||||||
|
bind:this={ref}
|
||||||
>
|
>
|
||||||
<NodeHeader {node} />
|
<NodeHeader {node} />
|
||||||
|
|
||||||
{#each parameters as [key, value], i}
|
{#each parameters as [key, value], i}
|
||||||
<NodeParameter bind:node id={key} input={value} isLast={i == parameters.length - 1} />
|
<NodeParameter
|
||||||
{/each}
|
bind:node
|
||||||
|
id={key}
|
||||||
|
input={value}
|
||||||
|
isLast={i == parameters.length - 1}
|
||||||
|
/>
|
||||||
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.node {
|
.node {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
user-select: none !important;
|
user-select: none !important;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
width: 200px;
|
width: 200px;
|
||||||
color: var(--text-color);
|
color: var(--text-color);
|
||||||
transform: translate3d(var(--nx), var(--ny), 0);
|
transform: translate3d(var(--nx), var(--ny), 0);
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
font-weight: 300;
|
opacity: calc((var(--cz) - 2.5) / 3.5);
|
||||||
--stroke: var(--background-color-lighter);
|
font-weight: 300;
|
||||||
--stroke-width: 2px;
|
--stroke: var(--background-color-lighter);
|
||||||
}
|
--stroke-width: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
.node.active {
|
.node.active {
|
||||||
--stroke: white;
|
--stroke: white;
|
||||||
--stroke-width: 1px;
|
--stroke-width: 1px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.node.selected {
|
.node.selected {
|
||||||
--stroke: #9d5f28;
|
--stroke: #9d5f28;
|
||||||
--stroke-width: 1px;
|
--stroke-width: 1px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.node.out-of-view {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
@ -156,10 +156,6 @@
|
|||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
:global(.zoom-small) .content {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
svg {
|
svg {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
|
@ -183,7 +183,7 @@ export class MemoryRuntimeExecutor implements RuntimeExecutor {
|
|||||||
return value;
|
return value;
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log(transformed_inputs);
|
// console.log(transformed_inputs);
|
||||||
|
|
||||||
const a2 = performance.now();
|
const a2 = performance.now();
|
||||||
|
|
||||||
@ -191,7 +191,7 @@ export class MemoryRuntimeExecutor implements RuntimeExecutor {
|
|||||||
|
|
||||||
const _inputs = concat_encoded(transformed_inputs);
|
const _inputs = concat_encoded(transformed_inputs);
|
||||||
const a3 = performance.now();
|
const a3 = performance.now();
|
||||||
console.log(`executing ${node_type.id || node.id}`, _inputs);
|
// console.log(`executing ${node_type.id || node.id}`, _inputs);
|
||||||
results[node.id] = node_type.execute(_inputs) as number;
|
results[node.id] = node_type.execute(_inputs) as number;
|
||||||
const duration = performance.now() - a3;
|
const duration = performance.now() - a3;
|
||||||
if (duration > 5) {
|
if (duration > 5) {
|
||||||
|
@ -75,7 +75,7 @@
|
|||||||
header
|
header
|
||||||
<button
|
<button
|
||||||
on:click={() => {
|
on:click={() => {
|
||||||
graph = templates.grid(10, 10);
|
graph = templates.grid(15, 15);
|
||||||
}}>grid stress-test</button
|
}}>grid stress-test</button
|
||||||
>
|
>
|
||||||
</header>
|
</header>
|
||||||
|
@ -8,6 +8,7 @@ export type Node = {
|
|||||||
tmp?: {
|
tmp?: {
|
||||||
depth?: number;
|
depth?: number;
|
||||||
mesh?: any;
|
mesh?: any;
|
||||||
|
random?: number;
|
||||||
parents?: Node[],
|
parents?: Node[],
|
||||||
children?: Node[],
|
children?: Node[],
|
||||||
inputNodes?: Record<string, Node>
|
inputNodes?: Record<string, Node>
|
||||||
|
Loading…
Reference in New Issue
Block a user