feat: some moving around

This commit is contained in:
2024-04-15 22:13:43 +02:00
parent 0254bc1ae5
commit dec205b234
86 changed files with 505 additions and 409 deletions

View File

@ -0,0 +1,67 @@
varying vec2 vUv;
uniform float uWidth;
uniform float uHeight;
uniform vec3 uColorDark;
uniform vec3 uColorBright;
uniform vec3 uSelectedColor;
uniform vec3 uActiveColor;
uniform bool uSelected;
uniform bool uActive;
uniform float uStrokeWidth;
float msign(in float x) { return (x < 0.0) ? -1.0 : 1.0; }
vec4 roundedBoxSDF( in vec2 p, in vec2 b, in float r, in float s) {
vec2 q = abs(p) - b + r;
float l = b.x + b.y + 1.570796 * r;
float k1 = min(max(q.x, q.y), 0.0) + length(max(q, 0.0)) - r;
float k2 = ((q.x > 0.0) ? atan(q.y, q.x) : 1.570796);
float k3 = 3.0 + 2.0 * msign(min(p.x, -p.y)) - msign(p.x);
float k4 = msign(p.x * p.y);
float k5 = r * k2 + max(-q.x, 0.0);
float ra = s * round(k1 / s);
float l2 = l + 1.570796 * ra;
return vec4(k1 - ra, k3 * l2 + k4 * (b.y + ((q.y > 0.0) ? k5 + k2 * ra : q.y)), 4.0 * l2, k1);
}
void main(){
float y = (1.0-vUv.y) * uHeight;
float x = vUv.x * uWidth;
vec2 size = vec2(uWidth, uHeight);
vec2 uv = (vUv - 0.5) * 2.0;
float u_border_radius = 0.4;
vec4 distance = roundedBoxSDF(uv * size, size, u_border_radius*2.0, 0.0);
if (distance.w > 0.0 ) {
// outside
gl_FragColor = vec4(0.0,0.0,0.0, 0.0);
}else{
if (distance.w > -uStrokeWidth || mod(y+5.0, 10.0) < uStrokeWidth/2.0) {
// draw the outer stroke
if (uSelected) {
gl_FragColor = vec4(uSelectedColor, 1.0);
} else if (uActive) {
gl_FragColor = vec4(uActiveColor, 1.0);
} else {
gl_FragColor = vec4(uColorBright, 1.0);
}
}else if (y<5.0){
// draw the header
gl_FragColor = vec4(uColorBright, 1.0);
}else{
gl_FragColor = vec4(uColorDark, 1.0);
}
}
}

View File

@ -0,0 +1,129 @@
<script lang="ts">
import type { Node } from '@nodes/types';
import { getContext, onMount } from 'svelte';
import NodeHeader from './NodeHeader.svelte';
import NodeParameter from './NodeParameter.svelte';
import { activeNodeId, selectedNodes } from '../graph/stores.js';
import { T } from '@threlte/core';
import { Color, type Mesh } from 'three';
import NodeFrag from './Node.frag';
import NodeVert from './Node.vert';
export let node: Node;
export let inView = true;
export let z = 2;
$: isActive = $activeNodeId === node.id;
$: isSelected = !!$selectedNodes?.has(node.id);
const updateNodePosition = getContext<(n: Node) => void>('updateNodePosition');
const getNodeHeight = getContext<(n: string) => number>('getNodeHeight');
const type = node?.tmp?.type;
const parameters = Object.entries(type?.inputs || {})
.filter((p) => p[1].type !== 'seed')
.filter((p) => !('setting' in p[1]));
let ref: HTMLDivElement;
let meshRef: Mesh;
const height = getNodeHeight(node.type);
$: if (node && ref && meshRef) {
node.tmp = node.tmp || {};
node.tmp.ref = ref;
node.tmp.mesh = meshRef;
updateNodePosition(node);
}
onMount(() => {
node.tmp = node.tmp || {};
node.tmp.ref = ref;
node.tmp.mesh = meshRef;
updateNodePosition(node);
});
const colorDark = new Color();
colorDark.setStyle('#151515');
//colorDark.();
const colorBright = new Color('#202020');
//colorBright.convertLinearToSRGB();
</script>
<T.Mesh
position.x={node.position[0] + 10}
position.z={node.position[1] + height / 2}
position.y={0.8}
rotation.x={-Math.PI / 2}
bind:ref={meshRef}
visible={z < 7}
>
<T.PlaneGeometry args={[20, height]} radius={1} />
<T.ShaderMaterial
vertexShader={NodeVert}
fragmentShader={NodeFrag}
transparent
uniforms={{
uColorBright: { value: colorBright },
uColorDark: { value: colorDark },
uSelectedColor: { value: new Color('#9d5f28') },
uActiveColor: { value: new Color('white') },
uSelected: { value: false },
uActive: { value: false },
uStrokeWidth: { value: 1.0 },
uWidth: { value: 20 },
uHeight: { value: height }
}}
uniforms.uSelected.value={isSelected}
uniforms.uActive.value={isActive}
uniforms.uStrokeWidth.value={(7 - z) / 3}
/>
</T.Mesh>
<div
class="node"
class:active={isActive}
class:selected={isSelected}
class:out-of-view={!inView}
data-node-id={node.id}
bind:this={ref}
>
<NodeHeader {node} />
{#each parameters as [key, value], i}
<NodeParameter bind:node id={key} input={value} isLast={i == parameters.length - 1} />
{/each}
</div>
<style>
.node {
position: absolute;
box-sizing: border-box;
user-select: none !important;
cursor: pointer;
width: 200px;
color: var(--text-color);
transform: translate3d(var(--nx), var(--ny), 0);
z-index: 1;
font-weight: 300;
--stroke: var(--background-color-lighter);
--stroke-width: 2px;
}
.node.active {
--stroke: white;
--stroke-width: 1px;
}
.node.selected {
--stroke: #9d5f28;
--stroke-width: 1px;
}
.node.out-of-view {
display: none;
}
</style>

View File

@ -0,0 +1,15 @@
varying vec2 vUv;
varying vec3 vPosition;
void main() {
vUv = uv;
vec4 modelPosition = modelMatrix * vec4(position, 1.0);
vec4 viewPosition = viewMatrix * modelPosition;
vec4 projectedPosition = projectionMatrix * viewPosition;
gl_Position = projectedPosition;
}

View File

@ -0,0 +1,129 @@
<script lang="ts">
import { createNodePath } from '../helpers/index.js';
import type { Node, Socket } from '@nodes/types';
import { getContext } from 'svelte';
export let node: Node;
const setDownSocket = getContext<(socket: Socket) => void>('setDownSocket');
const getSocketPosition =
getContext<(node: Node, index: number) => [number, number]>('getSocketPosition');
function handleMouseDown(event: MouseEvent) {
event.stopPropagation();
event.preventDefault();
setDownSocket({
node,
index: 0,
position: getSocketPosition(node, 0)
});
}
const cornerTop = 10;
const rightBump = !!node?.tmp?.type?.outputs?.length;
const aspectRatio = 0.25;
const path = createNodePath({
depth: 4,
height: 24,
y: 50,
cornerTop,
rightBump,
aspectRatio
});
const pathDisabled = createNodePath({
depth: 0,
height: 15,
y: 50,
cornerTop,
rightBump,
aspectRatio
});
const pathHover = createNodePath({
depth: 5,
height: 30,
y: 50,
cornerTop,
rightBump,
aspectRatio
});
</script>
<div class="wrapper" data-node-id={node.id}>
<div class="content">
{node.type.split('/').pop()}
</div>
<div class="click-target" role="button" tabindex="0" on:mousedown={handleMouseDown} />
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 100 100"
width="100"
height="100"
preserveAspectRatio="none"
style={`
--path: path("${path}");
--hover-path: path("${pathHover}");
`}
>
<path vector-effect="non-scaling-stroke" stroke="white" stroke-width="0.1"></path>
</svg>
</div>
<style>
.wrapper {
position: relative;
width: 100%;
height: 50px;
}
.click-target {
position: absolute;
right: 0px;
top: 50%;
transform: translateX(50%) translateY(-50%);
height: 30px;
width: 30px;
z-index: 100;
border-radius: 50%;
/* background: red; */
/* opacity: 0.2; */
}
.click-target:hover + svg path {
d: var(--hover-path);
}
svg {
position: absolute;
top: 0;
left: 0;
z-index: -1;
box-sizing: border-box;
width: 100%;
height: 100%;
overflow: visible;
}
svg path {
stroke-width: 0.2px;
transition:
d 0.3s ease,
fill 0.3s ease;
fill: var(--background-color-lighter);
stroke: var(--stroke);
stroke-width: var(--stroke-width);
d: var(--path);
}
.content {
font-size: 1em;
display: flex;
align-items: center;
padding-left: 20px;
height: 100%;
}
svg:hover path {
d: var(--hover-path) !important;
}
</style>

View File

@ -0,0 +1,25 @@
<script lang="ts">
import type { Node, NodeInput } from "@nodes/types";
import { getGraphManager } from "../graph/context.js";
import { Input } from "@nodes/ui";
export let node: Node;
export let input: NodeInput;
export let id: string;
export let label: string | undefined;
const graph = getGraphManager();
let value = node?.props?.[id] ?? input.value;
let elementId = Math.random().toString(36).substring(7);
$: if (node?.props?.[id] !== value) {
node.props = { ...node.props, [id]: value };
graph.save();
graph.execute();
}
</script>
<label for="input-{elementId}">{label || id}</label>
<Input id="input-{elementId}" {input} bind:value />

View File

@ -0,0 +1,174 @@
<script lang="ts">
import type { NodeInput as NodeInputType, Socket, Node as NodeType } from '@nodes/types';
import { getContext } from 'svelte';
import { createNodePath } from '../helpers/index.js';
import { possibleSocketIds } from '../graph/stores.js';
import { getGraphManager } from '../graph/context.js';
import NodeInput from './NodeInput.svelte';
export let node: NodeType;
export let input: NodeInputType;
export let id: string;
export let isLast = false;
const socketId = `${node.id}-${id}`;
const graph = getGraphManager();
const graphId = graph.id;
const inputSockets = graph.inputSockets;
const setDownSocket = getContext<(socket: Socket) => void>('setDownSocket');
const getSocketPosition =
getContext<(node: NodeType, index: string) => [number, number]>('getSocketPosition');
function handleMouseDown(ev: MouseEvent) {
ev.preventDefault();
ev.stopPropagation();
setDownSocket({
node,
index: id,
position: getSocketPosition(node, id)
});
}
const leftBump = node.tmp?.type?.inputs?.[id].internal !== true;
const cornerBottom = isLast ? 5 : 0;
const aspectRatio = 0.5;
const path = createNodePath({
depth: 4,
height: 12,
y: 51,
cornerBottom,
leftBump,
aspectRatio
});
const pathDisabled = createNodePath({
depth: 0,
height: 15,
y: 50,
cornerBottom,
leftBump,
aspectRatio
});
const pathHover = createNodePath({
depth: 6,
height: 18,
y: 50.5,
cornerBottom,
leftBump,
aspectRatio
});
</script>
<div class="wrapper" class:disabled={$possibleSocketIds && !$possibleSocketIds.has(socketId)}>
{#key id && graphId}
{#if node?.tmp?.type?.inputs?.[id]?.external !== true}
<div class="content" class:disabled={$inputSockets.has(socketId)}>
<NodeInput {node} {input} {id} label={input.label} />
</div>
{/if}
{#if node?.tmp?.type?.inputs?.[id]?.internal !== true}
<div class="large target" on:mousedown={handleMouseDown} role="button" tabindex="0" />
<div class="small target" on:mousedown={handleMouseDown} role="button" tabindex="0" />
{/if}
{/key}
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 100 100"
width="100"
height="100"
preserveAspectRatio="none"
style={`
--path: path("${path}");
--hover-path: path("${pathHover}");
--hover-path-disabled: path("${pathDisabled}");
`}
>
<path vector-effect="non-scaling-stroke"></path>
</svg>
</div>
<style>
.wrapper {
position: relative;
width: 100%;
height: 100px;
transform: translateY(-0.5px);
}
.target {
position: absolute;
border-radius: 50%;
top: 50%;
transform: translateY(-50%) translateX(-50%);
/* background: red; */
/* opacity: 0.1; */
}
.small.target {
width: 30px;
height: 30px;
}
.large.target {
width: 60px;
height: 60px;
cursor: unset;
pointer-events: none;
}
:global(.hovering-sockets) .large.target {
pointer-events: all;
}
.content {
position: relative;
padding: 10px 20px;
display: flex;
flex-direction: column;
height: 100%;
justify-content: space-around;
box-sizing: border-box;
}
:global(.zoom-small) .content {
display: none;
}
svg {
position: absolute;
box-sizing: border-box;
width: 100%;
height: 100%;
overflow: visible;
top: 0;
left: 0;
z-index: -1;
}
svg path {
transition:
d 0.3s ease,
fill 0.3s ease;
fill: var(--background-color);
stroke: var(--stroke);
stroke-width: var(--stroke-width);
d: var(--path);
}
:global(.hovering-sockets) .large:hover ~ svg path {
d: var(--hover-path);
}
.content.disabled {
opacity: 0.2;
pointer-events: none;
}
.disabled svg path {
d: var(--hover-path-disabled) !important;
}
</style>