Der Graph

This commit is contained in:
kolaflash
2026-01-05 13:56:51 +01:00
parent 941474041b
commit 0c91ac8e67
2 changed files with 114 additions and 35 deletions
+85 -19
View File
@@ -8,7 +8,13 @@ import {
type Node, type Node,
type PositionPartial, type PositionPartial,
} from "#/lib/Graph.ts"; } from "#/lib/Graph.ts";
import { createVector, scale, type Vector } from "#/lib/Vector.ts"; import {
createVector,
getDistance,
scale,
setDistanceNormal,
type Vector,
} from "#/lib/Vector.ts";
function drawGraph<T extends PositionPartial>( function drawGraph<T extends PositionPartial>(
graph: Graph<T>, graph: Graph<T>,
@@ -22,13 +28,15 @@ function drawGraph<T extends PositionPartial>(
} }
} }
function applyRepulsion( type PhysicsPartial = PositionPartial & {
nodes: Array<{ position: Vector; velocity: Vector }>, velocity: Vector;
): void { acceleration: Vector;
const strength = 16; };
function applyRepulsion(nodes: Array<PhysicsPartial>): void {
const strength = 1.8;
nodes.forEach((node, index) => { nodes.forEach((node, index) => {
node.velocity = createVector(0, 0);
for (let i = 0; i < nodes.length; i++) { for (let i = 0; i < nodes.length; i++) {
if (index === i) { if (index === i) {
continue; continue;
@@ -43,24 +51,33 @@ function applyRepulsion(
const distanceSquare = dx * dx + dy * dy; const distanceSquare = dx * dx + dy * dy;
const magnitude = strength / distanceSquare; const magnitude = strength / (distanceSquare + Number.EPSILON);
scale(direction, magnitude); scale(direction, magnitude);
node.velocity.x += direction.x; node.acceleration.x += direction.x;
node.velocity.y += direction.y; node.acceleration.y += direction.y;
} }
}); });
} }
function doTheHookeThing<T extends { position: Vector; velocity: Vector }>([ function doTheHookeThing<T extends PhysicsPartial>([a, b]: Edge<T>): void {
a, const restLength = 48;
b, const stiffness = 0.3;
]: Edge<T>): void {
const edgeLength = 32; const direction = createVector(0, 0);
const stiffness = 0.8; setDistanceNormal(direction, a.position, b.position);
const displacement = 0;
const f = stiffness * displacement; const distance = getDistance(a.position, b.position);
const magnitude = (distance - restLength) * stiffness;
scale(direction, magnitude / 2 + Number.EPSILON);
a.acceleration.x -= direction.x;
a.acceleration.y -= direction.y;
b.acceleration.x += direction.x;
b.acceleration.y += direction.y;
} }
function placeNodes<T extends PositionPartial>(graph: Graph<T>): void { function placeNodes<T extends PositionPartial>(graph: Graph<T>): void {
@@ -92,21 +109,70 @@ function placeNodes<T extends PositionPartial>(graph: Graph<T>): void {
} }
export const GraphBrowser: Component<{ export const GraphBrowser: Component<{
graph: Graph<{ position: Vector; label: string }>; graph: Graph<PhysicsPartial & { label: string }>;
}> = (props) => { }> = (props) => {
let canvasRef!: HTMLCanvasElement; let canvasRef!: HTMLCanvasElement;
const [width, setWidth] = createSignal(300); const [width, setWidth] = createSignal(300);
const [height, setHeight] = createSignal(300); const [height, setHeight] = createSignal(300);
// placeNodes(props.graph);
const nodes: Array<Node<PhysicsPartial>> = [...props.graph.keys()];
const edges: Array<Edge<PhysicsPartial>> = props.graph.entries().reduce(
(acc, [node, neighbours]) => {
for (const neighbour of neighbours) {
acc.push([node, neighbour]);
}
return acc;
},
[] as Array<Edge<PhysicsPartial>>,
);
onMount(() => { onMount(() => {
const ctx: CanvasRenderingContext2D = canvasRef.getContext("2d", { const ctx: CanvasRenderingContext2D = canvasRef.getContext("2d", {
alpha: false, alpha: false,
})!; })!;
const damping = 0.9;
let then = self.performance.now();
let delta = 0;
const animate = (timestamp: number): void => {
delta = timestamp - then;
then = timestamp;
applyRepulsion(nodes);
for (const edge of edges) {
doTheHookeThing(edge);
}
for (const node of nodes) {
node.velocity.x += node.acceleration.x;
node.velocity.y += node.acceleration.y;
node.velocity.x *= damping;
node.velocity.y *= damping;
node.position.x += node.velocity.x;
node.position.y += node.velocity.y;
node.acceleration.x = 0;
node.acceleration.y = 0;
}
ctx.restore();
ctx.clearRect(0, 0, canvasRef.width, canvasRef.height);
ctx.save();
ctx.translate(width() / 2, height() / 2); ctx.translate(width() / 2, height() / 2);
placeNodes(props.graph);
drawGraph(props.graph, ctx); drawGraph(props.graph, ctx);
requestAnimationFrame(animate);
};
animate(then);
}); });
return ( return (
+27 -14
View File
@@ -33,29 +33,42 @@ function adjustGameContainer(gameContainer: HTMLElement, width: number): void {
const scenesRaw = await fetchScenes(); const scenesRaw = await fetchScenes();
type TestNode = Node<{ position: Vector; label: string }>; type TestNode = Node<{
position: Vector;
velocity: Vector;
acceleration: Vector;
label: string;
}>;
function createTestNode(label: string): TestNode {
return {
label,
position: createVector(Math.random(), Math.random()),
velocity: createVector(),
acceleration: createVector(),
};
}
const nodes: Array<TestNode> = [ const nodes: Array<TestNode> = [
{ position: createVector(), label: "foo" }, createTestNode("foo"),
{ position: createVector(), label: "bar" }, createTestNode("bar"),
{ position: createVector(), label: "lol" }, createTestNode("lol"),
{ position: createVector(), label: "hai" }, createTestNode("hai"),
{ position: createVector(), label: "sea" }, createTestNode("sea"),
{ position: createVector(), label: "vot" }, createTestNode("vot"),
{ position: createVector(), label: "zoe" }, createTestNode("zoe"),
{ position: createVector(), label: "asm" }, createTestNode("asm"),
]; ];
const edges: Array<Edge<TestNode>> = [ const edges: Array<Edge<TestNode>> = [
[nodes[0], nodes[1]], [nodes[0], nodes[1]],
[nodes[0], nodes[2]], [nodes[0], nodes[2]],
[nodes[4], nodes[5]],
[nodes[5], nodes[3]],
[nodes[1], nodes[2]], [nodes[1], nodes[2]],
[nodes[0], nodes[3]], [nodes[2], nodes[3]],
[nodes[2], nodes[6]], [nodes[4], nodes[5]],
[nodes[5], nodes[7]], [nodes[4], nodes[3]],
[nodes[5], nodes[6]], [nodes[5], nodes[6]],
[nodes[3], nodes[6]],
]; ];
const graph = createGraph<TestNode>(nodes, edges); const graph = createGraph<TestNode>(nodes, edges);