292 lines
5.5 KiB
HTML
292 lines
5.5 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="UTF-8" />
|
||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||
<title>Live Snap Drag Window with Canvas</title>
|
||
|
||
</head>
|
||
<body>
|
||
|
||
<div class="top-panel">
|
||
|
||
<button id="graphExplorer">Graph Explorer</button>
|
||
|
||
<button id="resetParticlesButton">Reset Particles</button>
|
||
|
||
</div>
|
||
|
||
|
||
<div id="wireMenu" style="
|
||
position: absolute;
|
||
display: none;
|
||
background: #333;
|
||
color: #eee;
|
||
padding: 8px;
|
||
border-radius: 4px;
|
||
font-family: Arial, sans-serif;
|
||
font-size: 14px;
|
||
z-index: 1000;
|
||
box-shadow: 0 2px 6px rgba(0,0,0,0.8);
|
||
">
|
||
<button id="deleteWireBtn" style="background:#900; color:#fff; border:none; padding:4px 8px; cursor:pointer;">Delete Wire</button>
|
||
</div>
|
||
|
||
<!-- Node context menu -->
|
||
<div id="nodeMenu" style="
|
||
position: absolute;
|
||
display: none;
|
||
background: #333;
|
||
color: #eee;
|
||
padding: 8px;
|
||
border-radius: 4px;
|
||
font-family: Arial, sans-serif;
|
||
font-size: 14px;
|
||
z-index: 1000;
|
||
box-shadow: 0 2px 6px rgba(0,0,0,0.8);
|
||
">
|
||
<button id="deleteNodeBtn" style="background:#900; color:#fff; border:none; padding:4px 8px; cursor:pointer;">Delete Node</button>
|
||
</div>
|
||
|
||
<link rel="stylesheet" href="./style/main.css" >
|
||
<script type="module">
|
||
function autoLayoutNodes( graphExplorer ) {
|
||
|
||
const placedNodes = new Set();
|
||
const visited = new Set();
|
||
const levels = new Map();
|
||
|
||
function dfs( node, level ) {
|
||
|
||
if ( visited.has( node ) ) return;
|
||
|
||
visited.add( node );
|
||
|
||
// Assign the current level (depth) if it’s deeper
|
||
if ( !levels.has( node ) || levels.get( node ) < level ) {
|
||
|
||
levels.set( node, level );
|
||
|
||
}
|
||
|
||
// Traverse all outgoing edges
|
||
for ( var i = 0; i < graphExplorer.edges.length; i++ ) {
|
||
|
||
const edge = graphExplorer.edges[i];
|
||
|
||
if ( edge.fromNode === node ) {
|
||
|
||
dfs( edge.toNode, level + 1 );
|
||
|
||
}
|
||
}
|
||
}
|
||
|
||
// Start from nodes with no incoming edges
|
||
for ( var i = 0; i < graphExplorer.nodes.length; i++ ) {
|
||
|
||
const node = graphExplorer.nodes[i];
|
||
|
||
const hasIncoming = graphExplorer.edges.some( edge => edge.toNode === node );
|
||
|
||
if ( !hasIncoming ) {
|
||
|
||
dfs( node, 0 );
|
||
|
||
}
|
||
}
|
||
|
||
// Group nodes by level
|
||
const layers = {};
|
||
|
||
for ( const [node, level] of levels ) {
|
||
|
||
if ( !layers[level] ) layers[level] = [];
|
||
|
||
layers[level].push( node );
|
||
|
||
}
|
||
|
||
const xSpacing = 400;
|
||
const yBase = 150;
|
||
|
||
for ( const levelEntry of Object.entries( layers ) ) {
|
||
|
||
const levelStr = levelEntry[0];
|
||
const nodesAtLevel = levelEntry[1];
|
||
const level = Number( levelStr );
|
||
|
||
const spacing = yBase + Math.max(...nodesAtLevel.map(n => n.inputs.length)) * 25;
|
||
|
||
for ( let i = 0; i < nodesAtLevel.length; i++ ) {
|
||
|
||
const node = nodesAtLevel[i];
|
||
|
||
const xJitter = ( Math.random() - 0.5 ) * 80; // wider jitter horizontally
|
||
const yJitter = ( Math.random() - 0.5 ) * 80; // wider jitter vertically
|
||
|
||
node.x = level * xSpacing + xJitter;
|
||
node.y = i * spacing + yJitter;
|
||
|
||
}
|
||
|
||
}
|
||
|
||
|
||
|
||
|
||
|
||
}
|
||
|
||
async function loadJSON( pathName ) {
|
||
|
||
const response = await fetch( pathName );
|
||
|
||
if ( !response.ok ) {
|
||
|
||
throw new Error( `Failed to load shader: ${ pathName }` );
|
||
|
||
}
|
||
|
||
return await response.json();
|
||
|
||
}
|
||
|
||
function getShaderByName( shaders, shaderName ) {
|
||
|
||
for (var i = 0; i < shaders.length; i++) {
|
||
|
||
var shader = shaders[i]
|
||
|
||
if( shader.name == shaderName ) {
|
||
|
||
return shader;
|
||
|
||
}
|
||
|
||
}
|
||
}
|
||
|
||
function getPortIndex( bindings, variableName ) {
|
||
|
||
for (var i = 0; i < bindings.length; i++) {
|
||
|
||
var binding = bindings[i]
|
||
|
||
if( binding.varName == variableName ) {
|
||
|
||
return i;
|
||
|
||
}
|
||
|
||
}
|
||
}
|
||
import { Node } from "./GraphExplorer.js"
|
||
|
||
import { Edge } from "./GraphExplorer.js"
|
||
|
||
import { GraphExplorer } from "./GraphExplorer.js"
|
||
|
||
import { WindowController } from "./WindowController.js"
|
||
|
||
|
||
var graphExplorer = new GraphExplorer();
|
||
|
||
const windowController = new WindowController();
|
||
|
||
windowController.setGraphExplorer( graphExplorer );
|
||
|
||
|
||
|
||
var graphCanvas = document.createElement( "canvas" );
|
||
|
||
graphCanvas.id = "graphCanvas";
|
||
|
||
graphCanvas.width = windowController.startWidth;
|
||
graphCanvas.height = windowController.startHeight;
|
||
|
||
|
||
|
||
console.log("set element", graphCanvas);
|
||
|
||
await windowController.setElement( graphCanvas );
|
||
|
||
await windowController.setup();
|
||
|
||
|
||
windowController.maximizeWindow();
|
||
|
||
var nodes = await loadJSON("./nodes.json");
|
||
|
||
console.log(nodes);
|
||
|
||
var shaders = nodes.shaders;
|
||
|
||
for (var i = 0; i < shaders.length; i++) {
|
||
|
||
var shader = shaders[i]
|
||
|
||
var bindings = shader.bindings;
|
||
|
||
var newNode = new Node(shader.name, 50 + 240 * i, 100, [], []);
|
||
|
||
for (var j = 0; j < bindings.length; j++) {
|
||
|
||
var binding = bindings[j]
|
||
|
||
var varName = binding.varName;
|
||
|
||
newNode.inputs[j] = varName;
|
||
|
||
newNode.outputs[j] = varName;
|
||
}
|
||
|
||
shader.node = newNode;
|
||
|
||
graphExplorer.addNode( newNode );
|
||
|
||
}
|
||
var edges = nodes.edges;
|
||
|
||
for (var i = 0; i < edges.length; i++) {
|
||
|
||
var edge = edges[i]
|
||
|
||
var fromShader = getShaderByName( shaders, edge.from );
|
||
|
||
var toShader = getShaderByName( shaders, edge.to );
|
||
|
||
var fromPortIndex = getPortIndex( fromShader.bindings, edge.fromBuffer );
|
||
|
||
var toPortIndex = getPortIndex( toShader.bindings, edge.buffer );
|
||
|
||
var edge = new Edge( fromShader.node, fromPortIndex, toShader.node, toPortIndex );
|
||
|
||
graphExplorer.addEdge( edge );
|
||
|
||
//console.log(edge, shaders, edge.from, fromShader);
|
||
|
||
}
|
||
|
||
autoLayoutNodes( graphExplorer );
|
||
|
||
graphExplorer.setup( graphCanvas );
|
||
graphExplorer.inertiaLoop();
|
||
graphExplorer.draw();
|
||
|
||
|
||
document.querySelector("#graphExplorer").addEventListener("click", function () {
|
||
|
||
windowController.show();
|
||
|
||
});
|
||
|
||
|
||
windowController.resizeCanvas();
|
||
|
||
</script>
|
||
|
||
|
||
</body>
|
||
</html>
|