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_depth_text(struct dive *dive, cairo_t *cr,
112 double topx, double topy, double maxx, double maxy)
114 double scalex, scaley;
115 int maxtime, maxdepth;
118 /* Get plot scaling limits */
119 maxtime = round_seconds_up(dive->duration.seconds);
120 maxdepth = round_feet_up(to_feet(dive->maxdepth));
125 cairo_set_font_size(cr, 14);
126 cairo_set_source_rgb(cr, 1, 0.2, 0.2);
128 while ((i = next_minmax(dive, i, 1)) != 0) {
129 struct sample *sample = dive->sample+i;
130 int sec = sample->time.seconds;
131 int depth = to_feet(sample->depth);
133 plot_text(cr, SCALE(sec, depth), "%d ft", depth);
134 i = next_minmax(dive, i, 0);
140 static void plot_depth_profile(struct dive *dive, cairo_t *cr,
141 double topx, double topy, double maxx, double maxy)
143 double scalex, scaley;
144 int begins, sec, depth;
146 struct sample *sample;
147 int maxtime, maxdepth;
149 samples = dive->samples;
153 cairo_set_line_width(cr, 2);
155 /* Get plot scaling limits */
156 maxtime = round_seconds_up(dive->duration.seconds);
157 maxdepth = round_feet_up(to_feet(dive->maxdepth));
159 /* Time markers: every 5 min */
162 for (i = 5*60; i < maxtime; i += 5*60) {
163 cairo_move_to(cr, SCALE(i, 0));
164 cairo_line_to(cr, SCALE(i, 1));
167 /* Depth markers: every 15 ft */
170 cairo_set_source_rgba(cr, 1, 1, 1, 0.5);
171 for (i = 15; i < maxdepth; i += 15) {
172 cairo_move_to(cr, SCALE(0, i));
173 cairo_line_to(cr, SCALE(1, i));
177 /* Show mean depth */
178 cairo_set_source_rgba(cr, 1, 0.2, 0.2, 0.40);
179 cairo_move_to(cr, SCALE(0, to_feet(dive->meandepth)));
180 cairo_line_to(cr, SCALE(1, to_feet(dive->meandepth)));
185 sample = dive->sample;
186 cairo_set_source_rgba(cr, 1, 0.2, 0.2, 0.80);
187 begins = sample->time.seconds;
188 cairo_move_to(cr, SCALE(sample->time.seconds, to_feet(sample->depth)));
189 for (i = 1; i < dive->samples; i++) {
191 sec = sample->time.seconds;
192 depth = to_feet(sample->depth);
193 cairo_line_to(cr, SCALE(sec, depth));
196 cairo_line_to(cr, SCALE(sec, 0));
197 cairo_line_to(cr, SCALE(begins, 0));
198 cairo_close_path(cr);
199 cairo_set_source_rgba(cr, 1, 0.2, 0.2, 0.20);
200 cairo_fill_preserve(cr);
201 cairo_set_source_rgba(cr, 1, 0.2, 0.2, 0.80);
205 static int get_cylinder_pressure_range(struct dive *dive, double *scalex, double *scaley)
210 *scalex = round_seconds_up(dive->duration.seconds);
214 for (i = 0; i < dive->samples; i++) {
215 struct sample *sample = dive->sample + i;
218 /* FIXME! We only track cylinder 0 right now */
219 if (sample->cylinderindex)
221 if (!sample->cylinderpressure.mbar)
223 bar = sample->cylinderpressure.mbar;
235 static void plot_cylinder_pressure(struct dive *dive, cairo_t *cr,
236 double topx, double topy, double maxx, double maxy)
239 double scalex, scaley;
241 if (!get_cylinder_pressure_range(dive, &scalex, &scaley))
244 cairo_set_source_rgba(cr, 0.2, 1.0, 0.2, 0.80);
246 cairo_move_to(cr, SCALE(0, dive->cylinder[0].start.mbar));
247 for (i = 1; i < dive->samples; i++) {
249 struct sample *sample = dive->sample + i;
251 mbar = sample->cylinderpressure.mbar;
254 sec = sample->time.seconds;
255 cairo_line_to(cr, SCALE(sec, mbar));
258 * We may have "surface time" events, in which case we don't go
259 * back to dive duration
261 if (sec < dive->duration.seconds)
262 cairo_line_to(cr, SCALE(dive->duration.seconds, dive->cylinder[0].end.mbar));
266 static void plot(cairo_t *cr, int w, int h, struct dive *dive)
268 double topx, topy, maxx, maxy;
269 double scalex, scaley;
276 /* Cylinder pressure plot */
277 plot_cylinder_pressure(dive, cr, topx, topy, maxx, maxy);
280 plot_depth_profile(dive, cr, topx, topy, maxx, maxy);
282 /* Text on top of all graphs.. */
283 plot_depth_text(dive, cr, topx, topy, maxx, maxy);
285 /* Bounding box last */
286 scalex = scaley = 1.0;
287 cairo_set_source_rgb(cr, 1, 1, 1);
288 cairo_move_to(cr, SCALE(0,0));
289 cairo_line_to(cr, SCALE(0,1));
290 cairo_line_to(cr, SCALE(1,1));
291 cairo_line_to(cr, SCALE(1,0));
292 cairo_close_path(cr);
297 static gboolean expose_event(GtkWidget *widget, GdkEventExpose *event, gpointer data)
299 struct dive *dive = current_dive;
303 w = widget->allocation.width;
304 h = widget->allocation.height;
306 cr = gdk_cairo_create(widget->window);
307 cairo_set_source_rgb(cr, 0, 0, 0);
311 plot(cr, w, h, dive);
318 GtkWidget *dive_profile_widget(void)
322 da = gtk_drawing_area_new();
323 gtk_widget_set_size_request(da, 450, 350);
324 g_signal_connect(da, "expose_event", G_CALLBACK(expose_event), NULL);