]> git.tdb.fi Git - ext/subsurface.git/blob - profile.c
Open File works. I refactored the code and introduced a new type. I never used it...
[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;
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 sec, mbar;
141                 struct sample *sample = dive->sample + i;
142
143                 sec = sample->time.seconds;
144                 mbar = sample->cylinderpressure.mbar;
145                 if (!mbar)
146                         continue;
147                 cairo_line_to(cr, SCALE(sec, mbar));
148         }
149         cairo_line_to(cr, SCALE(dive->duration.seconds, dive->cylinder[0].end.mbar));
150         cairo_stroke(cr);
151 }
152
153 static void plot(cairo_t *cr, int w, int h, struct dive *dive)
154 {
155         double topx, topy, maxx, maxy;
156         double scalex, scaley;
157
158         topx = w / 20.0;
159         topy = h / 20.0;
160         maxx = (w - 2*topx);
161         maxy = (h - 2*topy);
162
163         /* Depth profile */
164         plot_profile(dive, cr, topx, topy, maxx, maxy);
165
166         /* Cylinder pressure plot? */
167         plot_cylinder_pressure(dive, cr, topx, topy, maxx, maxy);
168
169         /* Bounding box last */
170         scalex = scaley = 1.0;
171         cairo_set_source_rgb(cr, 1, 1, 1);
172         cairo_move_to(cr, SCALE(0,0));
173         cairo_line_to(cr, SCALE(0,1));
174         cairo_line_to(cr, SCALE(1,1));
175         cairo_line_to(cr, SCALE(1,0));
176         cairo_close_path(cr);
177         cairo_stroke(cr);
178
179 }
180
181 static gboolean expose_event(GtkWidget *widget, GdkEventExpose *event, gpointer data)
182 {
183         struct dive *dive = current_dive;
184         cairo_t *cr;
185         int w,h;
186
187         w = widget->allocation.width;
188         h = widget->allocation.height;
189
190         cr = gdk_cairo_create(widget->window);
191         cairo_set_source_rgb(cr, 0, 0, 0);
192         cairo_paint(cr);
193
194         if (dive)
195                 plot(cr, w, h, dive);
196
197         cairo_destroy(cr);
198
199         return FALSE;
200 }
201
202 GtkWidget *dive_profile_widget(void)
203 {
204         GtkWidget *da;
205
206         da = gtk_drawing_area_new();
207         gtk_widget_set_size_request(da, 450, 350);
208         g_signal_connect(da, "expose_event", G_CALLBACK(expose_event), NULL);
209
210         return da;
211 }