2021-10-02 12:58:03 +00:00
|
|
|
#include "graph.h"
|
2021-10-18 16:10:51 +00:00
|
|
|
|
2022-02-13 15:00:45 +00:00
|
|
|
void graph_init(struct graph* graph) {
|
|
|
|
graph->width = 0;
|
2021-10-02 12:58:03 +00:00
|
|
|
graph->cursor = 0;
|
|
|
|
|
|
|
|
graph->line_color = rgba_color_from_hex(0xcccccc);
|
|
|
|
graph->fill_color = rgba_color_from_hex(0xcccccc);
|
2022-02-13 15:00:45 +00:00
|
|
|
graph->line_width = 0.5;
|
2021-10-02 12:58:03 +00:00
|
|
|
graph->fill = true;
|
|
|
|
graph->overrides_fill_color = false;
|
|
|
|
graph->enabled = true;
|
|
|
|
}
|
|
|
|
|
2022-02-13 15:00:45 +00:00
|
|
|
void graph_setup(struct graph* graph, uint32_t width) {
|
|
|
|
graph->width = width;
|
|
|
|
graph->y = malloc(sizeof(float) * width);
|
|
|
|
memset(graph->y, 0, sizeof(float) * width);
|
|
|
|
}
|
|
|
|
|
2021-10-02 12:58:03 +00:00
|
|
|
float graph_get_y(struct graph* graph, uint32_t i) {
|
|
|
|
if (!graph->enabled) return 0.f;
|
|
|
|
return graph->y[ (graph->cursor + i)%graph->width ];
|
|
|
|
}
|
|
|
|
|
|
|
|
void graph_push_back(struct graph* graph, float y) {
|
|
|
|
if (!graph->enabled) return;
|
|
|
|
graph->y[graph->cursor] = y;
|
|
|
|
|
|
|
|
++graph->cursor;
|
|
|
|
graph->cursor %= graph->width;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint32_t graph_get_length(struct graph* graph) {
|
|
|
|
if (graph->enabled) return graph->width;
|
|
|
|
return 0;
|
|
|
|
}
|
2021-10-18 16:10:51 +00:00
|
|
|
|
2021-11-11 16:56:44 +00:00
|
|
|
void graph_calculate_bounds(struct graph* graph, uint32_t x, uint32_t y) {
|
|
|
|
graph->bounds.origin.x = x;
|
2022-03-25 20:52:37 +00:00
|
|
|
graph->bounds.origin.y = y - graph->bounds.size.height / 2
|
|
|
|
+ graph->line_width;
|
2021-11-11 16:56:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void graph_draw(struct graph* graph, CGContextRef context) {
|
2021-11-23 22:36:36 +00:00
|
|
|
uint32_t x = graph->bounds.origin.x + (graph->rtl ? graph->width : 0);
|
2021-11-11 16:56:44 +00:00
|
|
|
uint32_t y = graph->bounds.origin.y;
|
|
|
|
uint32_t height = graph->bounds.size.height;
|
|
|
|
|
|
|
|
uint32_t sample_width = 1;
|
|
|
|
bool fill = graph->fill;
|
|
|
|
CGContextSaveGState(context);
|
2022-03-25 20:52:37 +00:00
|
|
|
CGContextSetRGBStrokeColor(context,
|
|
|
|
graph->line_color.r,
|
|
|
|
graph->line_color.g,
|
|
|
|
graph->line_color.b,
|
|
|
|
graph->line_color.a );
|
|
|
|
|
|
|
|
if (graph->overrides_fill_color)
|
|
|
|
CGContextSetRGBFillColor(context,
|
|
|
|
graph->fill_color.r,
|
|
|
|
graph->fill_color.g,
|
|
|
|
graph->fill_color.b,
|
|
|
|
graph->fill_color.a );
|
|
|
|
else
|
|
|
|
CGContextSetRGBFillColor(context,
|
|
|
|
graph->line_color.r,
|
|
|
|
graph->line_color.g,
|
|
|
|
graph->line_color.b,
|
|
|
|
0.2 * graph->line_color.a);
|
|
|
|
|
2021-11-11 16:56:44 +00:00
|
|
|
CGContextSetLineWidth(context, graph->line_width);
|
|
|
|
CGMutablePathRef p = CGPathCreateMutable();
|
|
|
|
uint32_t start_x = x;
|
|
|
|
if (graph->rtl) {
|
2022-03-25 20:52:37 +00:00
|
|
|
CGPathMoveToPoint(p,
|
|
|
|
NULL,
|
|
|
|
x,
|
|
|
|
y + graph_get_y(graph, graph->width - 1) * height);
|
|
|
|
|
2021-11-11 16:56:44 +00:00
|
|
|
for (int i = graph->width - 1; i > 0; --i, x -= sample_width) {
|
|
|
|
CGPathAddLineToPoint(p, NULL, x, y + graph_get_y(graph, i) * height);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
CGPathMoveToPoint(p, NULL, x, y + graph_get_y(graph, 0) * height);
|
|
|
|
for (int i = graph->width - 1; i > 0; --i, x += sample_width) {
|
|
|
|
CGPathAddLineToPoint(p, NULL, x, y + graph_get_y(graph, i) * height);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
CGContextAddPath(context, p);
|
|
|
|
CGContextStrokePath(context);
|
|
|
|
if (fill) {
|
|
|
|
if (graph->rtl) {
|
|
|
|
CGPathAddLineToPoint(p, NULL, x + sample_width, y);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
CGPathAddLineToPoint(p, NULL, x - sample_width, y);
|
|
|
|
}
|
|
|
|
CGPathAddLineToPoint(p, NULL, start_x, y);
|
|
|
|
CGPathCloseSubpath(p);
|
|
|
|
CGContextAddPath(context, p);
|
|
|
|
CGContextFillPath(context);
|
|
|
|
}
|
|
|
|
CGPathRelease(p);
|
|
|
|
CGContextRestoreGState(context);
|
|
|
|
}
|
|
|
|
|
2022-07-20 15:46:06 +00:00
|
|
|
void graph_serialize(struct graph* graph, char* indent, FILE* rsp) {
|
|
|
|
fprintf(rsp, "%s\"graph.color\": \"0x%x\",\n"
|
|
|
|
"%s\"graph.fill_color\": \"0x%x\",\n"
|
|
|
|
"%s\"graph.line_width\": \"%f\",\n"
|
|
|
|
"%s\"data\": [\n",
|
|
|
|
indent, hex_from_rgba_color(graph->line_color),
|
|
|
|
indent, hex_from_rgba_color(graph->fill_color),
|
|
|
|
indent, graph->line_width, indent);
|
2021-11-30 16:59:56 +00:00
|
|
|
int counter = 0;
|
|
|
|
for (int i = 0; i < graph->width; i++) {
|
|
|
|
if (counter++ > 0) fprintf(rsp, ",\n");
|
2022-07-20 15:46:06 +00:00
|
|
|
fprintf(rsp, "%s\t\"%f\"", indent, graph->y[i]);
|
2021-11-30 16:59:56 +00:00
|
|
|
}
|
2022-07-20 15:46:06 +00:00
|
|
|
fprintf(rsp, "\n%s]", indent);
|
2021-11-30 16:59:56 +00:00
|
|
|
}
|
|
|
|
|
2022-01-07 19:13:57 +00:00
|
|
|
void graph_destroy(struct graph* graph) {
|
|
|
|
if (!graph->enabled) return;
|
|
|
|
free(graph->y);
|
|
|
|
}
|
|
|
|
|
2022-03-20 21:16:38 +00:00
|
|
|
bool graph_parse_sub_domain(struct graph* graph, FILE* rsp, struct token property, char* message) {
|
2021-10-18 16:10:51 +00:00
|
|
|
if (token_equals(property, PROPERTY_COLOR)) {
|
|
|
|
graph->line_color = rgba_color_from_hex(token_to_uint32t(get_token(&message)));
|
|
|
|
return true;
|
|
|
|
} else if (token_equals(property, PROPERTY_FILL_COLOR)) {
|
|
|
|
graph->fill_color = rgba_color_from_hex(token_to_uint32t(get_token(&message)));
|
|
|
|
graph->overrides_fill_color = true;
|
|
|
|
return true;
|
|
|
|
} else if (token_equals(property, PROPERTY_LINE_WIDTH)) {
|
|
|
|
graph->line_width = token_to_float(get_token(&message));
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
else {
|
2022-06-09 19:14:57 +00:00
|
|
|
respond(rsp, "[!] Graph: Invalid property '%s'\n", property.text);
|
2021-10-18 16:10:51 +00:00
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|