commit 7bd3c8e0800954a0cec321760031df35651aec23
parent 523f7641666ede528901bf0f7fc475762a724375
Author: William Casarin <jb55@jb55.com>
Date: Mon, 6 Aug 2018 23:16:34 -0700
add grid
will be useful for moving close things away from each other
Diffstat:
M | Makefile | | | 2 | ++ |
A | grid.c | | | 70 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | grid.h | | | 11 | +++++++++++ |
A | ln.c | | | 86 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
M | ln.h | | | 17 | +++++++++++++++++ |
M | main.c | | | 109 | ++++++++++--------------------------------------------------------------------- |
M | render.c | | | 2 | +- |
M | update.c | | | 70 | +++++++++++++++++++++++++++++++++++++++++++++++----------------------- |
8 files changed, 247 insertions(+), 120 deletions(-)
diff --git a/Makefile b/Makefile
@@ -10,6 +10,8 @@ LDFLAGS = -lglfw -lGL
SRCS = main.c
SRCS += update.c
+SRCS += ln.c
+SRCS += grid.c
SRCS += render.c
SRCS += perf.c
SRCS += demo.c
diff --git a/grid.c b/grid.c
@@ -0,0 +1,70 @@
+
+#include "grid.h"
+#include <string.h>
+#include <assert.h>
+#include <stdio.h>
+
+
+static void remove_node_from_cell(struct cell *cell, struct node *node) {
+ struct node **n = NULL;
+
+ for (int i = 0; i < cell->node_count; i++) {
+ n = &cell->nodes[i];
+ if (node != *n)
+ continue;
+
+ // shortcut: reduce node count if it's the last node
+ if (i == cell->node_count-1) {
+ cell->node_count--;
+ return;
+ }
+ else {
+ assert(i + 1 < CELL_MAX_ELEMS);
+ memmove(n, n + 1,
+ sizeof(*cell->nodes) * (cell->node_count - 1));
+ cell->node_count--;
+ return;
+ }
+ }
+
+ /* assert(!"expect to remove a node"); */
+}
+
+void update_grid_move_nodes(struct ln *ln) {
+ struct node *n = NULL;
+ struct cell *new_cell, *old_cell;
+
+ for (u32 i = 0; i < ln->node_count; i++) {
+ n = &ln->nodes[i];
+
+ int gx = ((double)(n->x / (double)ln->window_width)) * ln->grid_div;
+ int gy = ((double)(n->y / (double)ln->window_height)) * ln->grid_div;
+
+ // TODO: handle outside of grid?
+ if (gx > ln->grid_div || gy > ln->grid_div)
+ continue;
+
+ // we've moved to a new cell
+ if (gx != n->grid_x || gy != n->grid_y) {
+ new_cell = &ln->grid[gy * ln->grid_div + gx];
+
+ // can't move to new cell :[
+ if (new_cell->node_count == CELL_MAX_ELEMS)
+ continue;
+
+ old_cell = &ln->grid[n->grid_y * ln->grid_div + n->grid_x];
+
+ // remove from old cell
+ remove_node_from_cell(old_cell, n);
+
+ // add to new cell
+ n->grid_x = gx;
+ n->grid_y = gy;
+
+ new_cell->nodes[new_cell->node_count++] = n;
+ }
+ }
+}
+
+
+
diff --git a/grid.h b/grid.h
@@ -0,0 +1,11 @@
+
+#ifndef LNVIS_GRID_H
+#define LNVIS_GRID_H
+
+
+#include "ln.h"
+
+void update_grid_move_nodes(struct ln *ln);
+
+
+#endif /* LNVIS_GRID_H */
diff --git a/ln.c b/ln.c
@@ -0,0 +1,86 @@
+
+#include "ln.h"
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+void init_ln(struct ln *ln, int grid_div) {
+ ln->clicked = 0;
+ ln->grid_div = grid_div;
+ ln->grid = calloc(ln->grid_div * ln->grid_div, sizeof(*ln->grid));
+}
+
+void free_ln(struct ln *ln) {
+ free(ln->grid);
+}
+
+static double rand_0to1() {
+ return (double) rand() / RAND_MAX;
+}
+
+void random_network(int ww, int wh, int max_per_node, int num_nodes, struct ln *ln) {
+ int i, j;
+ int from, to;
+ int tries = 0;
+ struct node *n;
+ u8 connections[num_nodes][num_nodes];
+ memset(connections, 0, sizeof(connections));
+
+ ln->nodes = calloc(sizeof(*ln->nodes), num_nodes);
+ ln->channel_count = 0;
+ ln->node_count = num_nodes;
+ ln->channels = calloc(sizeof(*ln->channels), num_nodes * max_per_node);
+
+ printf("max channels %d\n", num_nodes * max_per_node);
+
+ for (i = 0; i < num_nodes; ++i) {
+ n = &ln->nodes[i];
+
+ n->alias = "test";
+ n->color.r = rand_0to1();
+ n->color.g = rand_0to1();
+ n->color.b = rand_0to1();
+ n->color.a = 1.0;
+ n->x = ww * rand_0to1();
+ n->y = wh * rand_0to1();
+ n->ax = 0.0;
+ n->ay = 0.0;
+ n->vx = 0.0;
+ n->vy = 0.0;
+ n->size = 10;
+ }
+
+ // connect nodes randomly
+ for (i = 0; i < num_nodes; ++i) {
+
+ from = i;
+ skip:
+ // for each node, it can have 0 to max_per_node connections
+ for (j = 0; j < rand() % max_per_node; ++j) {
+ do {
+ tries++;
+ // if connections are way higher than the
+ // possible number of nodes for some reason,
+ // we'll need an escape hatch
+ if (tries > 5) {
+ tries = 0;
+ goto skip;
+ }
+ to = rand() % num_nodes;
+ }
+ while(connections[from][to]);
+ tries = 0;
+
+ connections[from][to] = 1;
+
+ struct channel *c =
+ &ln->channels[ln->channel_count++];
+
+ c->nodes[0] = &ln->nodes[from];
+ c->nodes[1] = &ln->nodes[to];
+
+ }
+
+ // keep trying until we find on that isn't already connected
+ }
+}
diff --git a/ln.h b/ln.h
@@ -5,6 +5,8 @@
#include "nanovg/nanovg.h"
#include "defs.h"
+#define CELL_MAX_ELEMS 32
+
struct node {
const char *alias;
const char *id; // pubkey
@@ -16,6 +18,7 @@ struct node {
};
} color;
+ int grid_x, grid_y;
double x, y;
double vx, vy;
double ax, ay;
@@ -71,12 +74,22 @@ struct channel {
u32 last_update;
};
+struct cell {
+ struct node *nodes[CELL_MAX_ELEMS];
+ int node_count;
+};
+
struct ln {
NVGcontext *vg;
int clicked;
int mdown;
double mx, my;
+ int window_width;
+ int window_height;
+
+ int grid_div;
+ struct cell *grid;
struct node *drag_target;
@@ -87,4 +100,8 @@ struct ln {
u32 channel_count;
};
+void init_ln(struct ln *ln, int grid_div);
+void free_ln(struct ln *ln);
+void random_network(int ww, int wh, int max_per_node, int num_nodes, struct ln *ln);
+
#endif /* LNVIS_LN_H */
diff --git a/main.c b/main.c
@@ -36,11 +36,6 @@ static void key(GLFWwindow *window, int key, int scancode, int action, int mods)
premult = !premult;
}
-
-static double rand_0to1() {
- return (double) rand() / RAND_MAX;
-}
-
static double mx, my;
static int mdown = 0;
static int mclicked = 0;
@@ -66,69 +61,6 @@ void mouse_click(GLFWwindow *win, int button, int action, int mods)
}
-void random_network(int ww, int wh, int max_per_node, int num_nodes, struct ln *ln) {
- int i, j;
- int from, to;
- int tries = 0;
- struct node *n;
- u8 connections[num_nodes][num_nodes];
- memset(connections, 0, sizeof(connections));
-
- ln->nodes = malloc(sizeof(*ln->nodes) * num_nodes);
- ln->channel_count = 0;
- ln->node_count = num_nodes;
- ln->channels = malloc(sizeof(*ln->channels) * num_nodes * max_per_node);
-
- printf("max channels %d\n", num_nodes * max_per_node);
-
- for (i = 0; i < num_nodes; ++i) {
- n = &ln->nodes[i];
-
- n->alias = "test";
- n->color.r = rand_0to1();
- n->color.g = rand_0to1();
- n->color.b = rand_0to1();
- n->color.a = 1.0;
- n->x = ww * rand_0to1();
- n->y = wh * rand_0to1();
- n->ax = 0.0;
- n->ay = 0.0;
- n->vx = 0.0;
- n->vy = 0.0;
- n->size = 10;
- }
-
- // connect nodes randomly
- for (i = 0; i < num_nodes; ++i) {
-
- from = i;
- skip:
- // for each node, it can have 0 to max_per_node connections
- for (j = 0; j < rand() % max_per_node; ++j) {
- do {
- tries++;
- if (tries > 5) {
- tries = 0;
- goto skip;
- }
- to = rand() % num_nodes;
- }
- while(connections[from][to]);
- tries = 0;
-
- connections[from][to] = 1;
-
- struct channel *c =
- &ln->channels[ln->channel_count++];
-
- c->nodes[0] = &ln->nodes[from];
- c->nodes[1] = &ln->nodes[to];
-
- }
-
- // keep trying until we find on that isn't already connected
- }
-}
int main()
{
@@ -138,31 +70,12 @@ int main()
NVGcontext *vg = NULL;
PerfGraph fps;
double prevt = 0;
- struct ln ln;
- /* struct node test_nodes[] = { */
- /* { */
- /* .alias = "@jb55", */
- /* .color = { { 1.0, 0, 0, 1.0 } }, */
- /* }, */
- /* { */
- /* .alias = "SuperHub", */
- /* .color = { { 0.0, 1.0, 0.0, 1.0 } }, */
- /* }, */
- /* { */
- /* .alias = "satoshis.place", */
- /* .color = { { 0.0, 0.5, 1.0, 1.0 } }, */
- /* }, */
- /* }; */
-
- /* struct channel test_channels[] = { */
- /* { */
- /* .nodes = { &test_nodes[0], &test_nodes[1] }, */
- /* }, */
- /* { */
- /* .nodes = { &test_nodes[1], &test_nodes[2] }, */
- /* } */
- /* }; */
+ // ln collision grid subdivision
+ // cells = grid_div * grid_div
+ int grid_div = 20;
+
+ struct ln ln;
if (!glfwInit()) {
printf("Failed to init GLFW.");
@@ -179,7 +92,7 @@ int main()
glfwWindowHint(GLFW_SAMPLES, 4);
#endif
- window = glfwCreateWindow(1000, 600, "NanoVG", NULL, NULL);
+ window = glfwCreateWindow(1000, 600, "Lightning Network Vis", NULL, NULL);
// window = glfwCreateWindow(1000, 600, "NanoVG", glfwGetPrimaryMonitor(), NULL);
if (!window) {
glfwTerminate();
@@ -207,7 +120,7 @@ int main()
}
ln.vg = vg;
- ln.clicked = 0;
+ init_ln(&ln, grid_div);
if (loadDemoData(vg, &data) == -1)
return -1;
@@ -243,6 +156,8 @@ int main()
glfwGetCursorPos(window, &mx, &my);
glfwGetWindowSize(window, &winWidth, &winHeight);
+ ln.window_height = winHeight;
+ ln.window_width = winWidth;
if (first) {
random_network(winWidth, winHeight, 3, 50, &ln);
@@ -260,8 +175,9 @@ int main()
if (premult)
glClearColor(0, 0, 0, 0);
else // base16-onedark bg color ;)
- glClearColor(0x28 / 255.0, 0x2c / 255.0, 0x34 / 255.0,
- 1.0f);
+ glClearColor(0x28 / 255.0, 0x2c / 255.0, 0x34 / 255.0, 1.0f);
+ /* glClearColor(1.0f, 1.0f, 1.0f, 1.0f); */
+
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT |
GL_STENCIL_BUFFER_BIT);
@@ -278,6 +194,7 @@ int main()
glfwPollEvents();
}
+ free_ln(&ln);
freeDemoData(vg, &data);
nvgDeleteGL2(vg);
diff --git a/render.c b/render.c
@@ -59,7 +59,7 @@ void draw_node(NVGcontext *vg, struct node *node)
node->color.a);
NVGcolor blend =
- nvgRGBAf(0, 0, 0, 0.5);
+ nvgRGBAf(0, 0, 0, 1.0);
nvgSave(vg);
nvgTranslate(vg, node->x, node->y);
diff --git a/update.c b/update.c
@@ -1,5 +1,6 @@
#include "ln.h"
+#include "grid.h"
#include <math.h>
#include <assert.h>
#include <stdio.h>
@@ -23,30 +24,9 @@ struct node *hit_node(struct ln *ln) {
return NULL;
}
-// force graph update logic
-void update(struct ln *ln, double dt)
-{
- u32 i;
- static const double friction_coeff = 0.03;
- static const double friction = 1.0 - friction_coeff;
-
- // click detection
- if (ln->clicked) {
- struct node *hit = hit_node(ln);
- ln->drag_target = hit;
- }
-
- // stop dragging
- if (!ln->mdown && ln->drag_target)
- ln->drag_target = NULL;
- // drag
- if (ln->mdown && ln->drag_target) {
- ln->drag_target->x = ln->mx;
- ln->drag_target->y = ln->my;
- ln->drag_target->vx = 0;
- ln->drag_target->vy = 0;
- }
+void force_graph(struct ln *ln, double dt) {
+ u32 i;
// channel constraints
for (i = 0; i < ln->channel_count; ++i) {
@@ -62,21 +42,29 @@ void update(struct ln *ln, double dt)
static const double mindist = 100.0;
if (d < mindist) {
+
+ // normalized vector between two nodes
double nx = dx / d;
double ny = dy / d;
+ // get the distance past the minimum distance along
+ // the vector between the two nodes
double nnx = (d - mindist) * nx;
double nny = (d - mindist) * ny;
if (isnan(nnx) || isnan(nny))
continue;
+ // normalize by frame time
double mx = nnx * dt;
double my = nny * dt;
+ // correct for the distance by accelerating the node away
+ // from the other node
n1->vx += mx;
n1->vy += my;
+ // do the same in the opposite direction for the other node
n2->vx -= mx;
n2->vy -= my;
}
@@ -90,7 +78,14 @@ void update(struct ln *ln, double dt)
n2->vy += -dy * scale * dt;
}
}
+}
+
+
+void physics(struct ln *ln, double dt) {
+ static const double friction_coeff = 0.03;
+ static const double friction = 1.0 - friction_coeff;
+ // physics
for (u32 i = 0; i < ln->node_count; i++) {
struct node *node = &ln->nodes[i];
@@ -109,5 +104,34 @@ void update(struct ln *ln, double dt)
node->x += node->vx * dt;
node->y += node->vy * dt;
}
+}
+
+
+
+// force graph update logic
+void update(struct ln *ln, double dt)
+{
+ // click detection
+ if (ln->clicked) {
+ struct node *hit = hit_node(ln);
+ ln->drag_target = hit;
+ }
+
+ // stop dragging
+ if (!ln->mdown && ln->drag_target)
+ ln->drag_target = NULL;
+
+ // drag
+ if (ln->mdown && ln->drag_target) {
+ ln->drag_target->x = ln->mx;
+ ln->drag_target->y = ln->my;
+ ln->drag_target->vx = 0;
+ ln->drag_target->vy = 0;
+ }
+
+ force_graph(ln, dt);
+
+ physics(ln, dt);
+ update_grid_move_nodes(ln);
}