11 #define ROUND_UP(x,y) ((((x)+(y)-1)/(y))*(y))
14 * When showing dive profiles, we scale things to the
15 * current dive. However, we don't scale past less than
16 * 30 minutes or 90 ft, just so that small dives show
19 static int round_seconds_up(int seconds)
21 return MAX(30*60, ROUND_UP(seconds, 60*10));
24 static int round_feet_up(int feet)
26 return MAX(90, ROUND_UP(feet+5, 15));
29 /* Scale to 0,0 -> maxx,maxy */
30 #define SCALE(x,y) (x)*maxx/scalex+topx,(y)*maxy/scaley+topy
32 static void plot_profile(struct dive *dive, cairo_t *cr,
33 double topx, double topy, double maxx, double maxy)
35 double scalex, scaley;
36 int begins, sec, depth;
38 struct sample *sample;
39 int maxtime, maxdepth;
41 samples = dive->samples;
45 cairo_set_line_width(cr, 2);
47 /* Get plot scaling limits */
48 maxtime = round_seconds_up(dive->duration.seconds);
49 maxdepth = round_feet_up(to_feet(dive->maxdepth));
51 /* Time markers: every 5 min */
54 for (i = 5*60; i < maxtime; i += 5*60) {
55 cairo_move_to(cr, SCALE(i, 0));
56 cairo_line_to(cr, SCALE(i, 1));
59 /* Depth markers: every 15 ft */
62 cairo_set_source_rgba(cr, 1, 1, 1, 0.5);
63 for (i = 15; i < maxdepth; i += 15) {
64 cairo_move_to(cr, SCALE(0, i));
65 cairo_line_to(cr, SCALE(1, i));
70 cairo_set_source_rgba(cr, 1, 0.2, 0.2, 0.40);
71 cairo_move_to(cr, SCALE(0, to_feet(dive->meandepth)));
72 cairo_line_to(cr, SCALE(1, to_feet(dive->meandepth)));
77 sample = dive->sample;
78 cairo_set_source_rgba(cr, 1, 0.2, 0.2, 0.80);
79 begins = sample->time.seconds;
80 cairo_move_to(cr, SCALE(sample->time.seconds, to_feet(sample->depth)));
81 for (i = 1; i < dive->samples; i++) {
83 sec = sample->time.seconds;
84 depth = to_feet(sample->depth);
85 cairo_line_to(cr, SCALE(sec, depth));
88 cairo_line_to(cr, SCALE(sec, 0));
89 cairo_line_to(cr, SCALE(begins, 0));
91 cairo_set_source_rgba(cr, 1, 0.2, 0.2, 0.20);
92 cairo_fill_preserve(cr);
93 cairo_set_source_rgba(cr, 1, 0.2, 0.2, 0.80);
97 static int get_cylinder_pressure_range(struct dive *dive, double *scalex, double *scaley)
102 *scalex = round_seconds_up(dive->duration.seconds);
106 for (i = 0; i < dive->samples; i++) {
107 struct sample *sample = dive->sample + i;
110 /* FIXME! We only track cylinder 0 right now */
111 if (sample->cylinderindex)
113 if (!sample->cylinderpressure.mbar)
115 bar = sample->cylinderpressure.mbar;
127 static void plot_cylinder_pressure(struct dive *dive, cairo_t *cr,
128 double topx, double topy, double maxx, double maxy)
131 double scalex, scaley;
133 if (!get_cylinder_pressure_range(dive, &scalex, &scaley))
136 cairo_set_source_rgba(cr, 0.2, 1.0, 0.2, 0.80);
138 cairo_move_to(cr, SCALE(0, dive->cylinder[0].start.mbar));
139 for (i = 1; i < dive->samples; i++) {
141 struct sample *sample = dive->sample + i;
143 mbar = sample->cylinderpressure.mbar;
146 sec = sample->time.seconds;
147 cairo_line_to(cr, SCALE(sec, mbar));
150 * We may have "surface time" events, in which case we don't go
151 * back to dive duration
153 if (sec < dive->duration.seconds)
154 cairo_line_to(cr, SCALE(dive->duration.seconds, dive->cylinder[0].end.mbar));
158 static void plot(cairo_t *cr, int w, int h, struct dive *dive)
160 double topx, topy, maxx, maxy;
161 double scalex, scaley;
169 plot_profile(dive, cr, topx, topy, maxx, maxy);
171 /* Cylinder pressure plot? */
172 plot_cylinder_pressure(dive, cr, topx, topy, maxx, maxy);
174 /* Bounding box last */
175 scalex = scaley = 1.0;
176 cairo_set_source_rgb(cr, 1, 1, 1);
177 cairo_move_to(cr, SCALE(0,0));
178 cairo_line_to(cr, SCALE(0,1));
179 cairo_line_to(cr, SCALE(1,1));
180 cairo_line_to(cr, SCALE(1,0));
181 cairo_close_path(cr);
186 static gboolean expose_event(GtkWidget *widget, GdkEventExpose *event, gpointer data)
188 struct dive *dive = current_dive;
192 w = widget->allocation.width;
193 h = widget->allocation.height;
195 cr = gdk_cairo_create(widget->window);
196 cairo_set_source_rgb(cr, 0, 0, 0);
200 plot(cr, w, h, dive);
207 GtkWidget *dive_profile_widget(void)
211 da = gtk_drawing_area_new();
212 gtk_widget_set_size_request(da, 450, 350);
213 g_signal_connect(da, "expose_event", G_CALLBACK(expose_event), NULL);