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);
55 /* Find the next maximum point in a 5-minute window */
56 static int next_minmax(struct dive *dive, int index, int max)
58 int timelimit, depthlimit, result;
59 struct sample *sample = dive->sample + index;
61 if (index >= dive->samples)
65 depthlimit = sample->depth.mm;
73 if (index >= dive->samples)
75 time = sample->time.seconds;
76 depth = sample->depth.mm;
80 if (depth <= depthlimit)
83 if (depth >= depthlimit)
88 timelimit = time + 300;
94 /* Scale to 0,0 -> maxx,maxy */
95 #define SCALE(x,y) (x)*maxx/scalex+topx,(y)*maxy/scaley+topy
97 static void plot_profile(struct dive *dive, cairo_t *cr,
98 double topx, double topy, double maxx, double maxy)
100 double scalex, scaley;
101 int begins, sec, depth;
103 struct sample *sample;
104 int maxtime, maxdepth, mindepth;
106 samples = dive->samples;
110 cairo_set_line_width(cr, 2);
112 /* Get plot scaling limits */
113 maxtime = round_seconds_up(dive->duration.seconds);
114 maxdepth = round_feet_up(to_feet(dive->maxdepth));
116 /* Time markers: every 5 min */
119 for (i = 5*60; i < maxtime; i += 5*60) {
120 cairo_move_to(cr, SCALE(i, 0));
121 cairo_line_to(cr, SCALE(i, 1));
124 /* Depth markers: every 15 ft */
127 cairo_set_source_rgba(cr, 1, 1, 1, 0.5);
128 for (i = 15; i < maxdepth; i += 15) {
129 cairo_move_to(cr, SCALE(0, i));
130 cairo_line_to(cr, SCALE(1, i));
134 /* Show mean depth */
135 cairo_set_source_rgba(cr, 1, 0.2, 0.2, 0.40);
136 cairo_move_to(cr, SCALE(0, to_feet(dive->meandepth)));
137 cairo_line_to(cr, SCALE(1, to_feet(dive->meandepth)));
142 sample = dive->sample;
143 cairo_set_source_rgba(cr, 1, 0.2, 0.2, 0.80);
144 begins = sample->time.seconds;
145 cairo_move_to(cr, SCALE(sample->time.seconds, to_feet(sample->depth)));
146 for (i = 1; i < dive->samples; i++) {
148 sec = sample->time.seconds;
149 depth = to_feet(sample->depth);
150 cairo_line_to(cr, SCALE(sec, depth));
153 cairo_line_to(cr, SCALE(sec, 0));
154 cairo_line_to(cr, SCALE(begins, 0));
155 cairo_close_path(cr);
156 cairo_set_source_rgba(cr, 1, 0.2, 0.2, 0.20);
157 cairo_fill_preserve(cr);
158 cairo_set_source_rgba(cr, 1, 0.2, 0.2, 0.80);
164 maxdepth = mindepth = 0;
166 cairo_set_font_size(cr, 14);
167 cairo_set_source_rgb(cr, 1, 0.2, 0.2);
169 while ((i = next_minmax(dive, i, 1)) != 0) {
170 sample = dive->sample+i;
171 sec = sample->time.seconds;
172 depth = to_feet(sample->depth);
173 plot_text(cr, SCALE(sec, depth), "%d ft", depth);
174 i = next_minmax(dive, i, 0);
180 static int get_cylinder_pressure_range(struct dive *dive, double *scalex, double *scaley)
185 *scalex = round_seconds_up(dive->duration.seconds);
189 for (i = 0; i < dive->samples; i++) {
190 struct sample *sample = dive->sample + i;
193 /* FIXME! We only track cylinder 0 right now */
194 if (sample->cylinderindex)
196 if (!sample->cylinderpressure.mbar)
198 bar = sample->cylinderpressure.mbar;
210 static void plot_cylinder_pressure(struct dive *dive, cairo_t *cr,
211 double topx, double topy, double maxx, double maxy)
214 double scalex, scaley;
216 if (!get_cylinder_pressure_range(dive, &scalex, &scaley))
219 cairo_set_source_rgba(cr, 0.2, 1.0, 0.2, 0.80);
221 cairo_move_to(cr, SCALE(0, dive->cylinder[0].start.mbar));
222 for (i = 1; i < dive->samples; i++) {
224 struct sample *sample = dive->sample + i;
226 mbar = sample->cylinderpressure.mbar;
229 sec = sample->time.seconds;
230 cairo_line_to(cr, SCALE(sec, mbar));
233 * We may have "surface time" events, in which case we don't go
234 * back to dive duration
236 if (sec < dive->duration.seconds)
237 cairo_line_to(cr, SCALE(dive->duration.seconds, dive->cylinder[0].end.mbar));
241 static void plot(cairo_t *cr, int w, int h, struct dive *dive)
243 double topx, topy, maxx, maxy;
244 double scalex, scaley;
252 plot_profile(dive, cr, topx, topy, maxx, maxy);
254 /* Cylinder pressure plot? */
255 plot_cylinder_pressure(dive, cr, topx, topy, maxx, maxy);
257 /* Bounding box last */
258 scalex = scaley = 1.0;
259 cairo_set_source_rgb(cr, 1, 1, 1);
260 cairo_move_to(cr, SCALE(0,0));
261 cairo_line_to(cr, SCALE(0,1));
262 cairo_line_to(cr, SCALE(1,1));
263 cairo_line_to(cr, SCALE(1,0));
264 cairo_close_path(cr);
269 static gboolean expose_event(GtkWidget *widget, GdkEventExpose *event, gpointer data)
271 struct dive *dive = current_dive;
275 w = widget->allocation.width;
276 h = widget->allocation.height;
278 cr = gdk_cairo_create(widget->window);
279 cairo_set_source_rgb(cr, 0, 0, 0);
283 plot(cr, w, h, dive);
290 GtkWidget *dive_profile_widget(void)
294 da = gtk_drawing_area_new();
295 gtk_widget_set_size_request(da, 450, 350);
296 g_signal_connect(da, "expose_event", G_CALLBACK(expose_event), NULL);