Der Graph
This commit is contained in:
+87
-21
@@ -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,
|
||||||
})!;
|
})!;
|
||||||
ctx.translate(width() / 2, height() / 2);
|
|
||||||
|
|
||||||
placeNodes(props.graph);
|
const damping = 0.9;
|
||||||
drawGraph(props.graph, ctx);
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
drawGraph(props.graph, ctx);
|
||||||
|
|
||||||
|
requestAnimationFrame(animate);
|
||||||
|
};
|
||||||
|
|
||||||
|
animate(then);
|
||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
+27
-14
@@ -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);
|
||||||
|
|||||||
Reference in New Issue
Block a user