summaryrefslogtreecommitdiffstats
path: root/profile.c
blob: 9d84ff29d64de926983555be28660abcef2786dc (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

#include "dive.h"
#include "display.h"

int selected_dive = 0;

#define ROUND_UP(x,y) ((((x)+(y)-1)/(y))*(y))

/*
 * When showing dive profiles, we scale things to the
 * current dive. However, we don't scale past less than
 * 30 minutes or 90 ft, just so that small dives show
 * up as such.
 */
static int round_seconds_up(int seconds)
{
	return MAX(30*60, ROUND_UP(seconds, 60*10));
}

static int round_feet_up(int feet)
{
	return MAX(90, ROUND_UP(feet+5, 15));
}

/* Scale to 0,0 -> maxx,maxy */
#define SCALE(x,y) (x)*maxx/scalex+topx,(y)*maxy/scaley+topy

static void plot(cairo_t *cr, int w, int h, struct dive *dive, int samples, struct sample *sample)
{
	int i;
	double topx, topy, maxx, maxy;
	double scalex, scaley;
	int maxtime, maxdepth;
	int begins, sec, depth;

	topx = w / 20.0;
	topy = h / 20.0;
	maxx = (w - 2*topx);
	maxy = (h - 2*topy);

	cairo_set_line_width(cr, 2);

	/* Get plot scaling limits */
	maxtime = round_seconds_up(dive->duration.seconds);
	maxdepth = round_feet_up(to_feet(dive->maxdepth));

	/* Depth markers: every 15 ft */
	scalex = 1.0;
	scaley = maxdepth;
	cairo_set_source_rgba(cr, 1, 1, 1, 0.5);
	for (i = 15; i < maxdepth; i += 15) {
		cairo_move_to(cr, SCALE(0, i));
		cairo_line_to(cr, SCALE(1, i));
	}

	/* Time markers: every 5 min */
	scalex = maxtime;
	scaley = 1.0;
	for (i = 5*60; i < maxtime; i += 5*60) {
		cairo_move_to(cr, SCALE(i, 0));
		cairo_line_to(cr, SCALE(i, 1));
	}
	cairo_stroke(cr);

	scaley = maxdepth;

	/* Depth profile */
	cairo_set_source_rgba(cr, 1, 0.2, 0.2, 0.80);
	begins = sample->time.seconds;
	cairo_move_to(cr, SCALE(sample->time.seconds, to_feet(sample->depth)));
	for (i = 1; i < dive->samples; i++) {
		sample++;
		sec = sample->time.seconds;
		depth = to_feet(sample->depth);
		cairo_line_to(cr, SCALE(sec, depth));
	}
	scaley = 1.0;
	cairo_line_to(cr, SCALE(sec, 0));
	cairo_line_to(cr, SCALE(begins, 0));
	cairo_close_path(cr);
	cairo_set_source_rgba(cr, 1, 0.2, 0.2, 0.20);
	cairo_fill_preserve(cr);
	cairo_set_source_rgba(cr, 1, 0.2, 0.2, 0.80);
	cairo_stroke(cr);

	/* Bounding box last */
	scalex = scaley = 1.0;
	cairo_set_source_rgb(cr, 1, 1, 1);
	cairo_move_to(cr, SCALE(0,0));
	cairo_line_to(cr, SCALE(0,1));
	cairo_line_to(cr, SCALE(1,1));
	cairo_line_to(cr, SCALE(1,0));
	cairo_close_path(cr);
	cairo_stroke(cr);

}

static gboolean expose_event(GtkWidget *widget, GdkEventExpose *event, gpointer data)
{
	struct dive *dive = current_dive;
	cairo_t *cr;
	int w,h;

	w = widget->allocation.width;
	h = widget->allocation.height;

	cr = gdk_cairo_create(widget->window);
	cairo_set_source_rgb(cr, 0, 0, 0);
	cairo_paint(cr);

	if (dive && dive->samples)
		plot(cr, w, h, dive, dive->samples, dive->sample);

	cairo_destroy(cr);

	return FALSE;
}

GtkWidget *dive_profile_frame(void)
{
	GtkWidget *frame;
	GtkWidget *da;

	frame = gtk_frame_new("Dive profile");
	gtk_widget_show(frame);
	da = gtk_drawing_area_new();
	gtk_widget_set_size_request(da, 450, 350);
	g_signal_connect(da, "expose_event", G_CALLBACK(expose_event), NULL);
	gtk_container_add(GTK_CONTAINER(frame), da);

	return frame;
}