mirror of
https://github.com/rm-code/Graphoon.git
synced 2024-11-16 18:24:22 +00:00
Add first working draft of the force directed graph
This commit is contained in:
parent
ff55890510
commit
805278d67a
13
Edge.lua
Normal file
13
Edge.lua
Normal file
@ -0,0 +1,13 @@
|
||||
local Edge = {};
|
||||
|
||||
function Edge.new( id, origin, target )
|
||||
local self = {};
|
||||
|
||||
self.id = id;
|
||||
self.origin = origin;
|
||||
self.target = target;
|
||||
|
||||
return self;
|
||||
end
|
||||
|
||||
return Edge;
|
78
Graph.lua
Normal file
78
Graph.lua
Normal file
@ -0,0 +1,78 @@
|
||||
local Node = require('Node');
|
||||
local Edge = require('Edge');
|
||||
|
||||
local Graph = {};
|
||||
|
||||
-- TODO remove / replace LÖVE functions
|
||||
function Graph.new()
|
||||
local self = {};
|
||||
|
||||
-- Node Objects are stored in a table using their ID as an index.
|
||||
local nodes = {};
|
||||
local edges = {};
|
||||
local edgeIDs = 0;
|
||||
|
||||
local center = Node.new( 'center ', love.graphics.getWidth() * 0.5, love.graphics.getHeight() * 0.5);
|
||||
|
||||
function self:init( table )
|
||||
math.randomseed(120123091239581834213143141);
|
||||
|
||||
for _, id in pairs( table.nodes ) do
|
||||
self:addNode( Node.new( id, math.random(100, 800), math.random(100, 400) ) );
|
||||
end
|
||||
|
||||
for _, edge in pairs( table.edges ) do
|
||||
self:addEdge( nodes[edge[1]], nodes[edge[2]] );
|
||||
end
|
||||
end
|
||||
|
||||
function self:addNode( node )
|
||||
nodes[node:getID()] = node;
|
||||
end
|
||||
|
||||
function self:addEdge( origin, target )
|
||||
edges[edgeIDs] = Edge.new( edgeIDs, origin, target );
|
||||
edgeIDs = edgeIDs + 1;
|
||||
end
|
||||
|
||||
function self:update( dt, ... )
|
||||
for _, edge in pairs( edges ) do
|
||||
edge.origin:attractTo( edge.target );
|
||||
edge.target:attractTo( edge.origin );
|
||||
end
|
||||
|
||||
for _, nodeA in pairs( nodes ) do
|
||||
nodeA:attractTo( center );
|
||||
for _, nodeB in pairs( nodes ) do
|
||||
if nodeA ~= nodeB then
|
||||
nodeA:repelFrom( nodeB );
|
||||
end
|
||||
end
|
||||
nodeA:move( dt );
|
||||
end
|
||||
end
|
||||
|
||||
function self:draw()
|
||||
for _, edge in pairs( edges ) do
|
||||
love.graphics.line( edge.origin:getX(), edge.origin:getY(), edge.target:getX(), edge.target:getY() );
|
||||
end
|
||||
|
||||
for _, nodeA in pairs( nodes ) do
|
||||
love.graphics.points( nodeA:getX(), nodeA:getY() );
|
||||
love.graphics.print( nodeA:getID(), nodeA:getX(), nodeA:getY() );
|
||||
end
|
||||
end
|
||||
|
||||
function self:getNodeAt(x, y, range)
|
||||
for _, node in pairs( nodes ) do
|
||||
local nx, ny = node:getPosition();
|
||||
if x < nx + range and x > nx - range and y < ny + range and y > ny - range then
|
||||
return node;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return self;
|
||||
end
|
||||
|
||||
return Graph;
|
100
Node.lua
Normal file
100
Node.lua
Normal file
@ -0,0 +1,100 @@
|
||||
local Node = {};
|
||||
|
||||
local FORCE_SPRING = -0.01;
|
||||
local FORCE_CHARGE = 100000;
|
||||
|
||||
local FORCE_MAX = 4;
|
||||
local NODE_SPEED = 8;
|
||||
local DAMPING_FACTOR = 0.95;
|
||||
|
||||
---
|
||||
-- @param id - A unique id which will be used to reference this node.
|
||||
--
|
||||
function Node.new( id, x, y )
|
||||
local self = {};
|
||||
|
||||
local px, py = x or 0, y or 0;
|
||||
local ax, ay = 0, 0;
|
||||
local vx, vy = 0, 0;
|
||||
|
||||
---
|
||||
-- Clamps a value to a certain range.
|
||||
-- @param min
|
||||
-- @param val
|
||||
-- @param max
|
||||
--
|
||||
local function clamp( min, val, max )
|
||||
return math.max( min, math.min( val, max ) );
|
||||
end
|
||||
|
||||
---
|
||||
-- Calculates the new xy-acceleration for this node.
|
||||
-- The values are clamped to keep the graph from "exploding".
|
||||
-- @param fx - The force to apply in x-direction.
|
||||
-- @param fy - The force to apply in y-direction.
|
||||
--
|
||||
local function applyForce( fx, fy )
|
||||
ax = clamp( -FORCE_MAX, ax + fx, FORCE_MAX );
|
||||
ay = clamp( -FORCE_MAX, ay + fy, FORCE_MAX );
|
||||
end
|
||||
|
||||
function self:attractTo( node )
|
||||
local dx, dy = px - node:getX(), py - node:getY();
|
||||
local distance = math.sqrt(dx * dx + dy * dy);
|
||||
dx = dx / distance;
|
||||
dy = dy / distance;
|
||||
|
||||
local strength = FORCE_SPRING * distance;
|
||||
applyForce( dx * strength, dy * strength );
|
||||
end
|
||||
|
||||
function self:repelFrom( node )
|
||||
local dx, dy = px - node:getX(), py - node:getY();
|
||||
local distance = math.sqrt(dx * dx + dy * dy);
|
||||
dx = dx / distance;
|
||||
dy = dy / distance;
|
||||
|
||||
local mass = 0.015 * (0 + math.log(math.max(24, 0)));
|
||||
local strength = FORCE_CHARGE * ((mass) / (distance * distance));
|
||||
applyForce(dx * strength, dy * strength);
|
||||
end
|
||||
|
||||
---
|
||||
-- Update the node's position based on the calculated velocity and
|
||||
-- acceleration.
|
||||
--
|
||||
function self:move( dt )
|
||||
vx = (vx + ax * dt * NODE_SPEED) * DAMPING_FACTOR;
|
||||
vy = (vy + ay * dt * NODE_SPEED) * DAMPING_FACTOR;
|
||||
px = px + vx;
|
||||
py = py + vy;
|
||||
ax, ay = 0, 0;
|
||||
end
|
||||
|
||||
---
|
||||
-- Returns the node's unique identifier.
|
||||
--
|
||||
function self:getID()
|
||||
return id;
|
||||
end
|
||||
|
||||
function self:getX()
|
||||
return px;
|
||||
end
|
||||
|
||||
function self:getY()
|
||||
return py;
|
||||
end
|
||||
|
||||
function self:getPosition()
|
||||
return px, py;
|
||||
end
|
||||
|
||||
function self:setPosition( nx, ny )
|
||||
px, py = nx, ny;
|
||||
end
|
||||
|
||||
return self;
|
||||
end
|
||||
|
||||
return Node;
|
Loading…
Reference in New Issue
Block a user