]> git.tdb.fi Git - ext/subsurface.git/blob - profile.c
Fix drawing artifact with UEMIS xml data
[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 #include "divelist.h"
8
9 int selected_dive = 0;
10
11 #define ROUND_UP(x,y) ((((x)+(y)-1)/(y))*(y))
12
13 /*
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
17  * up as such.
18  */
19 static int round_seconds_up(int seconds)
20 {
21         return MAX(30*60, ROUND_UP(seconds, 60*10));
22 }
23
24 static int round_feet_up(int feet)
25 {
26         return MAX(90, ROUND_UP(feet+5, 15));
27 }
28
29 /* Scale to 0,0 -> maxx,maxy */
30 #define SCALE(x,y) (x)*maxx/scalex+topx,(y)*maxy/scaley+topy
31
32 static void plot_profile(struct dive *dive, cairo_t *cr,
33         double topx, double topy, double maxx, double maxy)
34 {
35         double scalex, scaley;
36         int begins, sec, depth;
37         int i, samples;
38         struct sample *sample;
39         int maxtime, maxdepth;
40
41         samples = dive->samples;
42         if (!samples)
43                 return;
44
45         cairo_set_line_width(cr, 2);
46
47         /* Get plot scaling limits */
48         maxtime = round_seconds_up(dive->duration.seconds);
49         maxdepth = round_feet_up(to_feet(dive->maxdepth));
50
51         /* Time markers: every 5 min */
52         scalex = maxtime;
53         scaley = 1.0;
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));
57         }
58
59         /* Depth markers: every 15 ft */
60         scalex = 1.0;
61         scaley = maxdepth;
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));
66         }
67         cairo_stroke(cr);
68
69         /* Show mean depth */
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)));
73         cairo_stroke(cr);
74
75         scalex = maxtime;
76
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++) {
82                 sample++;
83                 sec = sample->time.seconds;
84                 depth = to_feet(sample->depth);
85                 cairo_line_to(cr, SCALE(sec, depth));
86         }
87         scaley = 1.0;
88         cairo_line_to(cr, SCALE(sec, 0));
89         cairo_line_to(cr, SCALE(begins, 0));
90         cairo_close_path(cr);
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);
94         cairo_stroke(cr);
95 }
96
97 static int get_cylinder_pressure_range(struct dive *dive, double *scalex, double *scaley)
98 {
99         int i;
100         double min, max;
101
102         *scalex = round_seconds_up(dive->duration.seconds);
103
104         max = 0;
105         min = 5000;
106         for (i = 0; i < dive->samples; i++) {
107                 struct sample *sample = dive->sample + i;
108                 double bar;
109
110                 /* FIXME! We only track cylinder 0 right now */
111                 if (sample->cylinderindex)
112                         continue;
113                 if (!sample->cylinderpressure.mbar)
114                         continue;
115                 bar = sample->cylinderpressure.mbar;
116                 if (bar < min)
117                         min = bar;
118                 if (bar > max)
119                         max = bar;
120         }
121         if (!max)
122                 return 0;
123         *scaley = max * 1.5;
124         return 1;
125 }
126
127 static void plot_cylinder_pressure(struct dive *dive, cairo_t *cr,
128         double topx, double topy, double maxx, double maxy)
129 {
130         int i, sec = -1;
131         double scalex, scaley;
132
133         if (!get_cylinder_pressure_range(dive, &scalex, &scaley))
134                 return;
135
136         cairo_set_source_rgba(cr, 0.2, 1.0, 0.2, 0.80);
137
138         cairo_move_to(cr, SCALE(0, dive->cylinder[0].start.mbar));
139         for (i = 1; i < dive->samples; i++) {
140                 int mbar;
141                 struct sample *sample = dive->sample + i;
142
143                 mbar = sample->cylinderpressure.mbar;
144                 if (!mbar)
145                         continue;
146                 sec = sample->time.seconds;
147                 cairo_line_to(cr, SCALE(sec, mbar));
148         }
149         /*
150          * We may have "surface time" events, in which case we don't go
151          * back to dive duration
152          */
153         if (sec < dive->duration.seconds)
154                 cairo_line_to(cr, SCALE(dive->duration.seconds, dive->cylinder[0].end.mbar));
155         cairo_stroke(cr);
156 }
157
158 static void plot(cairo_t *cr, int w, int h, struct dive *dive)
159 {
160         double topx, topy, maxx, maxy;
161         double scalex, scaley;
162
163         topx = w / 20.0;
164         topy = h / 20.0;
165         maxx = (w - 2*topx);
166         maxy = (h - 2*topy);
167
168         /* Depth profile */
169         plot_profile(dive, cr, topx, topy, maxx, maxy);
170
171         /* Cylinder pressure plot? */
172         plot_cylinder_pressure(dive, cr, topx, topy, maxx, maxy);
173
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);
182         cairo_stroke(cr);
183
184 }
185
186 static gboolean expose_event(GtkWidget *widget, GdkEventExpose *event, gpointer data)
187 {
188         struct dive *dive = current_dive;
189         cairo_t *cr;
190         int w,h;
191
192         w = widget->allocation.width;
193         h = widget->allocation.height;
194
195         cr = gdk_cairo_create(widget->window);
196         cairo_set_source_rgb(cr, 0, 0, 0);
197         cairo_paint(cr);
198
199         if (dive)
200                 plot(cr, w, h, dive);
201
202         cairo_destroy(cr);
203
204         return FALSE;
205 }
206
207 GtkWidget *dive_profile_widget(void)
208 {
209         GtkWidget *da;
210
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);
214
215         return da;
216 }