commit 75b582227aae98f9fc3d4af5f8fb7f6031351781
parent 2c8b700a9aa63cdb35c0ec3e2c8d6d5de8500532
Author: William Casarin <jb55@jb55.com>
Date: Sat, 11 Aug 2018 17:39:18 -0700
very cool
Diffstat:
A | .rgignore | | | 2 | ++ |
M | defs.h | | | 18 | ++++++++++++++++++ |
M | json.c | | | 44 | +++++++++++++++++++++++++++++++++----------- |
M | json.h | | | 3 | +++ |
M | ln.c | | | 66 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
M | ln.h | | | 16 | ++++++++++++++-- |
M | main.c | | | 71 | ++++++++++++++++++++++++++++++++++++++++++++++------------------------- |
M | render.c | | | 73 | +++++++++++++++++++++++++++++++++++++++++++++++++++---------------------- |
M | update.c | | | 57 | +++++++++++++++++++++++++++++++++------------------------ |
9 files changed, 266 insertions(+), 84 deletions(-)
diff --git a/.rgignore b/.rgignore
@@ -0,0 +1 @@
+*.json+
\ No newline at end of file
diff --git a/defs.h b/defs.h
@@ -5,6 +5,7 @@
#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
#include <inttypes.h>
+#include <string.h>
typedef unsigned char u8;
typedef signed char s8;
@@ -18,5 +19,22 @@ typedef signed int s32;
typedef uint64_t u64;
typedef int64_t s64;
+static inline int streq(const char *a, const char *b)
+{
+ return strcmp(a, b) == 0;
+}
+
+#define min(a,b) \
+ ({ __typeof__ (a) _a = (a); \
+ __typeof__ (b) _b = (b); \
+ _a < _b ? _a : _b; })
+
+#define max(a,b) \
+ ({ __typeof__ (a) _a = (a); \
+ __typeof__ (b) _b = (b); \
+ _a > _b ? _a : _b; })
+
+
+
#endif /* LNVIS_DEFS_H */
diff --git a/json.c b/json.c
@@ -11,11 +11,6 @@
#define ERROR_READING_CHANNELS -2
#define ERROR_ALLOCATING_CHANNELS -3
-#define min(a,b) \
- ({ __typeof__ (a) _a = (a); \
- __typeof__ (b) _b = (b); \
- _a < _b ? _a : _b; })
-
struct channel_parser {
const char *field;
enum channel_parsing_state state;
@@ -39,6 +34,8 @@ struct node_parser {
/* "fee_per_millionth": 1, */
/* "delay": 14 */
static const struct channel_parser channel_parsers[] = {
+ { "source", PARSING_CHAN_SOURCE },
+ { "destination", PARSING_CHAN_DESTINATION },
{ "short_channel_id", PARSING_CHAN_SHORTID },
{ "public", PARSING_CHAN_PUBLIC },
{ "satoshis", PARSING_CHAN_SATOSHIS },
@@ -61,8 +58,9 @@ static const struct channel_parser channel_parsers[] = {
/* "port": 9735 */
/* }] */
static const struct node_parser node_parsers[] = {
- { "alias", PARSING_NODE_ALIAS },
- { "color", PARSING_NODE_COLOR },
+ { "nodeid", PARSING_NODE_ID },
+ { "alias", PARSING_NODE_ALIAS },
+ { "color", PARSING_NODE_COLOR },
{ "addresses", PARSING_NODE_ADDRESSES },
};
@@ -181,14 +179,14 @@ static void parse_color(int toklen, const char *tokstr, union color *color) {
goto colorerr;
for (int i = 0; i < 3; i++) {
- u8 c1 = tokstr[i];
- u8 c2 = tokstr[i+1];
+ u8 c1 = tokstr[i*2];
+ u8 c2 = tokstr[i*2+1];
if (!isxdigit(c1) || !isxdigit(c2))
goto colorerr;
u8 val = hexdigit(c1) << 4 | hexdigit(c2);
- color->rgba[i] = val / 255.0f;
+ color->rgba[i] = ((float)val) / 255.0f;
}
return;
@@ -214,6 +212,8 @@ int parse_clightning_nodes(FILE *fd, int *node_count, struct node **pnodes)
nodes = calloc(nodecap, sizeof(struct node));
+ int acount = 0;
+
int rez = parse_json(fd, &toks, &ntoks, &buffer);
if (rez < 0)
return rez;
@@ -221,8 +221,15 @@ int parse_clightning_nodes(FILE *fd, int *node_count, struct node **pnodes)
for (i = 0; i < ntoks; i++) {
tok = &toks[i];
- if (tok->type == JSMN_ARRAY)
+ if (tok->type == JSMN_ARRAY) {
+ if (state == PARSING_NODE_ADDRESSES) {
+ if (++acount == 1) {
+ acount = 0;
+ state = PARSING_NODE_TOKEN;
+ }
+ }
continue;
+ }
if (tok->type == JSMN_OBJECT) {
if (state == PARSING_NODE_ADDRESSES)
@@ -251,6 +258,11 @@ int parse_clightning_nodes(FILE *fd, int *node_count, struct node **pnodes)
}
switch (state) {
+ case PARSING_NODE_ID:
+ strncpy(node->id, tokstr, min(toklen, PUBKEY_SIZE));
+ state = PARSING_NODE_TOKEN;
+ break;
+
case PARSING_NODE_ALIAS:
strncpy(node->alias, tokstr, min(toklen, MAX_ALIAS_SIZE));
state = PARSING_NODE_TOKEN;
@@ -336,6 +348,16 @@ int parse_clightning_channels(FILE *fd, int *nchans, struct channel **pchannels)
// TODO: lookup node id, assign nodes
switch (state) {
+ case PARSING_CHAN_SOURCE:
+ strncpy(chan->source, tokstr, min(PUBKEY_SIZE, toklen));
+ state = PARSING_CHAN_TOKEN;
+ break;
+
+ case PARSING_CHAN_DESTINATION:
+ strncpy(chan->destination, tokstr, min(PUBKEY_SIZE, toklen));
+ state = PARSING_CHAN_TOKEN;
+ break;
+
case PARSING_CHAN_SHORTID:
parse_short_channel_id(toklen, tokstr,
&chan->short_channel_id);
diff --git a/json.h b/json.h
@@ -10,6 +10,7 @@ enum node_parsing_state {
PARSING_NODE_TOKEN,
PARSING_NODE_ALIAS,
PARSING_NODE_COLOR,
+ PARSING_NODE_ID,
PARSING_NODE_ADDRESSES,
};
@@ -24,6 +25,8 @@ enum channel_parsing_state {
PARSING_CHAN_BASE_FEE,
PARSING_CHAN_FEE_PER,
PARSING_CHAN_DELAY,
+ PARSING_CHAN_SOURCE,
+ PARSING_CHAN_DESTINATION,
};
int parse_clightning_nodes(FILE *fd, int *node_count, struct node **nodes);
diff --git a/ln.c b/ln.c
@@ -19,6 +19,22 @@ static double rand_0to1() {
return (double) rand() / RAND_MAX;
}
+void init_network(int ww, int wh, struct ln *ln) {
+ struct node *n;
+
+ for (u32 i = 0; i < ln->node_count; ++i) {
+ n = &ln->nodes[i];
+
+ 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;
+ }
+}
+
void random_network(int ww, int wh, int max_per_node, int num_nodes, struct ln *ln) {
int i, j;
int from, to;
@@ -85,3 +101,53 @@ void random_network(int ww, int wh, int max_per_node, int num_nodes, struct ln *
// keep trying until we find on that isn't already connected
}
}
+
+
+static void print_node(struct node *node)
+{
+ printf("node %s #%02X%02X%02X\n", node->alias,
+ (int)(node->color.r * 255.0f),
+ (int)(node->color.g * 255.0f),
+ (int)(node->color.b * 255.0f));
+}
+
+
+
+void filter_network(const char *nodeid, struct ln *ln)
+{
+ u32 i;
+ struct node *node = NULL;
+ struct channel *chan = NULL;
+ ln->filter = (char*)nodeid;
+
+ for (i = 0; i < ln->node_count; i++) {
+ node = &ln->nodes[i];
+
+ if (streq(nodeid, node->id) || rand_0to1() < 0.002) {
+ node->filtered = 1;
+ printf("filtering ");
+ print_node(node);
+ }
+ else
+ node->filtered = 0;
+ }
+
+ for (i = 0; i < ln->channel_count; i++) {
+ chan = &ln->channels[i];
+
+ if (chan->nodes[0]->filtered)
+ chan->nodes[1]->mark_filtered = 1;
+
+ if (chan->nodes[1]->filtered)
+ chan->nodes[0]->mark_filtered = 1;
+ }
+
+ for (i = 0; i < ln->node_count; i++) {
+ node = &ln->nodes[i];
+
+ if (node->mark_filtered) {
+ node->filtered = 1;
+ node->mark_filtered = 0;
+ }
+ }
+}
diff --git a/ln.h b/ln.h
@@ -8,6 +8,9 @@
#define CELL_MAX_ELEMS 32
#define MAX_ALIAS_SIZE 32
+// TODO: parse pubkeys
+#define PUBKEY_SIZE 66
+
union color {
float rgba[4];
struct {
@@ -17,8 +20,10 @@ union color {
};
struct node {
- char alias[MAX_ALIAS_SIZE];
- const char *id; // pubkey
+ char alias[MAX_ALIAS_SIZE+1];
+ char id[PUBKEY_SIZE+1];
+
+ int filtered, mark_filtered;
union color color;
@@ -76,6 +81,9 @@ struct short_channel_id {
struct channel {
struct node *nodes[2];
+ char source[PUBKEY_SIZE+1];
+ char destination[PUBKEY_SIZE+1];
+
struct short_channel_id short_channel_id;
u8 public;
u8 active;
@@ -106,6 +114,7 @@ struct ln {
int window_width;
int window_height;
+ char *filter;
union color clear_color;
u64 display_flags;
@@ -113,6 +122,7 @@ struct ln {
struct cell *grid;
struct node *drag_target;
+ struct node *last_drag_target;
struct node *nodes;
u32 node_count;
@@ -124,5 +134,7 @@ struct ln {
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);
+void init_network(int ww, int wh, struct ln *ln);
+void filter_network(const char *nodeid, struct ln *ln);
#endif /* LNVIS_LN_H */
diff --git a/main.c b/main.c
@@ -2,6 +2,7 @@
#include <stdio.h>
#include <time.h>
+#include <assert.h>
#ifdef NANOVG_GLEW
#include <GL/glew.h>
#endif
@@ -101,14 +102,6 @@ void key_callback(GLFWwindow* window, int key, int scancode, int action, int mod
}
}
-static void print_node(struct node *node)
-{
- printf("node %s #%02X%02X%02X\n", node->alias,
- (int)node->color.r * 255,
- (int)node->color.g * 255,
- (int)node->color.b * 255);
-}
-
static void print_channel(struct channel *chan)
{
printf("chan shortid=%u:%u:%hu public=%d sats=%"PRIu64" active=%d "
@@ -126,13 +119,41 @@ static void print_channel(struct channel *chan)
chan->delay);
}
+static struct node *find_node(const char *pubkey, struct node *nodes, int node_count)
+{
+ // TODO: hash table
+ for (int i = 0; i < node_count; i++) {
+ if (streq(pubkey, nodes[i].id))
+ return &nodes[i];
+ /* printf("%s != %s\n", pubkey, nodes[i].id); */
+ }
+
+ return NULL;
+}
+
-void test_read_json()
+static void connect_node_channels(struct node *nodes, int node_count,
+ struct channel *channels, int channel_count)
+{
+ struct channel *chan = NULL;
+
+ for (int i = 0; i < channel_count; i++) {
+ chan = &channels[i];
+
+ chan->nodes[0] = find_node(chan->source, nodes, node_count);
+ chan->nodes[1] = find_node(chan->destination, nodes, node_count);
+
+ assert(chan->nodes[0]);
+ assert(chan->nodes[1]);
+ }
+}
+
+void test_read_json(struct ln *ln)
{
FILE *channels_fd = fopen("clightning-channels.json", "r");
FILE *nodes_fd = fopen("clightning-nodes.json", "r");
- int nchans = 0;
+ int channel_count = 0;
int node_count = 0;
int i;
struct channel *channels;
@@ -146,7 +167,7 @@ void test_read_json()
exit(1);
}
- res = parse_clightning_channels(channels_fd, &nchans, &channels);
+ res = parse_clightning_channels(channels_fd, &channel_count, &channels);
fclose(channels_fd);
if (res != 0) {
@@ -154,16 +175,13 @@ void test_read_json()
exit(1);
}
- for (i = 0; i < nchans; i++)
- print_channel(&channels[i]);
+ printf("parsed %d nodes, %d channels\n", node_count, channel_count);
- for (i = 0; i < node_count; i++)
- print_node(&nodes[i]);
-
- printf("%d nodes, %d channels\n", node_count, nchans);
-
-
- exit(0);
+ connect_node_channels(nodes, node_count, channels, channel_count);
+ ln->channels = channels;
+ ln->channel_count = channel_count;
+ ln->nodes = nodes;
+ ln->node_count = node_count;
}
int main()
@@ -182,12 +200,13 @@ int main()
static const int dark_theme = 1;
u64 flags = DISP_DARK
- /* | DISP_ALIASES */
+ | DISP_ALIASES
| DISP_GRID
- /* | DISP_STROKE_NODES */
+ | DISP_STROKE_NODES
;
- test_read_json();
+ const char *filter = "03f3c108ccd536b8526841f0a5c58212bb9e6584a1eb493080e7c1cc34f82dad71";
+ test_read_json(&ln);
ln.display_flags = flags;
@@ -239,6 +258,7 @@ int main()
}
ln.vg = vg;
+
init_ln(&ln, grid_div);
if (loadDemoData(vg, &data) == -1)
@@ -280,8 +300,9 @@ int main()
ln.window_width = winWidth;
if (first) {
- random_network(winWidth, winHeight, 3, 500, &ln);
- printf("channels %d\n", ln.channel_count);
+ /* random_network(winWidth, winHeight, 3, 500, &ln); */
+ init_network(winWidth, winHeight, &ln);
+ filter_network(filter, &ln);
first = 0;
}
diff --git a/render.c b/render.c
@@ -1,14 +1,21 @@
#include "render.h"
+#include "defs.h"
#include <stdio.h>
#include <assert.h>
+#include <math.h>
#include "nanovg/nanovg.h"
-void draw_channel(NVGcontext *vg, struct channel *channel)
+void draw_channel(NVGcontext *vg, struct ln *ln, struct channel *channel)
{
const struct node *n1 = channel->nodes[0];
const struct node *n2 = channel->nodes[1];
- static const float stroke = 2.0f;
+
+ if (!(n1->filtered && n2->filtered))
+ return;
+
+ const float stroke = max(1.0, channel->satoshis * 0.000001f);
+ /* const float stroke = (logf(channel->satoshis) / logf(10)) * 0.0f; */
const float sx = n1->x;
const float sy = n1->y;
@@ -16,10 +23,19 @@ void draw_channel(NVGcontext *vg, struct channel *channel)
const float ex = n2->x;
const float ey = n2->y;
+ union color n1t, n2t;
+
+ n1t.nvg_color = n1->color.nvg_color;
+ n2t.nvg_color = n2->color.nvg_color;
+
+ if (n1 != ln->last_drag_target)
+ n1t.a = 0.4;
+
+ if (n2 != ln->last_drag_target)
+ n2t.a = 0.4;
+
NVGpaint linear_grad =
- nvgLinearGradient(vg, sx, sy, ex, ey,
- n1->color.nvg_color,
- n2->color.nvg_color);
+ nvgLinearGradient(vg, sx, sy, ex, ey, n1t.nvg_color, n2t.nvg_color);
nvgSave(vg);
nvgStrokeWidth(vg, stroke);
@@ -70,20 +86,30 @@ void draw_grid(NVGcontext *vg, struct ln *ln) {
void draw_node(NVGcontext *vg, struct ln *ln, struct node *node)
{
+ if (!node->filtered)
+ return;
+
+ nvgFontSize(vg, 18.0f);
+ nvgFontFace(vg, "sans");
+ nvgTextAlign(vg, NVG_ALIGN_LEFT | NVG_ALIGN_TOP);
+
const float r = node->size;
- /* const float pos = 500.0; */
-
- /* const float h = r; */
- /* const float w = r; */
- /* const float x = pos; */
- /* const float y = pos; */
- /* const float kr = (int)(h * 0.35f); */
- /* const float cy = y + (int)(h * 0.5f); */
+ float bounds[4];
+ static const float pad = 2.0f;
+
+ nvgTextBounds(vg, 0, 0, node->alias, NULL, &bounds);
+
NVGpaint bg;
+ /* if (streq(node->alias, "@jb55")) */
+ /* printf("%f %f %f\n", node->color.r, */
+ /* node->color.g, node->color.b); */
+
NVGcolor node_color =
- nvgRGBAf(node->color.r, node->color.g, node->color.b,
- node->color.a);
+ nvgRGBf(node->color.r, node->color.g, node->color.b);
+
+ NVGcolor node_color_inv =
+ nvgRGBf(1.0-node->color.r, 1.0-node->color.g, 1.0-node->color.b);
static const float adj = 0.3f;
@@ -105,7 +131,7 @@ void draw_node(NVGcontext *vg, struct ln *ln, struct node *node)
const float light = 2.0f;
const int dark_theme = ln->display_flags & DISP_DARK;
- // TODO: use brightness instead of clear color for white theme
+ //TODO: use brightness instead of clear color for white theme
if (!dark_theme)
bg = nvgRadialGradient(vg, -light, -light, 0, r+2.0,
ln->clear_color.nvg_color,
@@ -127,12 +153,15 @@ void draw_node(NVGcontext *vg, struct ln *ln, struct node *node)
nvgFill(vg);
if (ln->display_flags & DISP_ALIASES) {
- nvgFontSize(vg, 18.0f);
- nvgFontFace(vg, "sans");
- nvgTextAlign(vg, NVG_ALIGN_LEFT | NVG_ALIGN_TOP);
+
+ nvgBeginPath(vg);
+ nvgRoundedRect(vg, -bounds[2] / 2.0, bounds[3], bounds[2], bounds[3], 5.0);
+ nvgFillColor(vg, nvgRGBAf(node_color.r, node_color.g, node_color.b, 0.9));
+ nvgFill(vg);
+
/* nvgTextMetrics(vg, NULL, NULL, &lineh); */
- nvgFillColor(vg, node_color);
- nvgText(vg, -r, r, node->alias, NULL);
+ nvgFillColor(vg, node_color_inv);
+ nvgText(vg, -bounds[2] / 2.0, bounds[3], node->alias, NULL);
}
nvgRestore(vg);
@@ -147,7 +176,7 @@ void render_ln(struct ln *ln)
// render channels first
for (i = 0; i < ln->channel_count; i++)
- draw_channel(vg, &ln->channels[i]);
+ draw_channel(vg, ln, &ln->channels[i]);
for (i = 0; i < ln->node_count; i++)
draw_node(vg, ln, &ln->nodes[i]);
diff --git a/update.c b/update.c
@@ -31,7 +31,7 @@ void repel_nodes(struct node *n1, struct node *n2, double dt) {
double d = sqrt(dx*dx + dy*dy);
- static const double mindist = 100.0;
+ static const double mindist = 200.0;
if (d < mindist) {
// normalized vector between two nodes
@@ -81,36 +81,39 @@ static void force_graph(struct ln *ln, double dt) {
struct node *n1 = channel->nodes[0];
struct node *n2 = channel->nodes[1];
+ if (!n1->filtered || !n2->filtered)
+ continue;
+
repel_nodes(n1, n2, dt);
}
}
-/* static void repel_nearby(struct node *node, double dt) */
-/* { */
-/* struct node *n = NULL; */
-/* struct cell *cell = node->cell; */
+static void repel_nearby(struct node *node, double dt)
+{
+ struct node *n = NULL;
+ struct cell *cell = node->cell;
-/* // might happen the first iteration? */
-/* if (cell == NULL) */
-/* return; */
+ // might happen the first iteration?
+ if (cell == NULL)
+ return;
-/* // we're the only one in this cell, there's nothing to repel */
-/* if (cell->node_count == 1) */
-/* return; */
+ // we're the only one in this cell, there's nothing to repel
+ if (cell->node_count == 1)
+ return;
-/* for (int i = 0; i < cell->node_count; ++i) { */
-/* n = cell->nodes[i]; */
+ for (int i = 0; i < cell->node_count; ++i) {
+ n = cell->nodes[i];
-/* // dont repel against ourselves */
-/* if (n == node) */
-/* continue; */
+ // dont repel against ourselves
+ if (n == node)
+ continue;
-/* assert(n); */
-/* assert(node); */
-/* repel_nodes(n, node, dt); */
-/* } */
-/* } */
+ assert(n);
+ assert(node);
+ repel_nodes(n, node, dt);
+ }
+}
static void physics(struct ln *ln, double dt)
@@ -122,6 +125,9 @@ static void physics(struct ln *ln, double dt)
for (u32 i = 0; i < ln->node_count; i++) {
struct node *node = &ln->nodes[i];
+ if (!node->filtered)
+ continue;
+
/* repel_nearby(node, dt); */
// semi-implicit euler
@@ -150,11 +156,14 @@ void update(struct ln *ln, double dt)
if (ln->clicked) {
struct node *hit = hit_node(ln);
ln->drag_target = hit;
+ ln->last_drag_target = hit;
}
// stop dragging
- if (!ln->mdown && ln->drag_target)
+ if (!ln->mdown && ln->drag_target) {
+ ln->last_drag_target = ln->drag_target;
ln->drag_target = NULL;
+ }
// drag
if (ln->mdown && ln->drag_target) {
@@ -164,9 +173,9 @@ void update(struct ln *ln, double dt)
ln->drag_target->vy = 0;
}
- force_graph(ln, dt);
+ /* force_graph(ln, dt); */
- physics(ln, dt);
+ /* physics(ln, dt); */
/* update_grid_move_nodes(ln); */
}