10 int selected_dive = 0;
12 #define ROUND_UP(x,y) ((((x)+(y)-1)/(y))*(y))
15 * When showing dive profiles, we scale things to the
16 * current dive. However, we don't scale past less than
17 * 30 minutes or 90 ft, just so that small dives show
20 static int round_seconds_up(int seconds)
22 return MAX(30*60, ROUND_UP(seconds, 60*10));
25 static int round_feet_up(int feet)
27 return MAX(90, ROUND_UP(feet+5, 15));
30 static void plot_text(cairo_t *cr, double x, double y, const char *fmt, ...)
32 cairo_text_extents_t extents;
37 vsnprintf(buffer, sizeof(buffer), fmt, args);
40 cairo_text_extents(cr, buffer, &extents);
42 x -= extents.width/2 + extents.x_bearing;
43 y += extents.height * 1.2;
45 cairo_move_to(cr, x, y);
46 cairo_text_path(cr, buffer);
47 cairo_set_source_rgb(cr, 0, 0, 0);
50 cairo_move_to(cr, x, y);
51 cairo_set_source_rgb(cr, 1, 0, 0);
52 cairo_show_text(cr, buffer);
56 * Find the next maximum point in a 10-minute window.
58 * We exit early if we hit "enough" of a depth reversal,
59 * which is roughly 10 feet.
61 static int next_minmax(struct dive *dive, int index, int minmax)
63 const int enough = 3000;
64 int timelimit, depthlimit, result;
65 struct sample *sample = dive->sample + index;
67 if (index >= dive->samples)
71 depthlimit = sample->depth.mm;
79 if (index >= dive->samples)
81 time = sample->time.seconds;
82 depth = sample->depth.mm;
87 if (depth <= depthlimit) {
88 if (depthlimit - depth > enough)
93 if (depth >= depthlimit) {
94 if (depth - depthlimit > enough)
102 /* Look up to ten minutes into the future */
103 timelimit = time + 600;
108 /* Scale to 0,0 -> maxx,maxy */
109 #define SCALE(x,y) (x)*maxx/scalex+topx,(y)*maxy/scaley+topy
111 static void plot_profile(struct dive *dive, cairo_t *cr,
112 double topx, double topy, double maxx, double maxy)
114 double scalex, scaley;
115 int begins, sec, depth;
117 struct sample *sample;
118 int maxtime, maxdepth, mindepth;
120 samples = dive->samples;
124 cairo_set_line_width(cr, 2);
126 /* Get plot scaling limits */
127 maxtime = round_seconds_up(dive->duration.seconds);
128 maxdepth = round_feet_up(to_feet(dive->maxdepth));
130 /* Time markers: every 5 min */
133 for (i = 5*60; i < maxtime; i += 5*60) {
134 cairo_move_to(cr, SCALE(i, 0));
135 cairo_line_to(cr, SCALE(i, 1));
138 /* Depth markers: every 15 ft */
141 cairo_set_source_rgba(cr, 1, 1, 1, 0.5);
142 for (i = 15; i < maxdepth; i += 15) {
143 cairo_move_to(cr, SCALE(0, i));
144 cairo_line_to(cr, SCALE(1, i));
148 /* Show mean depth */
149 cairo_set_source_rgba(cr, 1, 0.2, 0.2, 0.40);
150 cairo_move_to(cr, SCALE(0, to_feet(dive->meandepth)));
151 cairo_line_to(cr, SCALE(1, to_feet(dive->meandepth)));
156 sample = dive->sample;
157 cairo_set_source_rgba(cr, 1, 0.2, 0.2, 0.80);
158 begins = sample->time.seconds;
159 cairo_move_to(cr, SCALE(sample->time.seconds, to_feet(sample->depth)));
160 for (i = 1; i < dive->samples; i++) {
162 sec = sample->time.seconds;
163 depth = to_feet(sample->depth);
164 cairo_line_to(cr, SCALE(sec, depth));
167 cairo_line_to(cr, SCALE(sec, 0));
168 cairo_line_to(cr, SCALE(begins, 0));
169 cairo_close_path(cr);
170 cairo_set_source_rgba(cr, 1, 0.2, 0.2, 0.20);
171 cairo_fill_preserve(cr);
172 cairo_set_source_rgba(cr, 1, 0.2, 0.2, 0.80);
178 maxdepth = mindepth = 0;
180 cairo_set_font_size(cr, 14);
181 cairo_set_source_rgb(cr, 1, 0.2, 0.2);
183 while ((i = next_minmax(dive, i, 1)) != 0) {
184 sample = dive->sample+i;
185 sec = sample->time.seconds;
186 depth = to_feet(sample->depth);
187 plot_text(cr, SCALE(sec, depth), "%d ft", depth);
188 i = next_minmax(dive, i, 0);
194 static int get_cylinder_pressure_range(struct dive *dive, double *scalex, double *scaley)
199 *scalex = round_seconds_up(dive->duration.seconds);
203 for (i = 0; i < dive->samples; i++) {
204 struct sample *sample = dive->sample + i;
207 /* FIXME! We only track cylinder 0 right now */
208 if (sample->cylinderindex)
210 if (!sample->cylinderpressure.mbar)
212 bar = sample->cylinderpressure.mbar;
224 static void plot_cylinder_pressure(struct dive *dive, cairo_t *cr,
225 double topx, double topy, double maxx, double maxy)
228 double scalex, scaley;
230 if (!get_cylinder_pressure_range(dive, &scalex, &scaley))
233 cairo_set_source_rgba(cr, 0.2, 1.0, 0.2, 0.80);
235 cairo_move_to(cr, SCALE(0, dive->cylinder[0].start.mbar));
236 for (i = 1; i < dive->samples; i++) {
238 struct sample *sample = dive->sample + i;
240 mbar = sample->cylinderpressure.mbar;
243 sec = sample->time.seconds;
244 cairo_line_to(cr, SCALE(sec, mbar));
247 * We may have "surface time" events, in which case we don't go
248 * back to dive duration
250 if (sec < dive->duration.seconds)
251 cairo_line_to(cr, SCALE(dive->duration.seconds, dive->cylinder[0].end.mbar));
255 static void plot(cairo_t *cr, int w, int h, struct dive *dive)
257 double topx, topy, maxx, maxy;
258 double scalex, scaley;
266 plot_profile(dive, cr, topx, topy, maxx, maxy);
268 /* Cylinder pressure plot? */
269 plot_cylinder_pressure(dive, cr, topx, topy, maxx, maxy);
271 /* Bounding box last */
272 scalex = scaley = 1.0;
273 cairo_set_source_rgb(cr, 1, 1, 1);
274 cairo_move_to(cr, SCALE(0,0));
275 cairo_line_to(cr, SCALE(0,1));
276 cairo_line_to(cr, SCALE(1,1));
277 cairo_line_to(cr, SCALE(1,0));
278 cairo_close_path(cr);
283 static gboolean expose_event(GtkWidget *widget, GdkEventExpose *event, gpointer data)
285 struct dive *dive = current_dive;
289 w = widget->allocation.width;
290 h = widget->allocation.height;
292 cr = gdk_cairo_create(widget->window);
293 cairo_set_source_rgb(cr, 0, 0, 0);
297 plot(cr, w, h, dive);
304 GtkWidget *dive_profile_widget(void)
308 da = gtk_drawing_area_new();
309 gtk_widget_set_size_request(da, 450, 350);
310 g_signal_connect(da, "expose_event", G_CALLBACK(expose_event), NULL);