+typedef enum { STABLE, SLOW, MODERATE, FAST, CRAZY } velocity_t;
+/* Plot info with smoothing, velocity indication
+ * and one-, two- and three-minute minimums and maximums */
+struct plot_info {
+ int nr;
+ int maxtime;
+ int meandepth, maxdepth;
+ int minpressure, maxpressure;
+ int mintemp, maxtemp;
+ struct plot_data {
+ int sec;
+ int pressure, temperature;
+ /* Depth info */
+ int val;
+ int smoothed;
+ velocity_t velocity;
+ struct plot_data *min[3];
+ struct plot_data *max[3];
+ int avg[3];
+ } entry[];
+};
+
+/* convert velocity to colors */
+typedef struct { double r, g, b; } rgb_t;
+static const rgb_t rgb[] = {
+ [STABLE] = {0.0, 0.4, 0.0},
+ [SLOW] = {0.4, 0.8, 0.0},
+ [MODERATE] = {0.8, 0.8, 0.0},
+ [FAST] = {0.8, 0.5, 0.0},
+ [CRAZY] = {1.0, 0.0, 0.0},
+};
+
+#define plot_info_size(nr) (sizeof(struct plot_info) + (nr)*sizeof(struct plot_data))
+
+/* Scale to 0,0 -> maxx,maxy */
+#define SCALEX(gc,x) (((x)-gc->leftx)/(gc->rightx-gc->leftx)*gc->maxx)
+#define SCALEY(gc,y) (((y)-gc->topy)/(gc->bottomy-gc->topy)*gc->maxy)
+#define SCALE(gc,x,y) SCALEX(gc,x),SCALEY(gc,y)
+
+static void move_to(struct graphics_context *gc, double x, double y)
+{
+ cairo_move_to(gc->cr, SCALE(gc, x, y));
+}
+
+static void line_to(struct graphics_context *gc, double x, double y)
+{
+ cairo_line_to(gc->cr, SCALE(gc, x, y));
+}
+
+static void set_source_rgba(struct graphics_context *gc, double r, double g, double b, double a)
+{
+ /*
+ * For printers, we still honor 'a', but ignore colors
+ * for now. Black is white and white is black
+ */
+ if (gc->printer) {
+ double sum = r+g+b;
+ if (sum > 0.8)
+ r = g = b = 0;
+ else
+ r = g = b = 1;
+ }
+ cairo_set_source_rgba(gc->cr, r, g, b, a);
+}
+
+void set_source_rgb(struct graphics_context *gc, double r, double g, double b)
+{
+ set_source_rgba(gc, r, g, b, 1);
+}
+
+#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.
+ * we also need to add 180 seconds at the end so the min/max
+ * plots correctly
+ */
+static int get_maxtime(struct plot_info *pi)
+{
+ int seconds = pi->maxtime;
+ /* min 30 minutes, rounded up to 5 minutes, with at least 2.5 minutes to spare */
+ return MAX(30*60, ROUND_UP(seconds+150, 60*5));
+}
+
+static int get_maxdepth(struct plot_info *pi)
+{
+ unsigned mm = pi->maxdepth;
+ /* Minimum 30m, rounded up to 10m, with at least 3m to spare */
+ return MAX(30000, ROUND_UP(mm+3000, 10000));
+}
+
+typedef struct {
+ int size;
+ double r,g,b;
+ double hpos, vpos;
+} text_render_options_t;
+
+#define RIGHT (-1.0)
+#define CENTER (-0.5)
+#define LEFT (0.0)
+
+#define TOP (1)
+#define MIDDLE (0)
+#define BOTTOM (-1)
+
+static void plot_text(struct graphics_context *gc, const text_render_options_t *tro,
+ double x, double y, const char *fmt, ...)
+{
+ cairo_t *cr = gc->cr;
+ cairo_font_extents_t fe;
+ cairo_text_extents_t extents;
+ double dx, dy;
+ char buffer[80];
+ va_list args;
+
+ va_start(args, fmt);
+ vsnprintf(buffer, sizeof(buffer), fmt, args);
+ va_end(args);
+
+ cairo_set_font_size(cr, tro->size);
+ cairo_font_extents(cr, &fe);
+ cairo_text_extents(cr, buffer, &extents);
+ dx = tro->hpos * extents.width + extents.x_bearing;
+ dy = tro->vpos * extents.height + fe.descent;
+
+ move_to(gc, x, y);
+ cairo_rel_move_to(cr, dx, dy);
+
+ cairo_text_path(cr, buffer);
+ set_source_rgb(gc, 0, 0, 0);
+ cairo_stroke(cr);
+
+ move_to(gc, x, y);
+ cairo_rel_move_to(cr, dx, dy);
+
+ set_source_rgb(gc, tro->r, tro->g, tro->b);
+ cairo_show_text(cr, buffer);
+}
+
+static void plot_one_event(struct graphics_context *gc, struct plot_info *pi, struct event *event, const text_render_options_t *tro)
+{
+ int i, depth = 0;
+
+ for (i = 0; i < pi->nr; i++) {
+ struct plot_data *data = pi->entry + i;
+ if (event->time.seconds < data->sec)
+ break;
+ depth = data->val;
+ }
+ plot_text(gc, tro, event->time.seconds, depth, "%s", event->name);
+}
+
+static void plot_events(struct graphics_context *gc, struct plot_info *pi, struct dive *dive)
+{
+ static const text_render_options_t tro = {14, 1.0, 0.2, 0.2, CENTER, TOP};
+ struct event *event = dive->events;
+
+ if (gc->printer)
+ return;
+
+ while (event) {
+ plot_one_event(gc, pi, event, &tro);
+ event = event->next;
+ }
+}
+
+static void render_depth_sample(struct graphics_context *gc, struct plot_data *entry, const text_render_options_t *tro)
+{
+ int sec = entry->sec, decimals;
+ double d;
+
+ d = get_depth_units(entry->val, &decimals, NULL);
+
+ plot_text(gc, tro, sec, entry->val, "%.*f", decimals, d);
+}
+
+static void plot_text_samples(struct graphics_context *gc, struct plot_info *pi)