mirror of
https://github.com/subsurface/subsurface.git
synced 2025-02-01 00:33:24 +00:00
Don't print partial pressure values in the profile
Instead provide a scale on the right in a highly transparent grey and rely on the tooltip available with mouse-over to pinpoint the value at certain spots with much better accuracy. Fixes #30 Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
This commit is contained in:
parent
8dfc4dccc5
commit
9463e7895b
3 changed files with 21 additions and 268 deletions
1
color.h
1
color.h
|
@ -31,6 +31,7 @@
|
||||||
|
|
||||||
// Monochromes
|
// Monochromes
|
||||||
#define BLACK1_LOW_TRANS { 0.0, 0.0, 0.0, 0.75 }
|
#define BLACK1_LOW_TRANS { 0.0, 0.0, 0.0, 0.75 }
|
||||||
|
#define BLACK1_HIGH_TRANS { 0.0, 0.0, 0.0, 0.25 }
|
||||||
#define TUNDORA1_MED_TRANS { 0.3, 0.3, 0.3, 0.5 }
|
#define TUNDORA1_MED_TRANS { 0.3, 0.3, 0.3, 0.5 }
|
||||||
#define MERCURY1_MED_TRANS { 0.9, 0.9, 0.9, 0.5 }
|
#define MERCURY1_MED_TRANS { 0.9, 0.9, 0.9, 0.5 }
|
||||||
#define CONCRETE1_LOWER_TRANS { 0.95, 0.95, 0.95, 0.9 }
|
#define CONCRETE1_LOWER_TRANS { 0.95, 0.95, 0.95, 0.9 }
|
||||||
|
|
|
@ -19,6 +19,7 @@ struct plot_info {
|
||||||
int endpressure, maxpressure;
|
int endpressure, maxpressure;
|
||||||
int mintemp, maxtemp, endtemp;
|
int mintemp, maxtemp, endtemp;
|
||||||
double endtempcoord;
|
double endtempcoord;
|
||||||
|
double maxpp;
|
||||||
gboolean has_ndl;
|
gboolean has_ndl;
|
||||||
struct plot_data *entry;
|
struct plot_data *entry;
|
||||||
};
|
};
|
||||||
|
|
285
profile.c
285
profile.c
|
@ -69,7 +69,7 @@ typedef enum {
|
||||||
VELO_STABLE, VELO_SLOW, VELO_MODERATE, VELO_FAST, VELO_CRAZY,
|
VELO_STABLE, VELO_SLOW, VELO_MODERATE, VELO_FAST, VELO_CRAZY,
|
||||||
|
|
||||||
/* gas colors */
|
/* gas colors */
|
||||||
PO2, PO2_ALERT, PN2, PN2_ALERT, PHE, PHE_ALERT,
|
PO2, PO2_ALERT, PN2, PN2_ALERT, PHE, PHE_ALERT, PP_LINES,
|
||||||
|
|
||||||
/* Other colors */
|
/* Other colors */
|
||||||
TEXT_BACKGROUND, ALERT_BG, ALERT_FG, EVENTS, SAMPLE_DEEP, SAMPLE_SHALLOW,
|
TEXT_BACKGROUND, ALERT_BG, ALERT_FG, EVENTS, SAMPLE_DEEP, SAMPLE_SHALLOW,
|
||||||
|
@ -109,6 +109,7 @@ static const color_t profile_color[] = {
|
||||||
[PN2_ALERT] = {{RED1, BLACK1_LOW_TRANS}},
|
[PN2_ALERT] = {{RED1, BLACK1_LOW_TRANS}},
|
||||||
[PHE] = {{PEANUT, PEANUT_MED_TRANS}},
|
[PHE] = {{PEANUT, PEANUT_MED_TRANS}},
|
||||||
[PHE_ALERT] = {{RED1, PEANUT_MED_TRANS}},
|
[PHE_ALERT] = {{RED1, PEANUT_MED_TRANS}},
|
||||||
|
[PP_LINES] = {{BLACK1_HIGH_TRANS, BLACK1_HIGH_TRANS}},
|
||||||
|
|
||||||
[TEXT_BACKGROUND] = {{CONCRETE1_LOWER_TRANS, WHITE1}},
|
[TEXT_BACKGROUND] = {{CONCRETE1_LOWER_TRANS, WHITE1}},
|
||||||
[ALERT_BG] = {{BROOM1_LOWER_TRANS, BLACK1_LOW_TRANS}},
|
[ALERT_BG] = {{BROOM1_LOWER_TRANS, BLACK1_LOW_TRANS}},
|
||||||
|
@ -522,186 +523,6 @@ static void plot_depth_scale(struct graphics_context *gc, struct plot_info *pi)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ap points to an array of int with pi->nr + 1 elements that is
|
|
||||||
* ininitialized with just one -1 entry
|
|
||||||
* this adds entries (if they aren't too close to an existing one)
|
|
||||||
* and keeps things sorted
|
|
||||||
* we KNOW the array is big enough to hold all possible indices
|
|
||||||
* a2p is a secondary array - we insert value at the same relative
|
|
||||||
* positio as idx in ap */
|
|
||||||
static void add_index(int idx, int margin, int **ap, int **a2p, int value)
|
|
||||||
{
|
|
||||||
int j, i = 0;
|
|
||||||
int *a = *ap;
|
|
||||||
int *a2 = *a2p;
|
|
||||||
|
|
||||||
while (a[i] != -1 && a[i] < idx)
|
|
||||||
i++;
|
|
||||||
if (a[i] == idx)
|
|
||||||
return;
|
|
||||||
/* already have a POI to the left with the same vertical positiom and too close */
|
|
||||||
if ((i > 0 && a[i - 1] != -1 && a2[i - 1] == value && idx - a[i - 1] < margin))
|
|
||||||
return;
|
|
||||||
/* already have a POI to the right with the same vertical positiom and too close */
|
|
||||||
if (a[i] != -1 && a2[i] == value && a[i] - idx < margin)
|
|
||||||
return;
|
|
||||||
if (a[i] != -1 && a[i] - idx < margin)
|
|
||||||
return;
|
|
||||||
j = i;
|
|
||||||
while (a[j] != -1)
|
|
||||||
j++;
|
|
||||||
while (j >= i) {
|
|
||||||
a[j+1] = a[j];
|
|
||||||
a2[j+1] = a2[j];
|
|
||||||
j--;
|
|
||||||
}
|
|
||||||
a[i] = idx;
|
|
||||||
a2[i] = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
#define LI(_i,_j) MAX((_i)-(_j), 0)
|
|
||||||
#define RI(_i,_j) MIN((_i)+(_j), nr - 1)
|
|
||||||
#define SPIKE(_i,_s) if (fabs(_s) > fabs(spk_data[_i])) spk_data[_i] = (_s)
|
|
||||||
/* this is an attempt at a metric that finds spikes in a data series */
|
|
||||||
static void calculate_spikyness(int nr, double *data, double *spk_data, int deltax, double deltay)
|
|
||||||
{
|
|
||||||
int i, j;
|
|
||||||
double dminl, dminr, dmaxl, dmaxr;
|
|
||||||
|
|
||||||
#if DEBUG_PROFILE > 2
|
|
||||||
printf("Spike data: \n 0 ");
|
|
||||||
#endif
|
|
||||||
for (i = 0; i < nr; i++) {
|
|
||||||
dminl = dminr = dmaxl = dmaxr = data[i];
|
|
||||||
spk_data[i] = 0.0;
|
|
||||||
for (j = 1; j < deltax; j++) {
|
|
||||||
if (data[LI(i,j)] < dminl)
|
|
||||||
dminl = data[LI(i,j)];
|
|
||||||
if (data[LI(i,j)] > dmaxl)
|
|
||||||
dmaxl = data[LI(i,j)];
|
|
||||||
|
|
||||||
if (data[RI(i,j)] < dminr)
|
|
||||||
dminr = data[RI(i,j)];
|
|
||||||
if (data[RI(i,j)] > dmaxr)
|
|
||||||
dmaxr = data[RI(i,j)];
|
|
||||||
|
|
||||||
/* don't do super narrow */
|
|
||||||
if (j < deltax / 3)
|
|
||||||
continue;
|
|
||||||
/* falling edge on left */
|
|
||||||
if (dmaxl == data[i] && dmaxr - data[i] < 0.1 * (data[i] - dminl))
|
|
||||||
SPIKE(i,(data[i] - dminl) / j);
|
|
||||||
/* falling edge on right */
|
|
||||||
if (dmaxr == data[i] && dmaxl - data[i] < 0.1 * (data[i] - dminr))
|
|
||||||
SPIKE(i,(data[i] - dminr) / j);
|
|
||||||
|
|
||||||
/* minima get a negative spike value */
|
|
||||||
/* rising edge on left */
|
|
||||||
if (dminl == data[i] && data[i] - dminr < 0.1 * (dmaxl - data[i]))
|
|
||||||
SPIKE(i,(data[i] - dmaxl) / j);
|
|
||||||
/* rising edge on right */
|
|
||||||
if (dminr == data[i] && data[i] - dminl < 0.1 * (dmaxr - data[i]))
|
|
||||||
SPIKE(i,(data[i] - dmaxr) / j);
|
|
||||||
}
|
|
||||||
#if DEBUG_PROFILE > 2
|
|
||||||
fprintf(debugfile, "%.4lf ", spk_data[i]);
|
|
||||||
if (i % 12 == 11)
|
|
||||||
fprintf(debugfile, "\n%2d ", (i + 1) / 12);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
#if DEBUG_PROFILE > 2
|
|
||||||
printf("\n");
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
/* only show one spike in a deltax wide region - pick the highest (and first if the same) */
|
|
||||||
static gboolean higher_spike(double *spk_data, int idx, int nr, int deltax)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
double s = spk_data[idx];
|
|
||||||
for (i = MAX(0, idx - deltax); i <= MIN(idx + deltax, nr - 1); i++) {
|
|
||||||
if (s > 0) {
|
|
||||||
if (spk_data[i] > s)
|
|
||||||
return TRUE;
|
|
||||||
} else {
|
|
||||||
if (spk_data[i] < s)
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
if (spk_data[i] == s && i < idx)
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* this figures out which time stamps provide "interesting" formations in the graphs;
|
|
||||||
* this includes local minima and maxima as well as long plateaus.
|
|
||||||
* pass in the function that returns the value at a certain point (as double),
|
|
||||||
* the delta in time (expressed as number of data points of "significant time")
|
|
||||||
* the delta at which the value is considered to have been "significantly changed" and
|
|
||||||
* the number of points to cover
|
|
||||||
* returns a list of indices that ends with a -1 of times that are "interesting" */
|
|
||||||
static void find_points_of_interest(struct plot_info *pi, double (*value_func)(int, struct plot_info *),
|
|
||||||
int deltax, double deltay, int **poip, int **poip_vpos)
|
|
||||||
{
|
|
||||||
int i, j, nr = pi->nr;
|
|
||||||
double *data, *data_max, *data_min, *spk_data;
|
|
||||||
double min, max;
|
|
||||||
int *pois;
|
|
||||||
|
|
||||||
/* avoid all the function calls by creating a local array and
|
|
||||||
* have some helper arrays to make our lifes easier */
|
|
||||||
|
|
||||||
data = malloc(nr * sizeof(double));
|
|
||||||
data_max = malloc(nr * sizeof(double));
|
|
||||||
data_min = malloc(nr * sizeof(double));
|
|
||||||
spk_data = malloc(nr * sizeof(double));
|
|
||||||
|
|
||||||
pois = *poip = malloc((nr + 1) * sizeof(int));
|
|
||||||
*poip_vpos = malloc((nr + 1) * sizeof(int));
|
|
||||||
pois[0] = -1;
|
|
||||||
pois[1] = -1;
|
|
||||||
|
|
||||||
/* copy the data and get the absolute minimum and maximum while we do it */
|
|
||||||
for (i = 0; i < nr; i++) {
|
|
||||||
data_max[i] = data_min[i] = data[i] = value_func(i, pi);
|
|
||||||
if (i == 0 || data[i] < min)
|
|
||||||
min = data[i];
|
|
||||||
if (i == 0 || data[i] > max)
|
|
||||||
max = data[i];
|
|
||||||
}
|
|
||||||
/* next find out if there are real spikes in the graph */
|
|
||||||
calculate_spikyness(nr, data, spk_data, deltax, deltay);
|
|
||||||
|
|
||||||
/* now process all data points */
|
|
||||||
for (i = 0; i < nr; i++) {
|
|
||||||
/* get the local min/max */
|
|
||||||
for (j = MAX(0, i - deltax); j < i + deltax && j < nr; j++) {
|
|
||||||
if (data[j] < data[i])
|
|
||||||
data_min[i] = data[j];
|
|
||||||
if (data[j] > data[i])
|
|
||||||
data_max[i] = data[j];
|
|
||||||
}
|
|
||||||
/* is i the overall minimum or maximum */
|
|
||||||
if (data[i] == max && (i == 0 || data[i - 1] != max))
|
|
||||||
add_index(i, deltax, poip, poip_vpos, BOTTOM);
|
|
||||||
if (data[i] == min && (i == 0 || data[i - 1] != min))
|
|
||||||
add_index(i, deltax, poip, poip_vpos, TOP);
|
|
||||||
/* is i a spike? */
|
|
||||||
if (fabs(spk_data[i]) > 0.01 && ! higher_spike(spk_data, i, nr, deltax)) {
|
|
||||||
if (spk_data[i] > 0.0)
|
|
||||||
add_index(i, deltax, poip, poip_vpos, BOTTOM);
|
|
||||||
if (spk_data[i] < 0.0)
|
|
||||||
add_index(i, deltax, poip, poip_vpos, TOP);
|
|
||||||
}
|
|
||||||
/* is i a significant local minimum or maximum? */
|
|
||||||
if (data[i] == data_min[i] && data_max[i] - data[i] > deltay)
|
|
||||||
add_index(i, deltax, poip, poip_vpos, TOP);
|
|
||||||
if (data[i] == data_max[i] && data[i] - data_min[i] > deltay)
|
|
||||||
add_index(i, deltax, poip, poip_vpos, BOTTOM);
|
|
||||||
}
|
|
||||||
/* still need to search for plateaus */
|
|
||||||
}
|
|
||||||
|
|
||||||
static void setup_pp_limits(struct graphics_context *gc, struct plot_info *pi)
|
static void setup_pp_limits(struct graphics_context *gc, struct plot_info *pi)
|
||||||
{
|
{
|
||||||
int maxdepth;
|
int maxdepth;
|
||||||
|
@ -717,99 +538,23 @@ static void setup_pp_limits(struct graphics_context *gc, struct plot_info *pi)
|
||||||
gc->bottomy = -gc->topy / 20;
|
gc->bottomy = -gc->topy / 20;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void plot_single_pp_text(struct graphics_context *gc, int sec, double pp,
|
|
||||||
double vpos, color_indice_t color)
|
|
||||||
{
|
|
||||||
text_render_options_t tro = {12, color, CENTER, vpos};
|
|
||||||
plot_text(gc, &tro, sec, pp, "%.1lf", pp);
|
|
||||||
}
|
|
||||||
|
|
||||||
#define MAXPP(_mpp, _pp) { _mpp = 0; \
|
|
||||||
for(i = 0; i< pi->nr; i++) \
|
|
||||||
if (pi->entry[i]._pp > _mpp) \
|
|
||||||
_mpp = pi->entry[i]._pp; \
|
|
||||||
}
|
|
||||||
|
|
||||||
static double po2_value(int idx, struct plot_info *pi)
|
|
||||||
{
|
|
||||||
return pi->entry[idx].po2;
|
|
||||||
}
|
|
||||||
|
|
||||||
static double pn2_value(int idx, struct plot_info *pi)
|
|
||||||
{
|
|
||||||
return pi->entry[idx].pn2;
|
|
||||||
}
|
|
||||||
|
|
||||||
static double phe_value(int idx, struct plot_info *pi)
|
|
||||||
{
|
|
||||||
return pi->entry[idx].phe;
|
|
||||||
}
|
|
||||||
|
|
||||||
static double plot_single_gas_pp_text(struct graphics_context *gc, struct plot_info *pi,
|
|
||||||
double (*value_func)(int, struct plot_info *),
|
|
||||||
double value_threshold, int color)
|
|
||||||
{
|
|
||||||
int *pois, *pois_vpos;
|
|
||||||
int i, two_minutes = 1;
|
|
||||||
double maxpp = 0.0;
|
|
||||||
|
|
||||||
/* don't bother with local min/max if the dive is under two minutes */
|
|
||||||
if (pi->entry[pi->nr - 1].sec > 120) {
|
|
||||||
int idx = 0;
|
|
||||||
while (pi->entry[idx].sec == 0)
|
|
||||||
idx++;
|
|
||||||
while (pi->entry[idx + two_minutes].sec < 120)
|
|
||||||
two_minutes++;
|
|
||||||
} else {
|
|
||||||
two_minutes = pi->nr;
|
|
||||||
}
|
|
||||||
find_points_of_interest(pi, value_func, two_minutes, value_threshold, &pois, &pois_vpos);
|
|
||||||
for (i = 0; pois[i] != -1; i++) {
|
|
||||||
struct plot_data *entry = pi->entry + pois[i];
|
|
||||||
double value = value_func(pois[i], pi);
|
|
||||||
|
|
||||||
#if DEBUG_PROFILE > 1
|
|
||||||
fprintf(debugfile, "POI at %d sec value %lf\n", entry->sec, entry->po2);
|
|
||||||
#endif
|
|
||||||
plot_single_pp_text(gc, entry->sec, value, pois_vpos[i], color);
|
|
||||||
if (value > maxpp)
|
|
||||||
maxpp = value;
|
|
||||||
}
|
|
||||||
free(pois);
|
|
||||||
free(pois_vpos);
|
|
||||||
|
|
||||||
return maxpp;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void plot_pp_text(struct graphics_context *gc, struct plot_info *pi)
|
static void plot_pp_text(struct graphics_context *gc, struct plot_info *pi)
|
||||||
{
|
{
|
||||||
double pp, dpp, m, maxpp = 0.0;
|
double pp, dpp, m;
|
||||||
int hpos;
|
int hpos;
|
||||||
static const text_render_options_t tro = {11, PN2, LEFT, MIDDLE};
|
static const text_render_options_t tro = {11, PP_LINES, LEFT, MIDDLE};
|
||||||
|
|
||||||
setup_pp_limits(gc, pi);
|
setup_pp_limits(gc, pi);
|
||||||
|
|
||||||
if (partial_pressure_graphs.po2) {
|
pp = floor(pi->maxpp * 10.0) / 10.0 + 0.2;
|
||||||
maxpp = plot_single_gas_pp_text(gc, pi, po2_value, 0.4, PO2);
|
|
||||||
}
|
|
||||||
if (partial_pressure_graphs.pn2) {
|
|
||||||
m = plot_single_gas_pp_text(gc, pi, pn2_value, 0.6, PN2);
|
|
||||||
if (m > maxpp)
|
|
||||||
maxpp = m;
|
|
||||||
}
|
|
||||||
if (partial_pressure_graphs.phe) {
|
|
||||||
m = plot_single_gas_pp_text(gc, pi, phe_value, 0.4, PHE);
|
|
||||||
if (m > maxpp)
|
|
||||||
maxpp = m;
|
|
||||||
}
|
|
||||||
/* while this is somewhat useful, I don't like the way it looks...
|
|
||||||
* for now I'll leave the code here, but disable it */
|
|
||||||
if (0) {
|
|
||||||
pp = floor(maxpp * 10.0) / 10.0 + 0.2;
|
|
||||||
dpp = floor(2.0 * pp) / 10.0;
|
dpp = floor(2.0 * pp) / 10.0;
|
||||||
hpos = pi->entry[pi->nr - 1].sec + 30;
|
hpos = pi->entry[pi->nr - 1].sec;
|
||||||
for (m = 0.0; m <= pp; m += dpp)
|
set_source_rgba(gc, PP_LINES);
|
||||||
plot_text(gc, &tro, hpos, m, "%.1f", m);
|
for (m = 0.0; m <= pp; m += dpp) {
|
||||||
|
move_to(gc, 0, m);
|
||||||
|
line_to(gc, hpos, m);
|
||||||
|
cairo_stroke(gc->cr);
|
||||||
|
plot_text(gc, &tro, hpos + 30, m, "%.1f", m);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1894,6 +1639,12 @@ static struct plot_info *create_plot_info(struct dive *dive, struct divecomputer
|
||||||
entry->phe = fhe / 1000.0 * amb_pressure;
|
entry->phe = fhe / 1000.0 * amb_pressure;
|
||||||
entry->pn2 = (1000 - fo2 - fhe) / 1000.0 * amb_pressure;
|
entry->pn2 = (1000 - fo2 - fhe) / 1000.0 * amb_pressure;
|
||||||
}
|
}
|
||||||
|
if (entry->po2 > pi->maxpp)
|
||||||
|
pi->maxpp = entry->po2;
|
||||||
|
if (entry->phe > pi->maxpp)
|
||||||
|
pi->maxpp = entry->phe;
|
||||||
|
if (entry->pn2 > pi->maxpp)
|
||||||
|
pi->maxpp = entry->pn2;
|
||||||
/* finally, do the discrete integration to get the SAC rate equivalent */
|
/* finally, do the discrete integration to get the SAC rate equivalent */
|
||||||
current->pressure_time += (entry->sec - (entry-1)->sec) *
|
current->pressure_time += (entry->sec - (entry-1)->sec) *
|
||||||
depth_to_mbar((entry->depth + (entry-1)->depth) / 2, dive) / 1000.0;
|
depth_to_mbar((entry->depth + (entry-1)->depth) / 2, dive) / 1000.0;
|
||||||
|
|
Loading…
Add table
Reference in a new issue