Implement Dummy Graph-View

This commit is contained in:
2025-08-29 20:17:20 +02:00
parent 1d2233ece4
commit 0767a8be17
7 changed files with 1640 additions and 0 deletions

24
.gitignore vendored Normal file
View File

@ -0,0 +1,24 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
dist
dist-ssr
*.local
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

59
index.html Normal file
View File

@ -0,0 +1,59 @@
<!doctype html>
<html>
<!-- TODO: We want a toolbar at the top, the model visualization on the left -->
<!-- and the graph on the right. -->
<!-- The toolbar should have a model selector, an initial state editor, -->
<!-- an initial state selector (from presets) and a generate button. -->
<body style="margin: 5px">
<div
id="toolbar"
style="
width: fit-content;
height: 26px;
margin-top: 0px;
margin-bottom: 5px;
margin-left: auto;
margin-right: auto;
display: flex;
gap: 5px;
"
>
<button id="select_model_button">Select Model</button>
<button id="select_state_button">Select State</button>
<button id="generate_graph_button">Generate Graph</button>
<button id="reset_view_button">Reset View</button>
<button id="clear_graph_button">Clear Graph</button>
</div>
<div id="columns" style="display: flex">
<div
id="model"
style="
/* Body m-left / 2 + graph m-left / 2 + body m-right / 2 + borders x4 / 2 */
/* 2.5px + 2.5px + 2.5px + 2px */
width: calc(50vw - 9.5px);
/* Bar height + body m-top + bar m-bot + body m-bot + border x2 */
/* 26px + 5px + 5px + 5px + 2px */
height: calc(100vh - 43px);
border-style: solid;
border-color: black;
border-width: 1px;
"
></div>
<div
id="graph"
style="
width: calc(50vw - 7.5px);
height: calc(100vh - 43px);
margin-left: 5px;
border-style: solid;
border-color: black;
border-width: 1px;
"
></div>
</div>
<!-- Load the script last, such that the dom is populated beforehand -->
<script type="module" src="/src/main.js"></script>
</body>
</html>

1416
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

18
package.json Normal file
View File

@ -0,0 +1,18 @@
{
"name": "my3dgraph",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview"
},
"devDependencies": {
"prettier": "^3.6.2",
"vite": "^7.1.2"
},
"dependencies": {
"3d-force-graph": "^1.78.4"
}
}

1
public/vite.svg Normal file
View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

94
src/graph.js Normal file
View File

@ -0,0 +1,94 @@
import ForceGraph3D from "3d-force-graph";
export const generate_sample_data = () => {
// Example data:
// {
// "nodes": [
// {
// "id": "id1",
// "name": "name1",
// "val": 1
// },
// {
// "id": "id2",
// "name": "name2",
// "val": 10
// },
// ...
// ],
// "links": [
// {
// "source": "id1",
// "target": "id2"
// },
// ...
// ]
// }
let data = {
nodes: [...Array(50).keys()].map((i) => ({ id: i })),
links: [...Array(50).keys()]
.filter((id) => id)
.map((id) => ({
source: id,
target: Math.round(Math.random() * (id - 1)),
})),
};
return data;
};
export const generate_graph = (data) => {
// TODO: Highlight the current state by coloring the node
let graph = ForceGraph3D()(document.getElementById("graph"))
// Input the data into the graph
.graphData(data)
// Set up the styling
.backgroundColor("#FFFFFF")
.nodeColor(["#555555"])
.linkColor(["#000000"])
// Set up the interactions
.onNodeHover(
(node) => (document.body.style.cursor = node ? "pointer" : null),
)
.onNodeClick((node) => {
// TODO: Visualize the clicked state in the GameView
const distance = 40;
const distRatio = 1 + distance / Math.hypot(node.x, node.y, node.z);
graph.cameraPosition(
{ x: node.x * distRatio, y: node.y * distRatio, z: node.z * distRatio },
{ x: node.x, y: node.y, z: node.z },
1000,
);
});
reset_graph_view(graph);
return graph;
};
const get_viewport_dims = () => {
let vw = Math.max(
document.documentElement.clientWidth || 0,
window.innerWidth || 0,
);
let vh = Math.max(
document.documentElement.clientHeight || 0,
window.innerHeight || 0,
);
return [vw, vh];
};
export const reset_graph_view = (graph) => {
if (graph === null) {
return;
}
const [vw, vh] = get_viewport_dims();
graph
.width(vw / 2 - 9.5)
.height(vh - 43)
.zoomToFit();
};

28
src/main.js Normal file
View File

@ -0,0 +1,28 @@
import "./graph.js";
import {
generate_graph,
reset_graph_view,
generate_sample_data,
} from "./graph.js";
let data = null;
let graph = null;
const clear = () => {
document.getElementById("graph").innerHTML = "";
graph = null;
data = null;
};
// Set up button event-handlers
document
.getElementById("generate_graph_button")
.addEventListener("click", () => {
clear();
data = generate_sample_data();
graph = generate_graph(data);
});
document.getElementById("reset_view_button").addEventListener("click", () => {
reset_graph_view(graph);
});
document.getElementById("clear_graph_button").addEventListener("click", clear);