+/* profile.c */
+/* creates all the necessary data for drawing the dive profile
+ * uses cairo to draw it
+ */
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
int selected_dive = 0;
+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 {
/* Depth info */
int val;
int smoothed;
- enum { STABLE, SLOW, MODERATE, FAST, CRAZY } velocity;
+ velocity_t velocity;
struct plot_data *min[3];
struct plot_data *max[3];
int avg[3];
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) {
- /* Black is white and white is black */
double sum = r+g+b;
- if (sum > 2)
+ if (sum > 0.8)
r = g = b = 0;
- else if (sum < 1)
+ else
r = g = b = 1;
}
cairo_set_source_rgba(gc->cr, r, g, b, a);
}
-static void set_source_rgb(struct graphics_context *gc, double r, double g, double b)
+void set_source_rgb(struct graphics_context *gc, double r, double g, double b)
{
set_source_rgba(gc, r, g, b, 1);
}
int i;
struct plot_data *entry = pi->entry;
- cairo_set_source_rgba(gc->cr, 1, 0.2, 0.2, 0.20);
+ set_source_rgba(gc, 1, 0.2, 0.2, 0.20);
move_to(gc, entry->sec, entry->smoothed);
for (i = 1; i < pi->nr; i++) {
entry++;
int i;
struct plot_data *entry = pi->entry;
- cairo_set_source_rgba(gc->cr, 1, 0.2, 1, a);
+ set_source_rgba(gc, 1, 0.2, 1, a);
move_to(gc, entry->sec, entry->min[index]->val);
for (i = 1; i < pi->nr; i++) {
entry++;
{
int i;
cairo_t *cr = gc->cr;
- int ends, sec, depth;
- int *secs;
- int *depths;
+ int sec, depth;
struct plot_data *entry;
int maxtime, maxdepth, marker;
gc->leftx = 0; gc->rightx = maxtime;
- plot_smoothed_profile(gc, pi);
- plot_minmax_profile(gc, pi);
-
- entry = pi->entry;
- set_source_rgba(gc, 1, 0.2, 0.2, 0.80);
- secs = (int *) malloc(sizeof(int) * pi->nr);
- depths = (int *) malloc(sizeof(int) * pi->nr);
- secs[0] = entry->sec;
- depths[0] = entry->val;
- for (i = 1; i < pi->nr; i++) {
- entry++;
- sec = entry->sec;
- if (sec <= maxtime || entry->val > 0) {
- /* we want to draw the segments in different colors
- * representing the vertical velocity, so we need to
- * chop this into short segments */
- rgb_t color = rgb[entry->velocity];
- depth = entry->val;
- set_source_rgb(gc, color.r, color.g, color.b);
- move_to(gc, secs[i-1], depths[i-1]);
- line_to(gc, sec, depth);
- cairo_stroke(cr);
- ends = i;
- }
- secs[i] = sec;
- depths[i] = depth;
+ /*
+ * These are good for debugging text placement etc,
+ * but not for actual display..
+ */
+ if (0) {
+ plot_smoothed_profile(gc, pi);
+ plot_minmax_profile(gc, pi);
}
- move_to(gc, secs[ends], depths[ends]);
- gc->topy = 0; gc->bottomy = 1.0;
- line_to(gc, secs[ends], 0);
- line_to(gc, secs[0], 0);
- cairo_close_path(cr);
+
set_source_rgba(gc, 1, 0.2, 0.2, 0.80);
- cairo_stroke(cr);
- /* now do it again for the neat fill */
+
+ /* Do the depth profile for the neat fill */
gc->topy = 0; gc->bottomy = maxdepth;
set_source_rgba(gc, 1, 0.2, 0.2, 0.20);
- move_to(gc, secs[0], depths[0]);
- for (i = 1; i <= ends; i++) {
- line_to(gc, secs[i],depths[i]);
- }
- gc->topy = 0; gc->bottomy = 1.0;
- line_to(gc, secs[ends], 0);
- line_to(gc, secs[0], 0);
+
+ entry = pi->entry;
+ move_to(gc, 0, 0);
+ for (i = 0; i < pi->nr; i++, entry++)
+ line_to(gc, entry->sec, entry->val);
cairo_close_path(gc->cr);
+ if (gc->printer) {
+ set_source_rgba(gc, 1, 1, 1, 0.2);
+ cairo_fill_preserve(cr);
+ set_source_rgb(gc, 1, 1, 1);
+ cairo_stroke(cr);
+ return;
+ }
cairo_fill(gc->cr);
+
+ /* Now do it again for the velocity colors */
+ entry = pi->entry;
+ for (i = 1; i < pi->nr; i++) {
+ entry++;
+ sec = entry->sec;
+ /* we want to draw the segments in different colors
+ * representing the vertical velocity, so we need to
+ * chop this into short segments */
+ rgb_t color = rgb[entry->velocity];
+ depth = entry->val;
+ set_source_rgb(gc, color.r, color.g, color.b);
+ move_to(gc, entry[-1].sec, entry[-1].val);
+ line_to(gc, sec, depth);
+ cairo_stroke(cr);
+ }
}
static int setup_temperature_limits(struct graphics_context *gc, struct plot_info *pi)
if (output_units.temperature == FAHRENHEIT) {
deg = to_F(temperature);
- unit = "F";
+ unit = UTF8_DEGREE "F";
} else {
deg = to_C(temperature);
- unit = "C";
+ unit = UTF8_DEGREE "C";
}
- plot_text(gc, &tro, sec, temperature.mkelvin, "%d %s", deg, unit);
+ plot_text(gc, &tro, sec, temperature.mkelvin, "%d%s", deg, unit);
}
static void plot_temperature_text(struct graphics_context *gc, struct plot_info *pi)
gc->leftx = 0;
gc->rightx = get_maxtime(pi);
- gc->topy = 0; gc->bottomy = pi->maxpressure * 1.5;
+ gc->bottomy = 0; gc->topy = pi->maxpressure * 1.5;
return pi->maxpressure != 0;
}
if (!get_cylinder_pressure_range(gc, pi))
return;
- cairo_set_source_rgba(gc->cr, 0.2, 1.0, 0.2, 0.80);
+ set_source_rgba(gc, 0.2, 1.0, 0.2, 0.80);
move_to(gc, 0, pi->maxpressure);
for (i = 1; i < pi->nr; i++) {
cairo_stroke(gc->cr);
}
-/*
- * Return air usage (in liters).
- */
-static double calculate_airuse(struct dive *dive)
-{
- double airuse = 0;
- int i;
-
- for (i = 0; i < MAX_CYLINDERS; i++) {
- cylinder_t *cyl = dive->cylinder + i;
- int size = cyl->type.size.mliter;
- double kilo_atm;
-
- if (!size)
- continue;
-
- kilo_atm = (cyl->start.mbar - cyl->end.mbar) / 1013250.0;
-
- /* Liters of air at 1 atm == milliliters at 1k atm*/
- airuse += kilo_atm * size;
- }
- return airuse;
-}
-
-static void plot_info(struct dive *dive, struct graphics_context *gc)
-{
- text_render_options_t tro = {10, 0.2, 1.0, 0.2, RIGHT, BOTTOM};
- const double liters_per_cuft = 28.317;
- const char *unit, *format, *desc;
- double airuse;
- char buffer1[80];
- char buffer2[80];
- int len;
-
- airuse = calculate_airuse(dive);
- if (!airuse) {
- update_air_info(" \n ");
- return;
- }
- switch (output_units.volume) {
- case LITER:
- unit = "l";
- format = "vol: %4.0f %s";
- break;
- case CUFT:
- unit = "cuft";
- format = "vol: %4.2f %s";
- airuse /= liters_per_cuft;
- break;
- }
- tro.vpos = -1.0;
- plot_text(gc, &tro, 0.98, 0.98, format, airuse, unit);
- len = snprintf(buffer1, sizeof(buffer1), format, airuse, unit);
- tro.vpos = -2.2;
- if (dive->duration.seconds) {
- double pressure = 1 + (dive->meandepth.mm / 10000.0);
- double sac = airuse / pressure * 60 / dive->duration.seconds;
- plot_text(gc, &tro, 0.98, 0.98, "SAC: %4.2f %s/min", sac, unit);
- snprintf(buffer1+len, sizeof(buffer1)-len,
- "\nSAC: %4.2f %s/min", sac, unit);
- }
- len = 0;
- tro.vpos = -3.4;
- desc = dive->cylinder[0].type.description;
- if (desc || dive->cylinder[0].gasmix.o2.permille) {
- int o2 = dive->cylinder[0].gasmix.o2.permille / 10;
- if (!desc)
- desc = "";
- if (!o2)
- o2 = 21;
- plot_text(gc, &tro, 0.98, 0.98, "%s (%d%%)", desc, o2);
- len = snprintf(buffer2, sizeof(buffer2), "%s (%d%%): used ", desc, o2);
- }
- snprintf(buffer2+len, sizeof(buffer2)-len, buffer1);
- update_air_info(buffer2);
-}
-
static int mbar_to_PSI(int mbar)
{
pressure_t p = {mbar};
analyze_plot_info_minmax_minute(entry, first, last, 2);
}
+static velocity_t velocity(int speed)
+{
+ velocity_t v;
+
+ if (speed < -304) /* ascent faster than -60ft/min */
+ v = CRAZY;
+ else if (speed < -152) /* above -30ft/min */
+ v = FAST;
+ else if (speed < -76) /* -15ft/min */
+ v = MODERATE;
+ else if (speed < -25) /* -5ft/min */
+ v = SLOW;
+ else if (speed < 25) /* very hard to find data, but it appears that the recommendations
+ for descent are usually about 2x ascent rate; still, we want
+ stable to mean stable */
+ v = STABLE;
+ else if (speed < 152) /* between 5 and 30ft/min is considered slow */
+ v = SLOW;
+ else if (speed < 304) /* up to 60ft/min is moderate */
+ v = MODERATE;
+ else if (speed < 507) /* up to 100ft/min is fast */
+ v = FAST;
+ else /* more than that is just crazy - you'll blow your ears out */
+ v = CRAZY;
+
+ return v;
+}
static struct plot_info *analyze_plot_info(struct plot_info *pi)
{
int i;
entry->smoothed = (val+4) / 9;
}
/* vertical velocity in mm/sec */
+ /* Linus wants to smooth this - let's at least look at the samples that aren't FAST or CRAZY */
if (entry[0].sec - entry[-1].sec) {
- val = (entry[0].val - entry[-1].val) / (entry[0].sec - entry[-1].sec);
- if (val < -304) /* ascent faster than -60ft/min */
- entry->velocity = CRAZY;
- else if (val < -152) /* above -30ft/min */
- entry->velocity = FAST;
- else if (val < -76) /* -15ft/min */
- entry->velocity = MODERATE;
- else if (val < -25) /* -5ft/min */
- entry->velocity = SLOW;
- else if (val < 25) /* very hard to find data, but it appears that the recommendations
- for descent are usually about 2x ascent rate; still, we want
- stable to mean stable */
- entry->velocity = STABLE;
- else if (val < 152) /* between 5 and 30ft/min is considered slow */
- entry->velocity = SLOW;
- else if (val < 304) /* up to 60ft/min is moderate */
- entry->velocity = MODERATE;
- else if (val < 507) /* up to 100ft/min is fast */
- entry->velocity = FAST;
- else /* more than that is just crazy - you'll blow your ears out */
- entry->velocity = CRAZY;
+ entry->velocity = velocity((entry[0].val - entry[-1].val) / (entry[0].sec - entry[-1].sec));
+ /* if our samples are short and we aren't too FAST*/
+ if (entry[0].sec - entry[-1].sec < 30 && entry->velocity < FAST) {
+ int past = -2;
+ while (i+past > 0 && entry[0].sec - entry[past].sec < 30)
+ past--;
+ entry->velocity = velocity((entry[0].val - entry[past].val) /
+ (entry[0].sec - entry[past].sec));
+ }
} else
entry->velocity = STABLE;
}
plot_depth_text(gc, pi);
plot_cylinder_pressure_text(gc, pi);
- /* And info box in the lower right corner.. */
+ /* Bounding box last */
gc->leftx = 0; gc->rightx = 1.0;
gc->topy = 0; gc->bottomy = 1.0;
- plot_info(dive, gc);
- /* Bounding box last */
set_source_rgb(gc, 1, 1, 1);
move_to(gc, 0, 0);
line_to(gc, 0, 1);
cairo_close_path(gc->cr);
cairo_stroke(gc->cr);
-}
-
-static gboolean expose_event(GtkWidget *widget, GdkEventExpose *event, gpointer data)
-{
- struct dive *dive = current_dive;
- struct graphics_context gc = { .printer = 0 };
- int w,h;
-
- w = widget->allocation.width;
- h = widget->allocation.height;
-
- gc.cr = gdk_cairo_create(widget->window);
- set_source_rgb(&gc, 0, 0, 0);
- cairo_paint(gc.cr);
-
- if (dive)
- plot(&gc, w, h, dive);
-
- cairo_destroy(gc.cr);
-
- return FALSE;
-}
-
-GtkWidget *dive_profile_widget(void)
-{
- GtkWidget *da;
-
- da = gtk_drawing_area_new();
- gtk_widget_set_size_request(da, 350, 250);
- g_signal_connect(da, "expose_event", G_CALLBACK(expose_event), NULL);
-
- return da;
+ free(pi);
}