lnvis

nanovg lightning network visualizer
git clone git://jb55.com/lnvis
Log | Files | Refs | README | LICENSE

render.c (6790B)


      1 
      2 #include "render.h"
      3 #include "defs.h"
      4 #include <stdio.h>
      5 #include <assert.h>
      6 #include <math.h>
      7 #include "nanovg/nanovg.h"
      8 #include <stdlib.h>
      9 
     10 
     11 #define  Pr  .299
     12 #define  Pg  .587
     13 #define  Pb  .114
     14 
     15 static inline double rand_0to1() {
     16 	return (double) rand() / RAND_MAX;
     17 }
     18 
     19 
     20 void saturate(union color *c, double change)
     21 {
     22 	double  P=sqrt(
     23 		(c->r)*(c->r)*Pr+
     24 		(c->g)*(c->g)*Pg+
     25 		(c->b)*(c->b)*Pb ) ;
     26 
     27 	c->r = P+((c->r)-P)*change;
     28 	c->g = P+((c->g)-P)*change;
     29 	c->b = P+((c->b)-P)*change;
     30 }
     31 
     32 void draw_channel(NVGcontext *vg, struct ln *ln, struct channel *channel)
     33 {
     34 	if (!channel->visible)
     35 		return;
     36 
     37 	const struct node *n1 = channel->nodes[0];
     38 	const struct node *n2 = channel->nodes[1];
     39 
     40 	if (!n1->visible || !n2->visible)
     41 		return;
     42 
     43 	const float stroke = max(1.0, channel->satoshis * 0.000001f);
     44 	/* const float stroke = (logf(channel->satoshis) / logf(10)) * 0.0f; */
     45 
     46 	union color n1t, n2t;
     47 
     48 	n1t.nvg_color = n1->color.nvg_color;
     49 	n2t.nvg_color = n2->color.nvg_color;
     50 
     51 	NVGcolor c;
     52 
     53 	if (nodeid_eq(channel->source, n1->id))
     54 		c = n1t.nvg_color;
     55 	else
     56 		c = n2t.nvg_color;
     57 
     58 	if (channel->active && (channel->nodes[0] == ln->last_drag_target || channel->nodes[1] == ln->last_drag_target))
     59 		c.a = 0.9;
     60 	else {
     61 		if (!channel->active || ln->drag_target) {
     62 			saturate((union color*)&c, 0.01);
     63 			c.a = 0.1;
     64 		}
     65 		else
     66 			c.a = 0.4;
     67 	}
     68 
     69 
     70 	/* NVGpaint linear_grad = */
     71 	/* 	nvgLinearGradient(vg, sx, sy, ex, ey, n1t.nvg_color, n2t.nvg_color); */
     72 
     73 	nvgSave(vg);
     74 	nvgStrokeWidth(vg, stroke);
     75 
     76 	/* nvgStrokePaint(vg, linear_grad); */
     77 	nvgStrokeColor(vg, c);
     78 	nvgBeginPath(vg);
     79 
     80 #define TAU (2.0*NVG_PI)
     81 
     82 
     83 
     84 	nvgMoveTo(vg, n1->x, n1->y);
     85 
     86 	if (ln->display_flags & DISP_BEZIER) {
     87 		srand(channel->short_channel_id.blocknum ^
     88 		      channel->short_channel_id.outnum ^
     89 		      channel->short_channel_id.txnum);
     90 		float ang1 = TAU*rand_0to1();
     91 		float ang2 = TAU*rand_0to1();
     92 
     93 		float dx = n2->x - n1->x;
     94 		float dy = n2->y - n1->y;
     95 
     96 		float len = sqrt(dx*dx + dy*dy);
     97 
     98 		/* float nx = dx/len; */
     99 		/* float ny = dy/len; */
    100 
    101 		const float dist = 1.0/3.0;
    102 
    103 		float ax1 = cos(ang1) * len * dist;
    104 		float ay1 = sin(ang1) * len * dist;
    105 
    106 		float ax2 = cos(ang2) * len * dist;
    107 		float ay2 = sin(ang2) * len * dist;
    108 
    109 		nvgBezierTo(vg, n1->x + ax1,
    110 			        n1->y + ay1,
    111 			        n2->x - ax2,
    112 			        n2->y - ay2,
    113 			        n2->x, n2->y);
    114 	}
    115 	else {
    116 		nvgLineTo(vg, n2->x, n2->y);
    117 	}
    118 	/* nvgMoveTo(vg, n2->x, n2->y); */
    119 	/* nvgArc(vg, n1->x, n1->y, TAU*4.0, TAU/8.0, 100.0, NVG_CCW); */
    120 	/* nvgClosePath(vg); */
    121 	/* nvgArcTo(vg, n1->x, n1->y, n2->x, n2->y, 100.0); */
    122 	/* nvgLineTo(vg, n2->x, n2->y); */
    123 	nvgStroke(vg);
    124 	nvgRestore(vg);
    125 }
    126 
    127 
    128 void draw_grid(NVGcontext *vg, struct ln *ln) {
    129 	static const float adj = 0.6f;
    130 	const int grid_div = ln->grid_div;
    131 	const int ww = ln->window_width;
    132 	const int wh = ln->window_height;
    133 
    134 	NVGcolor clear_adj =
    135 		nvgRGBAf(ln->clear_color.r * adj,
    136 			 ln->clear_color.g * adj,
    137 			 ln->clear_color.b * adj,
    138 			 1.0f);
    139 
    140 	for (int x = 0; x < grid_div; ++x)  {
    141 		double px = ww / grid_div * x;
    142 			//int py = wh / grid_div * y;
    143 
    144 		nvgBeginPath(vg);
    145 		nvgMoveTo(vg, px, 0);
    146 		nvgLineTo(vg, px, wh);
    147 		nvgStrokeColor(vg, clear_adj);
    148 		nvgStroke(vg);
    149 	}
    150 
    151 	for (int y = 0; y < grid_div; ++y)  {
    152 		double py = wh / grid_div * y;
    153 		//int py = wh / grid_div * y;
    154 
    155 		nvgBeginPath(vg);
    156 		nvgMoveTo(vg, 0, py);
    157 		nvgLineTo(vg, ww, py);
    158 		nvgStroke(vg);
    159 	}
    160 }
    161 
    162 
    163 void draw_node(NVGcontext *vg, struct ln *ln, struct node *node)
    164 {
    165 	if (!node->visible)
    166 		return;
    167 
    168 	nvgFontSize(vg, 18.0f);
    169 	nvgFontFace(vg, "sans");
    170 	nvgTextAlign(vg, NVG_ALIGN_LEFT | NVG_ALIGN_TOP);
    171 
    172 	const float r = node->size;
    173 	float bounds[4];
    174 	static const float pad = 2.0f;
    175 
    176 	nvgTextBounds(vg, 0, 0, node->alias, NULL, &bounds[0]);
    177 
    178 	NVGpaint bg;
    179 
    180 	/* if (streq(node->alias, "@jb55")) */
    181 	/* 	printf("%f %f %f\n", node->color.r, */
    182 	/* 		node->color.g, node->color.b); */
    183 
    184 	NVGcolor node_color =
    185 		nvgRGBf(node->color.r, node->color.g, node->color.b);
    186 
    187 	NVGcolor node_color_inv =
    188 		nvgRGBf(1.0-node->color.r, 1.0-node->color.g, 1.0-node->color.b);
    189 
    190 	static const float adj = 0.3f;
    191 
    192 	NVGcolor clear_adj =
    193 		nvgRGBAf(ln->clear_color.r * adj,
    194 			 ln->clear_color.g * adj,
    195 			 ln->clear_color.b * adj,
    196 			 1.0f);
    197 
    198 	NVGcolor blend =
    199 		nvgRGBAf(node->color.r * adj,
    200 			 node->color.g * adj,
    201 			 node->color.b * adj,
    202 			 1.0f);
    203 
    204 	nvgSave(vg);
    205 	nvgTranslate(vg, node->x, node->y);
    206 
    207 	const float light = 2.0f;
    208 	const int dark_theme = ln->display_flags & DISP_DARK;
    209 
    210 	//TODO: use brightness instead of clear color for white theme
    211 	nvgBeginPath(vg);
    212 	nvgCircle(vg, 0, 0, r);
    213 	/* nvgPathWinding(vg, NVG_HOLE); */
    214 
    215 
    216 	if (node->adj_drag_target || node == ln->last_drag_target) {
    217 		node_color.a = 1.0;
    218 		node_color_inv.a = 1.0;
    219 		blend.a = 1.0;
    220 		clear_adj.a = 1.0;
    221 	}
    222 	else {
    223 		if (ln->drag_target) {
    224 			saturate((union color*)&node_color, 0.01);
    225 			node_color.a = 0.1;
    226 			node_color_inv.a = 0.1;
    227 			blend.a = 0.1;
    228 			clear_adj.a = 0.1;
    229 		}
    230 		else {
    231 			node_color.a = 0.9;
    232 			node_color_inv.a = 0.9;
    233 			blend.a = 0.9;
    234 			clear_adj.a = 0.9;
    235 		}
    236 	}
    237 
    238 	if (!dark_theme)
    239 		bg = nvgRadialGradient(vg, -light, -light, 0, r+2.0,
    240 				       ln->clear_color.nvg_color,
    241 				       node_color);
    242 	else
    243 		bg = nvgRadialGradient(vg, -light, -light, 0, r+2.0, node_color, blend);
    244 
    245 	if (ln->display_flags & DISP_STROKE_NODES) {
    246 		if (ln->filter_target == node) {
    247 			nvgStrokeColor(vg, node_color_inv);
    248 			nvgStrokeWidth(vg, 5.0f);
    249 		}
    250 		else {
    251 			nvgStrokeColor(vg, dark_theme ? clear_adj : ln->clear_color.nvg_color);
    252 			nvgStrokeWidth(vg, 3.0f);
    253 		}
    254 		nvgStroke(vg);
    255 	}
    256 
    257 	nvgFillPaint(vg, bg);
    258 	nvgFill(vg);
    259 
    260 	int is_adj = !ln->drag_target || ((ln->drag_target && node->adj_drag_target) || node == ln->drag_target);
    261 
    262 	if (is_adj && ln->display_flags & DISP_ALIASES) {
    263 
    264 		nvgBeginPath(vg);
    265 		nvgRoundedRect(vg, -bounds[2] / 2.0 - pad, bounds[3] - pad, bounds[2] + pad * 2.0, bounds[3] + pad * 2.0, 5.0);
    266 		nvgFillColor(vg, nvgRGBAf(node_color.r, node_color.g, node_color.b, node_color.a));
    267 		nvgFill(vg);
    268 
    269 		/* nvgTextMetrics(vg, NULL, NULL, &lineh); */
    270 		nvgFillColor(vg, node_color_inv);
    271 		nvgText(vg, -bounds[2] / 2.0, bounds[3], node->alias, NULL);
    272 	}
    273 
    274 	nvgRestore(vg);
    275 }
    276 
    277 void render_ln(struct ln *ln)
    278 {
    279 	u32 i;
    280 	NVGcontext *vg = ln->vg;
    281 
    282 	struct channel *c = NULL;
    283 
    284 	if (ln->display_flags & DISP_GRID)
    285 		draw_grid(vg, ln);
    286 
    287 	// render channels first
    288 	for (i = 0; i < ln->channel_count; i++) {
    289 		c = &ln->channels[i];
    290 
    291 		if (c->nodes[0] == ln->last_drag_target ||
    292 		    c->nodes[1] == ln->last_drag_target)
    293 			c->draw_last = 1;
    294 		else
    295 			draw_channel(vg, ln, c);
    296 	}
    297 
    298 	// draw important channels last
    299 	for (i = 0; i < ln->channel_count; i++) {
    300 		c = &ln->channels[i];
    301 
    302 		if (!c->draw_last)
    303 			continue;
    304 
    305 		c->draw_last = 0;
    306 		draw_channel(vg, ln, c);
    307 	}
    308 
    309 
    310 	for (i = 0; i < ln->node_count; i++)
    311 		draw_node(vg, ln, &ln->nodes[i]);
    312 }