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 }