]> git.tdb.fi Git - ext/subsurface.git/blob - profile.c
Add various dive fixups, and show pressure (if any) in the plot
[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
8 int selected_dive = 0;
9
10 #define ROUND_UP(x,y) ((((x)+(y)-1)/(y))*(y))
11
12 /*
13  * When showing dive profiles, we scale things to the
14  * current dive. However, we don't scale past less than
15  * 30 minutes or 90 ft, just so that small dives show
16  * up as such.
17  */
18 static int round_seconds_up(int seconds)
19 {
20         return MAX(30*60, ROUND_UP(seconds, 60*10));
21 }
22
23 static int round_feet_up(int feet)
24 {
25         return MAX(90, ROUND_UP(feet+5, 15));
26 }
27
28 /* Scale to 0,0 -> maxx,maxy */
29 #define SCALE(x,y) (x)*maxx/scalex+topx,(y)*maxy/scaley+topy
30
31 static void plot_profile(struct dive *dive, cairo_t *cr,
32         double topx, double topy, double maxx, double maxy)
33 {
34         double scalex, scaley;
35         int begins, sec, depth;
36         int i, samples;
37         struct sample *sample;
38         int maxtime, maxdepth;
39
40         samples = dive->samples;
41         if (!samples)
42                 return;
43
44         cairo_set_line_width(cr, 2);
45
46         /* Get plot scaling limits */
47         maxtime = round_seconds_up(dive->duration.seconds);
48         maxdepth = round_feet_up(to_feet(dive->maxdepth));
49
50         /* Depth markers: every 15 ft */
51         scalex = 1.0;
52         scaley = maxdepth;
53         cairo_set_source_rgba(cr, 1, 1, 1, 0.5);
54         for (i = 15; i < maxdepth; i += 15) {
55                 cairo_move_to(cr, SCALE(0, i));
56                 cairo_line_to(cr, SCALE(1, i));
57         }
58
59         /* Time markers: every 5 min */
60         scalex = maxtime;
61         scaley = 1.0;
62         for (i = 5*60; i < maxtime; i += 5*60) {
63                 cairo_move_to(cr, SCALE(i, 0));
64                 cairo_line_to(cr, SCALE(i, 1));
65         }
66         cairo_stroke(cr);
67
68         scaley = maxdepth;
69
70         sample = dive->sample;
71         cairo_set_source_rgba(cr, 1, 0.2, 0.2, 0.80);
72         begins = sample->time.seconds;
73         cairo_move_to(cr, SCALE(sample->time.seconds, to_feet(sample->depth)));
74         for (i = 1; i < dive->samples; i++) {
75                 sample++;
76                 sec = sample->time.seconds;
77                 depth = to_feet(sample->depth);
78                 cairo_line_to(cr, SCALE(sec, depth));
79         }
80         scaley = 1.0;
81         cairo_line_to(cr, SCALE(sec, 0));
82         cairo_line_to(cr, SCALE(begins, 0));
83         cairo_close_path(cr);
84         cairo_set_source_rgba(cr, 1, 0.2, 0.2, 0.20);
85         cairo_fill_preserve(cr);
86         cairo_set_source_rgba(cr, 1, 0.2, 0.2, 0.80);
87         cairo_stroke(cr);
88 }
89
90 static int get_tank_pressure_range(struct dive *dive, double *scalex, double *scaley)
91 {
92         int i;
93         double min, max;
94
95         *scalex = round_seconds_up(dive->duration.seconds);
96
97         max = 0;
98         min = 5000;
99         for (i = 0; i < dive->samples; i++) {
100                 struct sample *sample = dive->sample + i;
101                 double bar;
102
103                 if (!sample->tankpressure.mbar)
104                         continue;
105                 bar = sample->tankpressure.mbar;
106                 if (bar < min)
107                         min = bar;
108                 if (bar > max)
109                         max = bar;
110         }
111         if (!max)
112                 return 0;
113         *scaley = max * 1.5;
114         return 1;
115 }
116
117 static void plot_tank_pressure(struct dive *dive, cairo_t *cr,
118         double topx, double topy, double maxx, double maxy)
119 {
120         int i;
121         double scalex, scaley;
122
123         if (!get_tank_pressure_range(dive, &scalex, &scaley))
124                 return;
125
126         cairo_set_source_rgba(cr, 0.2, 1.0, 0.2, 0.80);
127
128         cairo_move_to(cr, SCALE(0, dive->beginning_pressure.mbar));
129         for (i = 1; i < dive->samples; i++) {
130                 int sec, mbar;
131                 struct sample *sample = dive->sample + i;
132
133                 sec = sample->time.seconds;
134                 mbar = sample->tankpressure.mbar;
135                 if (!mbar)
136                         continue;
137                 cairo_line_to(cr, SCALE(sec, mbar));
138         }
139         cairo_line_to(cr, SCALE(dive->duration.seconds, dive->end_pressure.mbar));
140         cairo_stroke(cr);
141 }
142
143 static void plot(cairo_t *cr, int w, int h, struct dive *dive)
144 {
145         double topx, topy, maxx, maxy;
146         double scalex, scaley;
147
148         topx = w / 20.0;
149         topy = h / 20.0;
150         maxx = (w - 2*topx);
151         maxy = (h - 2*topy);
152
153         /* Depth profile */
154         plot_profile(dive, cr, topx, topy, maxx, maxy);
155
156         /* Tank pressure plot? */
157         plot_tank_pressure(dive, cr, topx, topy, maxx, maxy);
158
159         /* Bounding box last */
160         scalex = scaley = 1.0;
161         cairo_set_source_rgb(cr, 1, 1, 1);
162         cairo_move_to(cr, SCALE(0,0));
163         cairo_line_to(cr, SCALE(0,1));
164         cairo_line_to(cr, SCALE(1,1));
165         cairo_line_to(cr, SCALE(1,0));
166         cairo_close_path(cr);
167         cairo_stroke(cr);
168
169 }
170
171 static gboolean expose_event(GtkWidget *widget, GdkEventExpose *event, gpointer data)
172 {
173         struct dive *dive = current_dive;
174         cairo_t *cr;
175         int w,h;
176
177         w = widget->allocation.width;
178         h = widget->allocation.height;
179
180         cr = gdk_cairo_create(widget->window);
181         cairo_set_source_rgb(cr, 0, 0, 0);
182         cairo_paint(cr);
183
184         if (dive)
185                 plot(cr, w, h, dive);
186
187         cairo_destroy(cr);
188
189         return FALSE;
190 }
191
192 GtkWidget *dive_profile_frame(void)
193 {
194         GtkWidget *frame;
195         GtkWidget *da;
196
197         frame = gtk_frame_new("Dive profile");
198         gtk_widget_show(frame);
199         da = gtk_drawing_area_new();
200         gtk_widget_set_size_request(da, 450, 350);
201         g_signal_connect(da, "expose_event", G_CALLBACK(expose_event), NULL);
202         gtk_container_add(GTK_CONTAINER(frame), da);
203
204         return frame;
205 }