feat: add color to sockets
Some checks failed
🚀 Lint & Test & Deploy / release (push) Failing after 3m5s
Some checks failed
🚀 Lint & Test & Deploy / release (push) Failing after 3m5s
Closes #34
This commit is contained in:
Binary file not shown.
|
Before Width: | Height: | Size: 42 KiB After Width: | Height: | Size: 43 KiB |
@@ -6,15 +6,12 @@
|
|||||||
toneMapped: false
|
toneMapped: false
|
||||||
});
|
});
|
||||||
|
|
||||||
let lineColor = $state(colors.outline.clone().convertSRGBToLinear());
|
|
||||||
|
|
||||||
$effect.root(() => {
|
$effect.root(() => {
|
||||||
$effect(() => {
|
$effect(() => {
|
||||||
if (appSettings.value.theme === undefined) {
|
if (appSettings.value.theme === undefined) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
circleMaterial.color = colors.outline.clone().convertSRGBToLinear();
|
circleMaterial.color = colors.outline.clone().convertSRGBToLinear();
|
||||||
lineColor = colors.outline.clone().convertSRGBToLinear();
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -35,6 +32,7 @@
|
|||||||
import { CubicBezierCurve } from 'three/src/extras/curves/CubicBezierCurve.js';
|
import { CubicBezierCurve } from 'three/src/extras/curves/CubicBezierCurve.js';
|
||||||
import { Vector2 } from 'three/src/math/Vector2.js';
|
import { Vector2 } from 'three/src/math/Vector2.js';
|
||||||
import { getGraphState } from '../graph-state.svelte';
|
import { getGraphState } from '../graph-state.svelte';
|
||||||
|
import MeshGradientLineMaterial from './MeshGradientLine/MeshGradientLineMaterial.svelte';
|
||||||
|
|
||||||
const graphState = getGraphState();
|
const graphState = getGraphState();
|
||||||
|
|
||||||
@@ -45,12 +43,17 @@
|
|||||||
y2: number;
|
y2: number;
|
||||||
z: number;
|
z: number;
|
||||||
id?: string;
|
id?: string;
|
||||||
|
inputType?: string;
|
||||||
|
outputType?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
const { x1, y1, x2, y2, z, id }: Props = $props();
|
const { x1, y1, x2, y2, z, inputType = 'unknown', outputType = 'unknown', id }: 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)));
|
||||||
|
|
||||||
|
const inputColor = $derived(graphState.colors.getColor(inputType));
|
||||||
|
const outputColor = $derived(graphState.colors.getColor(outputType));
|
||||||
|
|
||||||
let points = $state<Vector3[]>([]);
|
let points = $state<Vector3[]>([]);
|
||||||
|
|
||||||
let lastId: string | null = null;
|
let lastId: string | null = null;
|
||||||
@@ -106,9 +109,9 @@
|
|||||||
position.z={y1}
|
position.z={y1}
|
||||||
position.y={0.8}
|
position.y={0.8}
|
||||||
rotation.x={-Math.PI / 2}
|
rotation.x={-Math.PI / 2}
|
||||||
material={circleMaterial}
|
|
||||||
>
|
>
|
||||||
<T.CircleGeometry args={[0.5, 16]} />
|
<T.CircleGeometry args={[0.5, 16]} />
|
||||||
|
<T.MeshBasicMaterial color={inputColor} toneMapped={false} />
|
||||||
</T.Mesh>
|
</T.Mesh>
|
||||||
|
|
||||||
<T.Mesh
|
<T.Mesh
|
||||||
@@ -119,6 +122,7 @@
|
|||||||
material={circleMaterial}
|
material={circleMaterial}
|
||||||
>
|
>
|
||||||
<T.CircleGeometry args={[0.5, 16]} />
|
<T.CircleGeometry args={[0.5, 16]} />
|
||||||
|
<T.MeshBasicMaterial color={outputColor} toneMapped={false} />
|
||||||
</T.Mesh>
|
</T.Mesh>
|
||||||
|
|
||||||
{#if graphState.hoveredEdgeId === id}
|
{#if graphState.hoveredEdgeId === id}
|
||||||
@@ -126,7 +130,8 @@
|
|||||||
<MeshLineGeometry {points} />
|
<MeshLineGeometry {points} />
|
||||||
<MeshLineMaterial
|
<MeshLineMaterial
|
||||||
width={thickness * 5}
|
width={thickness * 5}
|
||||||
color={lineColor}
|
color={inputColor}
|
||||||
|
tonemapped={false}
|
||||||
opacity={0.5}
|
opacity={0.5}
|
||||||
transparent
|
transparent
|
||||||
/>
|
/>
|
||||||
@@ -135,5 +140,10 @@
|
|||||||
|
|
||||||
<T.Mesh position.x={x1} position.z={y1} position.y={0.1}>
|
<T.Mesh position.x={x1} position.z={y1} position.y={0.1}>
|
||||||
<MeshLineGeometry {points} />
|
<MeshLineGeometry {points} />
|
||||||
<MeshLineMaterial width={thickness} color={lineColor} />
|
<MeshGradientLineMaterial
|
||||||
|
width={thickness}
|
||||||
|
colorStart={inputColor}
|
||||||
|
colorEnd={outputColor}
|
||||||
|
tonemapped={false}
|
||||||
|
/>
|
||||||
</T.Mesh>
|
</T.Mesh>
|
||||||
|
|||||||
@@ -23,6 +23,7 @@
|
|||||||
|
|
||||||
let { invalidate, size } = useThrelte();
|
let { invalidate, size } = useThrelte();
|
||||||
|
|
||||||
|
// svelte-ignore state_referenced_locally
|
||||||
const uniforms = {
|
const uniforms = {
|
||||||
lineWidth: { value: width },
|
lineWidth: { value: width },
|
||||||
colorStart: { value: new Color(colorStart) },
|
colorStart: { value: new Color(colorStart) },
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { getContext, setContext } from 'svelte';
|
|||||||
import { SvelteMap, SvelteSet } from 'svelte/reactivity';
|
import { SvelteMap, SvelteSet } from 'svelte/reactivity';
|
||||||
import type { OrthographicCamera, Vector3 } from 'three';
|
import type { OrthographicCamera, Vector3 } from 'three';
|
||||||
import type { GraphManager } from './graph-manager.svelte';
|
import type { GraphManager } from './graph-manager.svelte';
|
||||||
|
import { ColorGenerator } from './graph/colors';
|
||||||
import { getNodeHeight, getSocketPosition } from './helpers/nodeHelpers';
|
import { getNodeHeight, getSocketPosition } from './helpers/nodeHelpers';
|
||||||
|
|
||||||
const graphStateKey = Symbol('graph-state');
|
const graphStateKey = Symbol('graph-state');
|
||||||
@@ -28,7 +29,32 @@ type EdgeData = {
|
|||||||
points: Vector3[];
|
points: Vector3[];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const predefinedColors = {
|
||||||
|
path: {
|
||||||
|
hue: 80,
|
||||||
|
lightness: 20,
|
||||||
|
saturation: 80
|
||||||
|
},
|
||||||
|
float: {
|
||||||
|
hue: 70,
|
||||||
|
lightness: 10,
|
||||||
|
saturation: 0
|
||||||
|
},
|
||||||
|
geometry: {
|
||||||
|
hue: 0,
|
||||||
|
lightness: 50,
|
||||||
|
saturation: 70
|
||||||
|
},
|
||||||
|
'*': {
|
||||||
|
hue: 200,
|
||||||
|
lightness: 20,
|
||||||
|
saturation: 100
|
||||||
|
}
|
||||||
|
} as const;
|
||||||
|
|
||||||
export class GraphState {
|
export class GraphState {
|
||||||
|
colors = new ColorGenerator(predefinedColors);
|
||||||
|
|
||||||
constructor(private graph: GraphManager) {
|
constructor(private graph: GraphManager) {
|
||||||
$effect.root(() => {
|
$effect.root(() => {
|
||||||
$effect(() => {
|
$effect(() => {
|
||||||
|
|||||||
@@ -95,6 +95,13 @@
|
|||||||
graphState.activeSocket = null;
|
graphState.activeSocket = null;
|
||||||
graphState.addMenuPosition = null;
|
graphState.addMenuPosition = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getSocketType(node: NodeInstance, index: number | string): string {
|
||||||
|
if (typeof index === 'string') {
|
||||||
|
return node.state.type?.inputs?.[index].type || 'unknown';
|
||||||
|
}
|
||||||
|
return node.state.type?.outputs?.[index] || 'unknown';
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<svelte:window
|
<svelte:window
|
||||||
@@ -175,6 +182,8 @@
|
|||||||
{#if graphState.activeSocket}
|
{#if graphState.activeSocket}
|
||||||
<EdgeEl
|
<EdgeEl
|
||||||
z={graphState.cameraPosition[2]}
|
z={graphState.cameraPosition[2]}
|
||||||
|
inputType={getSocketType(graphState.activeSocket.node, graphState.activeSocket.index)}
|
||||||
|
outputType={getSocketType(graphState.activeSocket.node, graphState.activeSocket.index)}
|
||||||
x1={graphState.activeSocket.position[0]}
|
x1={graphState.activeSocket.position[0]}
|
||||||
y1={graphState.activeSocket.position[1]}
|
y1={graphState.activeSocket.position[1]}
|
||||||
x2={graphState.edgeEndPosition?.[0] ?? graphState.mousePosition[0]}
|
x2={graphState.edgeEndPosition?.[0] ?? graphState.mousePosition[0]}
|
||||||
@@ -187,6 +196,8 @@
|
|||||||
<EdgeEl
|
<EdgeEl
|
||||||
id={graph.getEdgeId(edge)}
|
id={graph.getEdgeId(edge)}
|
||||||
z={graphState.cameraPosition[2]}
|
z={graphState.cameraPosition[2]}
|
||||||
|
inputType={getSocketType(edge[0], edge[1])}
|
||||||
|
outputType={getSocketType(edge[2], edge[3])}
|
||||||
{x1}
|
{x1}
|
||||||
{y1}
|
{y1}
|
||||||
{x2}
|
{x2}
|
||||||
|
|||||||
45
app/src/lib/graph-interface/graph/colors.ts
Normal file
45
app/src/lib/graph-interface/graph/colors.ts
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
type Color = { hue: number; saturation: number; lightness: number };
|
||||||
|
|
||||||
|
export class ColorGenerator {
|
||||||
|
private colors: Map<string, Color> = new Map();
|
||||||
|
private lightnessLevels = [10, 60];
|
||||||
|
|
||||||
|
constructor(predefined: Record<string, Color>) {
|
||||||
|
for (const [id, colorStr] of Object.entries(predefined)) {
|
||||||
|
this.colors.set(id, colorStr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public getColor(id: string): string {
|
||||||
|
if (this.colors.has(id)) {
|
||||||
|
return this.colorToHsl(this.colors.get(id)!);
|
||||||
|
}
|
||||||
|
|
||||||
|
const newColor = this.generateNewColor();
|
||||||
|
this.colors.set(id, newColor);
|
||||||
|
console.log(id, newColor);
|
||||||
|
return this.colorToHsl(newColor);
|
||||||
|
}
|
||||||
|
|
||||||
|
private generateNewColor(): Color {
|
||||||
|
const existingHues = Array.from(this.colors.values()).map(c => c.hue).sort();
|
||||||
|
let hue = existingHues[0];
|
||||||
|
let attempts = 0;
|
||||||
|
|
||||||
|
while (
|
||||||
|
existingHues.some(h => Math.abs(h - hue) < 30 || Math.abs(h - hue) > 330)
|
||||||
|
&& attempts < 360
|
||||||
|
) {
|
||||||
|
hue = (hue + 30) % 360;
|
||||||
|
attempts++;
|
||||||
|
}
|
||||||
|
|
||||||
|
const lightness = 60;
|
||||||
|
|
||||||
|
return { hue, lightness, saturation: 100 };
|
||||||
|
}
|
||||||
|
|
||||||
|
private colorToHsl(c: Color): string {
|
||||||
|
return `hsl(${c.hue}, ${c.saturation}%, ${c.lightness}%)`;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { appSettings } from '$lib/settings/app-settings.svelte';
|
import { appSettings } from '$lib/settings/app-settings.svelte';
|
||||||
import type { NodeInstance } from '@nodarium/types';
|
import type { NodeInstance, Socket } from '@nodarium/types';
|
||||||
import { getGraphState } from '../graph-state.svelte';
|
import { getGraphState } from '../graph-state.svelte';
|
||||||
import { createNodePath } from '../helpers/index.js';
|
import { createNodePath } from '../helpers/index.js';
|
||||||
import { getSocketPosition } from '../helpers/nodeHelpers';
|
import { getSocketPosition } from '../helpers/nodeHelpers';
|
||||||
@@ -47,12 +47,23 @@
|
|||||||
);
|
);
|
||||||
|
|
||||||
const socketId = $derived(`${node.id}-${0}`);
|
const socketId = $derived(`${node.id}-${0}`);
|
||||||
|
|
||||||
|
function getSocketType(s: Socket | null) {
|
||||||
|
if (!s) return 'unknown';
|
||||||
|
if (typeof s.index === 'string') {
|
||||||
|
return s.node.state.type?.inputs?.[s.index].type || 'unknown';
|
||||||
|
}
|
||||||
|
return s.node.state.type?.outputs?.[s.index] || 'unknown';
|
||||||
|
}
|
||||||
|
const socketType = $derived(getSocketType(graphState.activeSocket));
|
||||||
|
const hoverColor = $derived(graphState.colors.getColor(socketType));
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
class="wrapper"
|
class="wrapper"
|
||||||
data-node-id={node.id}
|
data-node-id={node.id}
|
||||||
data-node-type={node.type}
|
data-node-type={node.type}
|
||||||
|
style:--socket-color={hoverColor}
|
||||||
class:possible-socket={graphState?.possibleSocketIds.has(socketId)}
|
class:possible-socket={graphState?.possibleSocketIds.has(socketId)}
|
||||||
>
|
>
|
||||||
<div class="content">
|
<div class="content">
|
||||||
@@ -96,10 +107,10 @@
|
|||||||
width: 30px;
|
width: 30px;
|
||||||
height: 30px;
|
height: 30px;
|
||||||
border-radius: 100%;
|
border-radius: 100%;
|
||||||
box-shadow: 0px 0px 10px var(--color-layer-3);
|
box-shadow: 0px 0px 10px var(--socket-color);
|
||||||
background-color: var(--color-layer-3);
|
background-color: var(--socket-color);
|
||||||
outline: solid thin white;
|
outline: solid thin var(--socket-color);
|
||||||
opacity: 0.2;
|
opacity: 0.7;
|
||||||
z-index: -10;
|
z-index: -10;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import type { NodeInput, NodeInstance } from '@nodarium/types';
|
import type { NodeInput, NodeInstance, Socket } from '@nodarium/types';
|
||||||
import { getGraphManager, getGraphState } from '../graph-state.svelte';
|
import { getGraphManager, getGraphState } from '../graph-state.svelte';
|
||||||
import { createNodePath } from '../helpers';
|
import { createNodePath } from '../helpers';
|
||||||
import { getParameterHeight, getSocketPosition } from '../helpers/nodeHelpers';
|
import { getParameterHeight, getSocketPosition } from '../helpers/nodeHelpers';
|
||||||
@@ -60,6 +60,17 @@
|
|||||||
aspectRatio
|
aspectRatio
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
|
function getSocketType(s: Socket | null) {
|
||||||
|
if (!s) return 'unknown';
|
||||||
|
if (typeof s.index === 'string') {
|
||||||
|
return s.node.state.type?.inputs?.[s.index].type || 'unknown';
|
||||||
|
}
|
||||||
|
return s.node.state.type?.outputs?.[s.index] || 'unknown';
|
||||||
|
}
|
||||||
|
|
||||||
|
const socketType = $derived(getSocketType(graphState.activeSocket));
|
||||||
|
const hoverColor = $derived(graphState.colors.getColor(socketType));
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
@@ -67,6 +78,7 @@
|
|||||||
data-node-type={node.type}
|
data-node-type={node.type}
|
||||||
data-node-input={id}
|
data-node-input={id}
|
||||||
style:height="{height}px"
|
style:height="{height}px"
|
||||||
|
style:--socket-color={hoverColor}
|
||||||
class:possible-socket={graphState?.possibleSocketIds.has(socketId)}
|
class:possible-socket={graphState?.possibleSocketIds.has(socketId)}
|
||||||
>
|
>
|
||||||
{#key id && graphId}
|
{#key id && graphId}
|
||||||
@@ -95,10 +107,8 @@
|
|||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
viewBox="0 0 100 100"
|
viewBox="0 0 100 100"
|
||||||
preserveAspectRatio="none"
|
preserveAspectRatio="none"
|
||||||
style={`
|
style:--path={`path("${path}")`}
|
||||||
--path: path("${path}");
|
style:--hover-path={`path("${pathHover}")`}
|
||||||
--hover-path: path("${pathHover}");
|
|
||||||
`}
|
|
||||||
>
|
>
|
||||||
<path vector-effect="non-scaling-stroke"></path>
|
<path vector-effect="non-scaling-stroke"></path>
|
||||||
</svg>
|
</svg>
|
||||||
@@ -126,10 +136,10 @@
|
|||||||
width: 30px;
|
width: 30px;
|
||||||
height: 30px;
|
height: 30px;
|
||||||
border-radius: 100%;
|
border-radius: 100%;
|
||||||
box-shadow: 0px 0px 10px var(--color-layer-3);
|
box-shadow: 0px 0px 10px var(--socket-color);
|
||||||
background-color: var(--color-layer-3);
|
background-color: var(--socket-color);
|
||||||
outline: solid thin white;
|
outline: solid thin var(--socket-color);
|
||||||
opacity: 0.2;
|
opacity: 0.5;
|
||||||
z-index: -10;
|
z-index: -10;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user