]> git.tdb.fi Git - ext/subsurface.git/blob - profile.c
Remove the redundant frames in the notebook. Closes #9
[ext/subsurface.git] / profile.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <time.h>
4
5 #include "dive.h"
6 #include "display.h"
7
8 int selected_dive = 0;
9
10 #define ROUND_UP(x,y) ((((x)+(y)-1)/(y))*(y))
11
12 /*
13  * When showing dive profiles, we scale things to the
14  * current dive. However, we don't scale past less than
15  * 30 minutes or 90 ft, just so that small dives show
16  * up as such.
17  */
18 static int round_seconds_up(int seconds)
19 {
20         return MAX(30*60, ROUND_UP(seconds, 60*10));
21 }
22
23 static int round_feet_up(int feet)
24 {
25         return MAX(90, ROUND_UP(feet+5, 15));
26 }
27
28 /* Scale to 0,0 -> maxx,maxy */
29 #define SCALE(x,y) (x)*maxx/scalex+topx,(y)*maxy/scaley+topy
30
31 static void plot_profile(struct dive *dive, cairo_t *cr,
32         double topx, double topy, double maxx, double maxy)
33 {
34         double scalex, scaley;
35         int begins, sec, depth;
36         int i, samples;
37         struct sample *sample;
38         int maxtime, maxdepth;
39
40         samples = dive->samples;
41         if (!samples)
42                 return;
43
44         cairo_set_line_width(cr, 2);
45
46         /* Get plot scaling limits */
47         maxtime = round_seconds_up(dive->duration.seconds);
48         maxdepth = round_feet_up(to_feet(dive->maxdepth));
49
50         /* Time markers: every 5 min */
51         scalex = maxtime;
52         scaley = 1.0;
53         for (i = 5*60; i < maxtime; i += 5*60) {
54                 cairo_move_to(cr, SCALE(i, 0));
55                 cairo_line_to(cr, SCALE(i, 1));
56         }
57
58         /* Depth markers: every 15 ft */
59         scalex = 1.0;
60         scaley = maxdepth;
61         cairo_set_source_rgba(cr, 1, 1, 1, 0.5);
62         for (i = 15; i < maxdepth; i += 15) {
63                 cairo_move_to(cr, SCALE(0, i));
64                 cairo_line_to(cr, SCALE(1, i));
65         }
66         cairo_stroke(cr);
67
68         /* Show mean depth */
69         cairo_set_source_rgba(cr, 1, 0.2, 0.2, 0.40);
70         cairo_move_to(cr, SCALE(0, to_feet(dive->meandepth)));
71         cairo_line_to(cr, SCALE(1, to_feet(dive->meandepth)));
72         cairo_stroke(cr);
73
74         scalex = maxtime;
75
76         sample = dive->sample;
77         cairo_set_source_rgba(cr, 1, 0.2, 0.2, 0.80);
78         begins = sample->time.seconds;
79         cairo_move_to(cr, SCALE(sample->time.seconds, to_feet(sample->depth)));
80         for (i = 1; i < dive->samples; i++) {
81                 sample++;
82                 sec = sample->time.seconds;
83                 depth = to_feet(sample->depth);
84                 cairo_line_to(cr, SCALE(sec, depth));
85         }
86         scaley = 1.0;
87         cairo_line_to(cr, SCALE(sec, 0));
88         cairo_line_to(cr, SCALE(begins, 0));
89         cairo_close_path(cr);
90         cairo_set_source_rgba(cr, 1, 0.2, 0.2, 0.20);
91         cairo_fill_preserve(cr);
92         cairo_set_source_rgba(cr, 1, 0.2, 0.2, 0.80);
93         cairo_stroke(cr);
94 }
95
96 static int get_cylinder_pressure_range(struct dive *dive, double *scalex, double *scaley)
97 {
98         int i;
99         double min, max;
100
101         *scalex = round_seconds_up(dive->duration.seconds);
102
103         max = 0;
104         min = 5000;
105         for (i = 0; i < dive->samples; i++) {
106                 struct sample *sample = dive->sample + i;
107                 double bar;
108
109                 if (!sample->cylinderpressure.mbar)
110                         continue;
111                 bar = sample->cylinderpressure.mbar;
112                 if (bar < min)
113                         min = bar;
114                 if (bar > max)
115                         max = bar;
116         }
117         if (!max)
118                 return 0;
119         *scaley = max * 1.5;
120         return 1;
121 }
122
123 static void plot_cylinder_pressure(struct dive *dive, cairo_t *cr,
124         double topx, double topy, double maxx, double maxy)
125 {
126         int i;
127         double scalex, scaley;
128
129         if (!get_cylinder_pressure_range(dive, &scalex, &scaley))
130                 return;
131
132         cairo_set_source_rgba(cr, 0.2, 1.0, 0.2, 0.80);
133
134         cairo_move_to(cr, SCALE(0, dive->beginning_pressure.mbar));
135         for (i = 1; i < dive->samples; i++) {
136                 int sec, mbar;
137                 struct sample *sample = dive->sample + i;
138
139                 sec = sample->time.seconds;
140                 mbar = sample->cylinderpressure.mbar;
141                 if (!mbar)
142                         continue;
143                 cairo_line_to(cr, SCALE(sec, mbar));
144         }
145         cairo_line_to(cr, SCALE(dive->duration.seconds, dive->end_pressure.mbar));
146         cairo_stroke(cr);
147 }
148
149 static void plot(cairo_t *cr, int w, int h, struct dive *dive)
150 {
151         double topx, topy, maxx, maxy;
152         double scalex, scaley;
153
154         topx = w / 20.0;
155         topy = h / 20.0;
156         maxx = (w - 2*topx);
157         maxy = (h - 2*topy);
158
159         /* Depth profile */
160         plot_profile(dive, cr, topx, topy, maxx, maxy);
161
162         /* Cylinder pressure plot? */
163         plot_cylinder_pressure(dive, cr, topx, topy, maxx, maxy);
164
165         /* Bounding box last */
166         scalex = scaley = 1.0;
167         cairo_set_source_rgb(cr, 1, 1, 1);
168         cairo_move_to(cr, SCALE(0,0));
169         cairo_line_to(cr, SCALE(0,1));
170         cairo_line_to(cr, SCALE(1,1));
171         cairo_line_to(cr, SCALE(1,0));
172         cairo_close_path(cr);
173         cairo_stroke(cr);
174
175 }
176
177 static gboolean expose_event(GtkWidget *widget, GdkEventExpose *event, gpointer data)
178 {
179         struct dive *dive = current_dive;
180         cairo_t *cr;
181         int w,h;
182
183         w = widget->allocation.width;
184         h = widget->allocation.height;
185
186         cr = gdk_cairo_create(widget->window);
187         cairo_set_source_rgb(cr, 0, 0, 0);
188         cairo_paint(cr);
189
190         if (dive)
191                 plot(cr, w, h, dive);
192
193         cairo_destroy(cr);
194
195         return FALSE;
196 }
197
198 GtkWidget *dive_profile_widget(void)
199 {
200         GtkWidget *da;
201
202         da = gtk_drawing_area_new();
203         gtk_widget_set_size_request(da, 450, 350);
204         g_signal_connect(da, "expose_event", G_CALLBACK(expose_event), NULL);
205
206         return da;
207 }