]>
Commit | Line | Data |
---|---|---|
5a945372 SW |
1 | /* |
2 | * h/t https://ptomato.name/advanced-gtk-techniques/html/custom-container.html | |
3 | * for examples of how to make GTK widgets | |
4 | */ | |
5 | ||
6 | #include "chart.h" | |
148e0e28 | 7 | #include <float.h> |
5a945372 SW |
8 | #include <gtk/gtk.h> |
9 | ||
10 | #define BV_CHART_PRIVATE(obj) \ | |
11 | (G_TYPE_INSTANCE_GET_PRIVATE((obj), BV_CHART_TYPE, BVChartPrivate)) | |
12 | ||
13 | typedef struct _BVChartPrivate BVChartPrivate; | |
14 | ||
15 | struct _BVChartPrivate { | |
27db65e2 | 16 | GArray *points; |
148e0e28 | 17 | float minx, miny, maxx, maxy; |
27db65e2 SW |
18 | }; |
19 | ||
20 | struct BVChartPoint { | |
21 | float x, y; | |
5a945372 SW |
22 | }; |
23 | ||
24 | G_DEFINE_TYPE_WITH_CODE(BVChart, bv_chart, GTK_TYPE_DRAWING_AREA, | |
25 | G_ADD_PRIVATE(BVChart)) | |
26 | ||
328956d5 SW |
27 | typedef void (*cairo_draw_func)(cairo_t *cr, double x, double y); |
28 | ||
366d0933 SW |
29 | static gboolean bv_chart_draw(GtkWidget *widget, cairo_t *cr) { |
30 | BVChart *chart = BV_CHART(widget); | |
31 | BVChartPrivate *priv = bv_chart_get_instance_private(chart); | |
32 | ||
33 | cairo_set_source_rgb(cr, 1.0, 1.0, 1.0); | |
34 | cairo_paint(cr); | |
35 | ||
36 | if (priv->points->len < 2) | |
37 | return TRUE; | |
38 | ||
39 | GtkAllocation allocation; | |
40 | gtk_widget_get_allocation(widget, &allocation); | |
41 | ||
abe665b4 SW |
42 | int margin = 3; |
43 | float xscale = (allocation.width - 2 * margin) / (priv->maxx - priv->minx); | |
44 | float yscale = (allocation.height - 2 * margin) / (priv->miny - priv->maxy); | |
366d0933 | 45 | cairo_set_source_rgb(cr, 0.0, 0.0, 1.0); |
328956d5 SW |
46 | float gap_threshold = 3.0; |
47 | float prevx = 0.0; | |
48 | for (guint i = 0; i < priv->points->len; i++) { | |
366d0933 SW |
49 | struct BVChartPoint *p = |
50 | &g_array_index(priv->points, struct BVChartPoint, i); | |
328956d5 SW |
51 | gboolean is_gap = p->x - prevx > gap_threshold; |
52 | cairo_draw_func f = is_gap ? cairo_move_to : cairo_line_to; | |
53 | float screen_x = xscale * (p->x - priv->minx) + margin; | |
54 | float screen_y = yscale * (p->y - priv->maxy) + margin; | |
55 | f(cr, screen_x, screen_y); | |
56 | prevx = p->x; | |
366d0933 SW |
57 | } |
58 | cairo_stroke(cr); | |
59 | ||
60 | return TRUE; | |
61 | } | |
62 | ||
63 | static void bv_chart_class_init(BVChartClass *klass) { | |
64 | GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass); | |
65 | widget_class->draw = bv_chart_draw; | |
66 | } | |
5a945372 SW |
67 | |
68 | static void bv_chart_init(BVChart *chart) { | |
69 | gtk_widget_set_has_window(GTK_WIDGET(chart), FALSE); | |
27db65e2 SW |
70 | BVChartPrivate *priv = bv_chart_get_instance_private(chart); |
71 | gboolean zero_terminated = FALSE; | |
72 | gboolean clear_ = FALSE; | |
73 | priv->points = | |
74 | g_array_new(zero_terminated, clear_, sizeof(struct BVChartPoint)); | |
148e0e28 SW |
75 | priv->minx = FLT_MAX; |
76 | priv->miny = FLT_MAX; | |
77 | priv->maxx = FLT_MIN; | |
78 | priv->maxy = FLT_MIN; | |
5a945372 SW |
79 | } |
80 | ||
81 | GtkWidget *bv_chart_new() { | |
82 | return GTK_WIDGET(g_object_new(bv_chart_get_type(), NULL)); | |
83 | } | |
27db65e2 SW |
84 | |
85 | void bv_chart_add_point(BVChart *chart, float x, float y) { | |
86 | BVChartPrivate *priv = bv_chart_get_instance_private(chart); | |
148e0e28 SW |
87 | if (x < priv->minx) |
88 | priv->minx = x; | |
89 | if (y < priv->miny) | |
90 | priv->miny = y; | |
91 | if (x > priv->maxx) | |
92 | priv->maxx = x; | |
93 | if (y > priv->maxy) | |
94 | priv->maxy = y; | |
27db65e2 SW |
95 | struct BVChartPoint p = {.x = x, .y = y}; |
96 | g_array_append_val(priv->points, p); | |
97 | } |