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
|
||||
});
|
||||
|
||||
let lineColor = $state(colors.outline.clone().convertSRGBToLinear());
|
||||
|
||||
$effect.root(() => {
|
||||
$effect(() => {
|
||||
if (appSettings.value.theme === undefined) {
|
||||
return;
|
||||
}
|
||||
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 { Vector2 } from 'three/src/math/Vector2.js';
|
||||
import { getGraphState } from '../graph-state.svelte';
|
||||
import MeshGradientLineMaterial from './MeshGradientLine/MeshGradientLineMaterial.svelte';
|
||||
|
||||
const graphState = getGraphState();
|
||||
|
||||
@@ -45,12 +43,17 @@
|
||||
y2: number;
|
||||
z: number;
|
||||
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 inputColor = $derived(graphState.colors.getColor(inputType));
|
||||
const outputColor = $derived(graphState.colors.getColor(outputType));
|
||||
|
||||
let points = $state<Vector3[]>([]);
|
||||
|
||||
let lastId: string | null = null;
|
||||
@@ -106,9 +109,9 @@
|
||||
position.z={y1}
|
||||
position.y={0.8}
|
||||
rotation.x={-Math.PI / 2}
|
||||
material={circleMaterial}
|
||||
>
|
||||
<T.CircleGeometry args={[0.5, 16]} />
|
||||
<T.MeshBasicMaterial color={inputColor} toneMapped={false} />
|
||||
</T.Mesh>
|
||||
|
||||
<T.Mesh
|
||||
@@ -119,6 +122,7 @@
|
||||
material={circleMaterial}
|
||||
>
|
||||
<T.CircleGeometry args={[0.5, 16]} />
|
||||
<T.MeshBasicMaterial color={outputColor} toneMapped={false} />
|
||||
</T.Mesh>
|
||||
|
||||
{#if graphState.hoveredEdgeId === id}
|
||||
@@ -126,7 +130,8 @@
|
||||
<MeshLineGeometry {points} />
|
||||
<MeshLineMaterial
|
||||
width={thickness * 5}
|
||||
color={lineColor}
|
||||
color={inputColor}
|
||||
tonemapped={false}
|
||||
opacity={0.5}
|
||||
transparent
|
||||
/>
|
||||
@@ -135,5 +140,10 @@
|
||||
|
||||
<T.Mesh position.x={x1} position.z={y1} position.y={0.1}>
|
||||
<MeshLineGeometry {points} />
|
||||
<MeshLineMaterial width={thickness} color={lineColor} />
|
||||
<MeshGradientLineMaterial
|
||||
width={thickness}
|
||||
colorStart={inputColor}
|
||||
colorEnd={outputColor}
|
||||
tonemapped={false}
|
||||
/>
|
||||
</T.Mesh>
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
|
||||
let { invalidate, size } = useThrelte();
|
||||
|
||||
// svelte-ignore state_referenced_locally
|
||||
const uniforms = {
|
||||
lineWidth: { value: width },
|
||||
colorStart: { value: new Color(colorStart) },
|
||||
|
||||
@@ -3,6 +3,7 @@ import { getContext, setContext } from 'svelte';
|
||||
import { SvelteMap, SvelteSet } from 'svelte/reactivity';
|
||||
import type { OrthographicCamera, Vector3 } from 'three';
|
||||
import type { GraphManager } from './graph-manager.svelte';
|
||||
import { ColorGenerator } from './graph/colors';
|
||||
import { getNodeHeight, getSocketPosition } from './helpers/nodeHelpers';
|
||||
|
||||
const graphStateKey = Symbol('graph-state');
|
||||
@@ -28,7 +29,32 @@ type EdgeData = {
|
||||
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 {
|
||||
colors = new ColorGenerator(predefinedColors);
|
||||
|
||||
constructor(private graph: GraphManager) {
|
||||
$effect.root(() => {
|
||||
$effect(() => {
|
||||
|
||||
@@ -95,6 +95,13 @@
|
||||
graphState.activeSocket = 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>
|
||||
|
||||
<svelte:window
|
||||
@@ -175,6 +182,8 @@
|
||||
{#if graphState.activeSocket}
|
||||
<EdgeEl
|
||||
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]}
|
||||
y1={graphState.activeSocket.position[1]}
|
||||
x2={graphState.edgeEndPosition?.[0] ?? graphState.mousePosition[0]}
|
||||
@@ -187,6 +196,8 @@
|
||||
<EdgeEl
|
||||
id={graph.getEdgeId(edge)}
|
||||
z={graphState.cameraPosition[2]}
|
||||
inputType={getSocketType(edge[0], edge[1])}
|
||||
outputType={getSocketType(edge[2], edge[3])}
|
||||
{x1}
|
||||
{y1}
|
||||
{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">
|
||||
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 { createNodePath } from '../helpers/index.js';
|
||||
import { getSocketPosition } from '../helpers/nodeHelpers';
|
||||
@@ -47,12 +47,23 @@
|
||||
);
|
||||
|
||||
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>
|
||||
|
||||
<div
|
||||
class="wrapper"
|
||||
data-node-id={node.id}
|
||||
data-node-type={node.type}
|
||||
style:--socket-color={hoverColor}
|
||||
class:possible-socket={graphState?.possibleSocketIds.has(socketId)}
|
||||
>
|
||||
<div class="content">
|
||||
@@ -96,10 +107,10 @@
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
border-radius: 100%;
|
||||
box-shadow: 0px 0px 10px var(--color-layer-3);
|
||||
background-color: var(--color-layer-3);
|
||||
outline: solid thin white;
|
||||
opacity: 0.2;
|
||||
box-shadow: 0px 0px 10px var(--socket-color);
|
||||
background-color: var(--socket-color);
|
||||
outline: solid thin var(--socket-color);
|
||||
opacity: 0.7;
|
||||
z-index: -10;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<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 { createNodePath } from '../helpers';
|
||||
import { getParameterHeight, getSocketPosition } from '../helpers/nodeHelpers';
|
||||
@@ -60,6 +60,17 @@
|
||||
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>
|
||||
|
||||
<div
|
||||
@@ -67,6 +78,7 @@
|
||||
data-node-type={node.type}
|
||||
data-node-input={id}
|
||||
style:height="{height}px"
|
||||
style:--socket-color={hoverColor}
|
||||
class:possible-socket={graphState?.possibleSocketIds.has(socketId)}
|
||||
>
|
||||
{#key id && graphId}
|
||||
@@ -95,10 +107,8 @@
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 100 100"
|
||||
preserveAspectRatio="none"
|
||||
style={`
|
||||
--path: path("${path}");
|
||||
--hover-path: path("${pathHover}");
|
||||
`}
|
||||
style:--path={`path("${path}")`}
|
||||
style:--hover-path={`path("${pathHover}")`}
|
||||
>
|
||||
<path vector-effect="non-scaling-stroke"></path>
|
||||
</svg>
|
||||
@@ -126,10 +136,10 @@
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
border-radius: 100%;
|
||||
box-shadow: 0px 0px 10px var(--color-layer-3);
|
||||
background-color: var(--color-layer-3);
|
||||
outline: solid thin white;
|
||||
opacity: 0.2;
|
||||
box-shadow: 0px 0px 10px var(--socket-color);
|
||||
background-color: var(--socket-color);
|
||||
outline: solid thin var(--socket-color);
|
||||
opacity: 0.5;
|
||||
z-index: -10;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user