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);
 }