profile: use C++ string functions to format plot data

This may appear a bit ominous, as it doesn't generate a string,
but a vector of strings (one for each line). However, that is
in preparation for the QtQuickification of the profile, where
the text-items take such a list.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
This commit is contained in:
Berthold Stoeger 2024-03-10 23:11:23 +01:00 committed by bstoeger
parent 422f693f5b
commit ae299d5e66
4 changed files with 149 additions and 158 deletions

View file

@ -57,8 +57,17 @@ static void dump_pi(struct plot_info *pi)
} }
#endif #endif
#define ROUND_UP(x, y) ((((x) + (y) - 1) / (y)) * (y)) template<typename T>
#define DIV_UP(x, y) (((x) + (y) - 1) / (y)) static T round_up(T x, T y)
{
return ((x + y - 1) / y) * y;
}
template<typename T>
static T div_up(T x, T y)
{
return (x + y - 1) / y;
}
/* /*
* When showing dive profiles, we scale things to the * When showing dive profiles, we scale things to the
@ -530,7 +539,7 @@ static void populate_plot_entries(const struct dive *dive, const struct divecomp
* *
* Everything in between has a cylinder pressure for at least some of the cylinders. * Everything in between has a cylinder pressure for at least some of the cylinders.
*/ */
static int sac_between(const struct dive *dive, struct plot_info *pi, int first, int last, const bool gases[]) static int sac_between(const struct dive *dive, struct plot_info *pi, int first, int last, const char gases[])
{ {
int i, airuse; int i, airuse;
double pressuretime; double pressuretime;
@ -578,7 +587,7 @@ static int sac_between(const struct dive *dive, struct plot_info *pi, int first,
} }
/* Is there pressure data for all gases? */ /* Is there pressure data for all gases? */
static bool all_pressures(struct plot_info *pi, int idx, const bool gases[]) static bool all_pressures(struct plot_info *pi, int idx, const char gases[])
{ {
int i; int i;
@ -591,7 +600,7 @@ static bool all_pressures(struct plot_info *pi, int idx, const bool gases[])
} }
/* Which of the set of gases have pressure data? Returns false if none of them. */ /* Which of the set of gases have pressure data? Returns false if none of them. */
static bool filter_pressures(struct plot_info *pi, int idx, const bool gases_in[], bool gases_out[]) static bool filter_pressures(struct plot_info *pi, int idx, const char gases_in[], char gases_out[])
{ {
int i; int i;
bool has_pressure = false; bool has_pressure = false;
@ -610,7 +619,7 @@ static bool filter_pressures(struct plot_info *pi, int idx, const bool gases_in[
* an array of gases, the caller passes in scratch memory in the last * an array of gases, the caller passes in scratch memory in the last
* argument. * argument.
*/ */
static void fill_sac(const struct dive *dive, struct plot_info *pi, int idx, const bool gases_in[], bool gases[]) static void fill_sac(const struct dive *dive, struct plot_info *pi, int idx, const char gases_in[], char gases[])
{ {
struct plot_data *entry = pi->entry + idx; struct plot_data *entry = pi->entry + idx;
int first, last; int first, last;
@ -668,7 +677,7 @@ static void fill_sac(const struct dive *dive, struct plot_info *pi, int idx, con
/* /*
* Create a bitmap of cylinders that match our current gasmix * Create a bitmap of cylinders that match our current gasmix
*/ */
static void matching_gases(const struct dive *dive, struct gasmix gasmix, bool gases[]) static void matching_gases(const struct dive *dive, struct gasmix gasmix, char gases[])
{ {
int i; int i;
@ -680,32 +689,28 @@ static void calculate_sac(const struct dive *dive, const struct divecomputer *dc
{ {
struct gasmix gasmix = gasmix_invalid; struct gasmix gasmix = gasmix_invalid;
const struct event *ev = NULL; const struct event *ev = NULL;
bool *gases, *gases_scratch;
gases = (bool *)calloc(pi->nr_cylinders, sizeof(*gases)); std::vector<char> gases(pi->nr_cylinders, false);
/* This might be premature optimization, but let's allocate the gas array for /* This might be premature optimization, but let's allocate the gas array for
* the fill_sac function only once an not once per sample */ * the fill_sac function only once an not once per sample */
gases_scratch = (bool *)malloc(pi->nr_cylinders * sizeof(*gases)); std::vector<char> gases_scratch(pi->nr_cylinders);
for (int i = 0; i < pi->nr; i++) { for (int i = 0; i < pi->nr; i++) {
struct plot_data *entry = pi->entry + i; struct plot_data *entry = pi->entry + i;
struct gasmix newmix = get_gasmix(dive, dc, entry->sec, &ev, gasmix); struct gasmix newmix = get_gasmix(dive, dc, entry->sec, &ev, gasmix);
if (!same_gasmix(newmix, gasmix)) { if (!same_gasmix(newmix, gasmix)) {
gasmix = newmix; gasmix = newmix;
matching_gases(dive, newmix, gases); matching_gases(dive, newmix, gases.data());
} }
fill_sac(dive, pi, i, gases, gases_scratch); fill_sac(dive, pi, i, gases.data(), gases_scratch.data());
} }
free(gases);
free(gases_scratch);
} }
static void populate_secondary_sensor_data(const struct divecomputer *dc, struct plot_info *pi) static void populate_secondary_sensor_data(const struct divecomputer *dc, struct plot_info *pi)
{ {
int *seen = (int *)calloc(pi->nr_cylinders, sizeof(*seen)); std::vector<int> seen(pi->nr_cylinders, 0);
for (int idx = 0; idx < pi->nr; ++idx) for (int idx = 0; idx < pi->nr; ++idx)
for (int c = 0; c < pi->nr_cylinders; ++c) for (int c = 0; c < pi->nr_cylinders; ++c)
if (get_plot_pressure_data(pi, idx, SENSOR_PR, c)) if (get_plot_pressure_data(pi, idx, SENSOR_PR, c))
@ -725,7 +730,6 @@ static void populate_secondary_sensor_data(const struct divecomputer *dc, struct
if (sample->sensor[s] != NO_SENSOR && seen[sample->sensor[s]] < 3 && sample->pressure[s].mbar) if (sample->sensor[s] != NO_SENSOR && seen[sample->sensor[s]] < 3 && sample->pressure[s].mbar)
set_plot_pressure_data(pi, idx, SENSOR_PR, sample->sensor[s], sample->pressure[s].mbar); set_plot_pressure_data(pi, idx, SENSOR_PR, sample->sensor[s], sample->pressure[s].mbar);
} }
free(seen);
} }
/* /*
@ -752,16 +756,11 @@ static void setup_gas_sensor_pressure(const struct dive *dive, const struct dive
/* FIXME: The planner uses a dummy one-past-end cylinder for surface air! */ /* FIXME: The planner uses a dummy one-past-end cylinder for surface air! */
int num_cyl = pi->nr_cylinders + 1; int num_cyl = pi->nr_cylinders + 1;
int *seen = (int *)malloc(num_cyl * sizeof(*seen)); std::vector<int> seen(num_cyl, 0);
int *first = (int *)malloc(num_cyl * sizeof(*first)); std::vector<int> first(num_cyl, 0);
int *last = (int *)malloc(num_cyl * sizeof(*last)); std::vector<int> last(num_cyl, INT_MAX);
const struct divecomputer *secondary; const struct divecomputer *secondary;
for (i = 0; i < num_cyl; i++) {
seen[i] = 0;
first[i] = 0;
last[i] = INT_MAX;
}
prev = explicit_first_cylinder(dive, dc); prev = explicit_first_cylinder(dive, dc);
seen[prev] = 1; seen[prev] = 1;
@ -839,10 +838,6 @@ static void setup_gas_sensor_pressure(const struct dive *dive, const struct dive
continue; continue;
populate_secondary_sensor_data(secondary, pi); populate_secondary_sensor_data(secondary, pi);
} while ((secondary = secondary->next) != NULL); } while ((secondary = secondary->next) != NULL);
free(seen);
free(first);
free(last);
} }
/* calculate DECO STOP / TTS / NDL */ /* calculate DECO STOP / TTS / NDL */
@ -857,7 +852,7 @@ static void calculate_ndl_tts(struct deco_state *ds, const struct dive *dive, st
const int time_stepsize = 60; const int time_stepsize = 60;
const int deco_stepsize = M_OR_FT(3, 10); const int deco_stepsize = M_OR_FT(3, 10);
/* at what depth is the current deco-step? */ /* at what depth is the current deco-step? */
int next_stop = ROUND_UP(deco_allowed_depth( int next_stop = round_up(deco_allowed_depth(
tissue_tolerance_calc(ds, dive, depth_to_bar(entry->depth, dive), in_planner), tissue_tolerance_calc(ds, dive, depth_to_bar(entry->depth, dive), in_planner),
surface_pressure, dive, 1), deco_stepsize); surface_pressure, dive, 1), deco_stepsize);
int ascent_depth = entry->depth; int ascent_depth = entry->depth;
@ -892,7 +887,7 @@ static void calculate_ndl_tts(struct deco_state *ds, const struct dive *dive, st
for (; ascent_depth > next_stop; ascent_depth -= ascent_s_per_step * ascent_velocity(ascent_depth, entry->running_sum / entry->sec, 0), entry->tts_calc += ascent_s_per_step) { for (; ascent_depth > next_stop; ascent_depth -= ascent_s_per_step * ascent_velocity(ascent_depth, entry->running_sum / entry->sec, 0), entry->tts_calc += ascent_s_per_step) {
add_segment(ds, depth_to_bar(ascent_depth, dive), add_segment(ds, depth_to_bar(ascent_depth, dive),
gasmix, ascent_s_per_step, entry->o2pressure.mbar, divemode, prefs.decosac, in_planner); gasmix, ascent_s_per_step, entry->o2pressure.mbar, divemode, prefs.decosac, in_planner);
next_stop = ROUND_UP(deco_allowed_depth(tissue_tolerance_calc(ds, dive, depth_to_bar(ascent_depth, dive), in_planner), next_stop = round_up(deco_allowed_depth(tissue_tolerance_calc(ds, dive, depth_to_bar(ascent_depth, dive), in_planner),
surface_pressure, dive, 1), deco_stepsize); surface_pressure, dive, 1), deco_stepsize);
} }
ascent_depth = next_stop; ascent_depth = next_stop;
@ -1098,7 +1093,7 @@ static void calculate_deco_information(struct deco_state *ds, const struct deco_
* at end of whole minute after clearing ceiling. The deepest ceiling when planning a dive * at end of whole minute after clearing ceiling. The deepest ceiling when planning a dive
* comes typically 10-60s after the end of the bottom time, so add 20s to the calculated * comes typically 10-60s after the end of the bottom time, so add 20s to the calculated
* deco time. */ * deco time. */
ds->deco_time = ROUND_UP(time_clear_ceiling - time_deep_ceiling + 20, 60) + 20; ds->deco_time = round_up(time_clear_ceiling - time_deep_ceiling + 20, 60) + 20;
vpmb_next_gradient(ds, ds->deco_time, surface_pressure / 1000.0, in_planner); vpmb_next_gradient(ds, ds->deco_time, surface_pressure / 1000.0, in_planner);
final_tts = 0; final_tts = 0;
last_ndl_tts_calc_time = 0; last_ndl_tts_calc_time = 0;
@ -1360,7 +1355,7 @@ extern "C" void create_plot_info_new(const struct dive *dive, const struct divec
analyze_plot_info(pi); analyze_plot_info(pi);
} }
static void plot_string(const struct dive *d, const struct plot_info *pi, int idx, struct membuffer *b) static std::vector<std::string> plot_string(const struct dive *d, const struct plot_info *pi, int idx)
{ {
int pressurevalue, mod, ead, end, eadd; int pressurevalue, mod, ead, end, eadd;
const char *depth_unit, *pressure_unit, *temp_unit, *vertical_speed_unit; const char *depth_unit, *pressure_unit, *temp_unit, *vertical_speed_unit;
@ -1368,43 +1363,45 @@ static void plot_string(const struct dive *d, const struct plot_info *pi, int id
int decimals, cyl; int decimals, cyl;
const char *unit; const char *unit;
const struct plot_data *entry = pi->entry + idx; const struct plot_data *entry = pi->entry + idx;
std::vector<std::string> res;
depthvalue = get_depth_units(entry->depth, NULL, &depth_unit); depthvalue = get_depth_units(entry->depth, NULL, &depth_unit);
put_format_loc(b, translate("gettextFromC", "@: %d:%02d\nD: %.1f%s\n"), FRACTION(entry->sec, 60), depthvalue, depth_unit); res.push_back(casprintf_loc(translate("gettextFromC", "@: %d:%02d"), FRACTION(entry->sec, 60), depthvalue));
res.push_back(casprintf_loc(translate("gettextFromC", "D: %.1f%s"), depth_unit));
for (cyl = 0; cyl < pi->nr_cylinders; cyl++) { for (cyl = 0; cyl < pi->nr_cylinders; cyl++) {
int mbar = get_plot_pressure(pi, idx, cyl); int mbar = get_plot_pressure(pi, idx, cyl);
if (!mbar) if (!mbar)
continue; continue;
struct gasmix mix = get_cylinder(d, cyl)->gasmix; struct gasmix mix = get_cylinder(d, cyl)->gasmix;
pressurevalue = get_pressure_units(mbar, &pressure_unit); pressurevalue = get_pressure_units(mbar, &pressure_unit);
put_format_loc(b, translate("gettextFromC", "P: %d%s (%s)\n"), pressurevalue, pressure_unit, gasname(mix)); res.push_back(casprintf_loc(translate("gettextFromC", "P: %d%s (%s)"), pressurevalue, pressure_unit, gasname(mix)));
} }
if (entry->temperature) { if (entry->temperature) {
tempvalue = get_temp_units(entry->temperature, &temp_unit); tempvalue = get_temp_units(entry->temperature, &temp_unit);
put_format_loc(b, translate("gettextFromC", "T: %.1f%s\n"), tempvalue, temp_unit); res.push_back(casprintf_loc(translate("gettextFromC", "T: %.1f%s"), tempvalue, temp_unit));
} }
speedvalue = get_vertical_speed_units(abs(entry->speed), NULL, &vertical_speed_unit); speedvalue = get_vertical_speed_units(abs(entry->speed), NULL, &vertical_speed_unit);
/* Ascending speeds are positive, descending are negative */ /* Ascending speeds are positive, descending are negative */
if (entry->speed > 0) if (entry->speed > 0)
speedvalue *= -1; speedvalue *= -1;
put_format_loc(b, translate("gettextFromC", "V: %.1f%s\n"), speedvalue, vertical_speed_unit); res.push_back(casprintf_loc(translate("gettextFromC", "V: %.1f%s"), speedvalue, vertical_speed_unit));
sacvalue = get_volume_units(entry->sac, &decimals, &unit); sacvalue = get_volume_units(entry->sac, &decimals, &unit);
if (entry->sac && prefs.show_sac) if (entry->sac && prefs.show_sac)
put_format_loc(b, translate("gettextFromC", "SAC: %.*f%s/min\n"), decimals, sacvalue, unit); res.push_back(casprintf_loc(translate("gettextFromC", "SAC: %.*f%s/min"), decimals, sacvalue, unit));
if (entry->cns) if (entry->cns)
put_format_loc(b, translate("gettextFromC", "CNS: %u%%\n"), entry->cns); res.push_back(casprintf_loc(translate("gettextFromC", "CNS: %u%%"), entry->cns));
if (prefs.pp_graphs.po2 && entry->pressures.o2 > 0) { if (prefs.pp_graphs.po2 && entry->pressures.o2 > 0) {
put_format_loc(b, translate("gettextFromC", "pO₂: %.2fbar\n"), entry->pressures.o2); res.push_back(casprintf_loc(translate("gettextFromC", "pO₂: %.2fbar"), entry->pressures.o2));
if (entry->scr_OC_pO2.mbar) if (entry->scr_OC_pO2.mbar)
put_format_loc(b, translate("gettextFromC", "SCR ΔpO₂: %.2fbar\n"), entry->scr_OC_pO2.mbar/1000.0 - entry->pressures.o2); res.push_back(casprintf_loc(translate("gettextFromC", "SCR ΔpO₂: %.2fbar"), entry->scr_OC_pO2.mbar/1000.0 - entry->pressures.o2));
} }
if (prefs.pp_graphs.pn2 && entry->pressures.n2 > 0) if (prefs.pp_graphs.pn2 && entry->pressures.n2 > 0)
put_format_loc(b, translate("gettextFromC", "pN₂: %.2fbar\n"), entry->pressures.n2); res.push_back(casprintf_loc(translate("gettextFromC", "pN₂: %.2fbar"), entry->pressures.n2));
if (prefs.pp_graphs.phe && entry->pressures.he > 0) if (prefs.pp_graphs.phe && entry->pressures.he > 0)
put_format_loc(b, translate("gettextFromC", "pHe: %.2fbar\n"), entry->pressures.he); res.push_back(casprintf_loc(translate("gettextFromC", "pHe: %.2fbar"), entry->pressures.he));
if (prefs.mod && entry->mod > 0) { if (prefs.mod && entry->mod > 0) {
mod = lrint(get_depth_units(entry->mod, NULL, &depth_unit)); mod = lrint(get_depth_units(entry->mod, NULL, &depth_unit));
put_format_loc(b, translate("gettextFromC", "MOD: %d%s\n"), mod, depth_unit); res.push_back(casprintf_loc(translate("gettextFromC", "MOD: %d%s"), mod, depth_unit));
} }
eadd = lrint(get_depth_units(entry->eadd, NULL, &depth_unit)); eadd = lrint(get_depth_units(entry->eadd, NULL, &depth_unit));
@ -1413,18 +1410,20 @@ static void plot_string(const struct dive *d, const struct plot_info *pi, int id
case plot_info::NITROX: case plot_info::NITROX:
if (entry->ead > 0) { if (entry->ead > 0) {
ead = lrint(get_depth_units(entry->ead, NULL, &depth_unit)); ead = lrint(get_depth_units(entry->ead, NULL, &depth_unit));
put_format_loc(b, translate("gettextFromC", "EAD: %d%s\nEADD: %d%s / %.1fg/\n"), ead, depth_unit, eadd, depth_unit, entry->density); res.push_back(casprintf_loc(translate("gettextFromC", "EAD: %d%s"), ead, depth_unit));
res.push_back(casprintf_loc(translate("gettextFromC", "EADD: %d%s / %.1fg/"), eadd, depth_unit, entry->density));
break; break;
} }
case plot_info::TRIMIX: case plot_info::TRIMIX:
if (entry->end > 0) { if (entry->end > 0) {
end = lrint(get_depth_units(entry->end, NULL, &depth_unit)); end = lrint(get_depth_units(entry->end, NULL, &depth_unit));
put_format_loc(b, translate("gettextFromC", "END: %d%s\nEADD: %d%s / %.1fg/\n"), end, depth_unit, eadd, depth_unit, entry->density); res.push_back(casprintf_loc(translate("gettextFromC", "END: %d%s"), end, depth_unit));
res.push_back(casprintf_loc(translate("gettextFromC", "EADD: %d%s / %.1fg/"), eadd, depth_unit, entry->density));
break; break;
} }
case plot_info::AIR: case plot_info::AIR:
if (entry->density > 0) { if (entry->density > 0) {
put_format_loc(b, translate("gettextFromC", "Density: %.1fg/\n"), entry->density); res.push_back(casprintf_loc(translate("gettextFromC", "Density: %.1fg/"), entry->density));
} }
case plot_info::FREEDIVING: case plot_info::FREEDIVING:
/* nothing */ /* nothing */
@ -1436,151 +1435,142 @@ static void plot_string(const struct dive *d, const struct plot_info *pi, int id
if (entry->ndl > 0) { if (entry->ndl > 0) {
/* this is a safety stop as we still have ndl */ /* this is a safety stop as we still have ndl */
if (entry->stoptime) if (entry->stoptime)
put_format_loc(b, translate("gettextFromC", "Safety stop: %umin @ %.0f%s\n"), DIV_UP(entry->stoptime, 60), res.push_back(casprintf_loc(translate("gettextFromC", "Safety stop: %umin @ %.0f%s"), div_up(entry->stoptime, 60),
depthvalue, depth_unit); depthvalue, depth_unit));
else else
put_format_loc(b, translate("gettextFromC", "Safety stop: unknown time @ %.0f%s\n"), res.push_back(casprintf_loc(translate("gettextFromC", "Safety stop: unknown time @ %.0f%s"),
depthvalue, depth_unit); depthvalue, depth_unit));
} else { } else {
/* actual deco stop */ /* actual deco stop */
if (entry->stoptime) if (entry->stoptime)
put_format_loc(b, translate("gettextFromC", "Deco: %umin @ %.0f%s\n"), DIV_UP(entry->stoptime, 60), res.push_back(casprintf_loc(translate("gettextFromC", "Deco: %umin @ %.0f%s"), div_up(entry->stoptime, 60),
depthvalue, depth_unit); depthvalue, depth_unit));
else else
put_format_loc(b, translate("gettextFromC", "Deco: unknown time @ %.0f%s\n"), res.push_back(casprintf_loc(translate("gettextFromC", "Deco: unknown time @ %.0f%s"),
depthvalue, depth_unit); depthvalue, depth_unit));
} }
} else if (entry->in_deco) { } else if (entry->in_deco) {
put_string(b, translate("gettextFromC", "In deco\n")); res.push_back(translate("gettextFromC", "In deco"));
} else if (entry->ndl >= 0) { } else if (entry->ndl >= 0) {
put_format_loc(b, translate("gettextFromC", "NDL: %umin\n"), DIV_UP(entry->ndl, 60)); res.push_back(casprintf_loc(translate("gettextFromC", "NDL: %umin"), div_up(entry->ndl, 60)));
} }
if (entry->tts) if (entry->tts)
put_format_loc(b, translate("gettextFromC", "TTS: %umin\n"), DIV_UP(entry->tts, 60)); res.push_back(casprintf_loc(translate("gettextFromC", "TTS: %umin"), div_up(entry->tts, 60)));
if (entry->stopdepth_calc && entry->stoptime_calc) { if (entry->stopdepth_calc && entry->stoptime_calc) {
depthvalue = get_depth_units(entry->stopdepth_calc, NULL, &depth_unit); depthvalue = get_depth_units(entry->stopdepth_calc, NULL, &depth_unit);
put_format_loc(b, translate("gettextFromC", "Deco: %umin @ %.0f%s (calc)\n"), DIV_UP(entry->stoptime_calc, 60), res.push_back(casprintf_loc(translate("gettextFromC", "Deco: %umin @ %.0f%s (calc)"), div_up(entry->stoptime_calc, 60),
depthvalue, depth_unit); depthvalue, depth_unit));
} else if (entry->in_deco_calc) { } else if (entry->in_deco_calc) {
/* This means that we have no NDL left, /* This means that we have no NDL left,
* and we have no deco stop, * and we have no deco stop,
* so if we just accend to the surface slowly * so if we just accend to the surface slowly
* (ascent_mm_per_step / ascent_s_per_step) * (ascent_mm_per_step / ascent_s_per_step)
* everything will be ok. */ * everything will be ok. */
put_string(b, translate("gettextFromC", "In deco (calc)\n")); res.push_back(translate("gettextFromC", "In deco (calc)"));
} else if (prefs.calcndltts && entry->ndl_calc != 0) { } else if (prefs.calcndltts && entry->ndl_calc != 0) {
if(entry->ndl_calc < MAX_PROFILE_DECO) if(entry->ndl_calc < MAX_PROFILE_DECO)
put_format_loc(b, translate("gettextFromC", "NDL: %umin (calc)\n"), DIV_UP(entry->ndl_calc, 60)); res.push_back(casprintf_loc(translate("gettextFromC", "NDL: %umin (calc)"), div_up(entry->ndl_calc, 60)));
else else
put_string(b, translate("gettextFromC", "NDL: >2h (calc)\n")); res.push_back(translate("gettextFromC", "NDL: >2h (calc)"));
} }
if (entry->tts_calc) { if (entry->tts_calc) {
if (entry->tts_calc < MAX_PROFILE_DECO) if (entry->tts_calc < MAX_PROFILE_DECO)
put_format_loc(b, translate("gettextFromC", "TTS: %umin (calc)\n"), DIV_UP(entry->tts_calc, 60)); res.push_back(casprintf_loc(translate("gettextFromC", "TTS: %umin (calc)"), div_up(entry->tts_calc, 60)));
else else
put_string(b, translate("gettextFromC", "TTS: >2h (calc)\n")); res.push_back(translate("gettextFromC", "TTS: >2h (calc)"));
} }
if (entry->rbt) if (entry->rbt)
put_format_loc(b, translate("gettextFromC", "RBT: %umin\n"), DIV_UP(entry->rbt, 60)); res.push_back(casprintf_loc(translate("gettextFromC", "RBT: %umin"), div_up(entry->rbt, 60)));
if (prefs.decoinfo) { if (prefs.decoinfo) {
if (entry->current_gf > 0.0) if (entry->current_gf > 0.0)
put_format(b, translate("gettextFromC", "GF %d%%\n"), (int)(100.0 * entry->current_gf)); res.push_back(casprintf_loc(translate("gettextFromC", "GF %d%%"), (int)(100.0 * entry->current_gf)));
if (entry->surface_gf > 0.0) if (entry->surface_gf > 0.0)
put_format(b, translate("gettextFromC", "Surface GF %.0f%%\n"), entry->surface_gf); res.push_back(casprintf_loc(translate("gettextFromC", "Surface GF %.0f%%"), entry->surface_gf));
if (entry->ceiling) { if (entry->ceiling) {
depthvalue = get_depth_units(entry->ceiling, NULL, &depth_unit); depthvalue = get_depth_units(entry->ceiling, NULL, &depth_unit);
put_format_loc(b, translate("gettextFromC", "Calculated ceiling %.1f%s\n"), depthvalue, depth_unit); res.push_back(casprintf_loc(translate("gettextFromC", "Calculated ceiling %.1f%s"), depthvalue, depth_unit));
if (prefs.calcalltissues) { if (prefs.calcalltissues) {
int k; int k;
for (k = 0; k < 16; k++) { for (k = 0; k < 16; k++) {
if (entry->ceilings[k]) { if (entry->ceilings[k]) {
depthvalue = get_depth_units(entry->ceilings[k], NULL, &depth_unit); depthvalue = get_depth_units(entry->ceilings[k], NULL, &depth_unit);
put_format_loc(b, translate("gettextFromC", "Tissue %.0fmin: %.1f%s\n"), buehlmann_N2_t_halflife[k], depthvalue, depth_unit); res.push_back(casprintf_loc(translate("gettextFromC", "Tissue %.0fmin: %.1f%s"), buehlmann_N2_t_halflife[k], depthvalue, depth_unit));
} }
} }
} }
} }
} }
if (entry->icd_warning) if (entry->icd_warning)
put_format(b, "%s", translate("gettextFromC", "ICD in leading tissue\n")); res.push_back(translate("gettextFromC", "ICD in leading tissue"));
if (entry->heartbeat && prefs.hrgraph) if (entry->heartbeat && prefs.hrgraph)
put_format_loc(b, translate("gettextFromC", "heart rate: %d\n"), entry->heartbeat); res.push_back(casprintf_loc(translate("gettextFromC", "heart rate: %d"), entry->heartbeat));
if (entry->bearing >= 0) if (entry->bearing >= 0)
put_format_loc(b, translate("gettextFromC", "bearing: %d\n"), entry->bearing); res.push_back(casprintf_loc(translate("gettextFromC", "bearing: %d"), entry->bearing));
if (entry->running_sum) { if (entry->running_sum) {
depthvalue = get_depth_units(entry->running_sum / entry->sec, NULL, &depth_unit); depthvalue = get_depth_units(entry->running_sum / entry->sec, NULL, &depth_unit);
put_format_loc(b, translate("gettextFromC", "mean depth to here %.1f%s\n"), depthvalue, depth_unit); res.push_back(casprintf_loc(translate("gettextFromC", "mean depth to here %.1f%s"), depthvalue, depth_unit));
} }
strip_mb(b); return res;
} }
extern "C" int get_plot_details_new(const struct dive *d, const struct plot_info *pi, int time, struct membuffer *mb) std::pair<int, std::vector<std::string>> get_plot_details_new(const struct dive *d, const struct plot_info *pi, int time)
{ {
int i;
/* The two first and the two last plot entries do not have useful data */ /* The two first and the two last plot entries do not have useful data */
if (pi->nr <= 4) if (pi->nr <= 4)
return 0; return { 0, {} };
for (i = 2; i < pi->nr - 3; i++) {
if (pi->entry[i].sec >= time) // binary search for sample index
break; auto it = std::lower_bound(pi->entry + 2, pi->entry + pi->nr - 3, time,
} [] (const plot_data &d, int time)
plot_string(d, pi, i, mb); { return d.sec < time; });
return i; int idx = it - pi->entry;
auto strings = plot_string(d, pi, idx);
return std::make_pair(idx, strings);
} }
/* Compare two plot_data entries and writes the results into a string */ /* Compare two plot_data entries and writes the results into a set of strings */
extern "C" void compare_samples(const struct dive *d, const struct plot_info *pi, int idx1, int idx2, char *buf, int bufsize, bool sum) std::vector<std::string> compare_samples(const struct dive *d, const struct plot_info *pi, int idx1, int idx2, bool sum)
{ {
struct plot_data *start, *stop, *data; std::string space(" ");
const char *depth_unit, *pressure_unit, *vertical_speed_unit; const char *depth_unit, *pressure_unit, *vertical_speed_unit;
char *buf2 = (char *)malloc(bufsize);
int avg_speed, max_asc_speed, max_desc_speed;
int delta_depth, avg_depth, max_depth, min_depth;
int pressurevalue;
int last_sec, delta_time;
double depthvalue, speedvalue; double depthvalue, speedvalue;
if (bufsize > 0) std::vector<std::string> res;
buf[0] = '\0'; if (idx1 < 0 || idx2 < 0)
if (idx1 < 0 || idx2 < 0) { return res;
free(buf2);
return;
}
if (pi->entry[idx1].sec > pi->entry[idx2].sec) { if (pi->entry[idx1].sec > pi->entry[idx2].sec) {
int tmp = idx2; int tmp = idx2;
idx2 = idx1; idx2 = idx1;
idx1 = tmp; idx1 = tmp;
} else if (pi->entry[idx1].sec == pi->entry[idx2].sec) { } else if (pi->entry[idx1].sec == pi->entry[idx2].sec) {
free(buf2); return res;
return;
} }
start = pi->entry + idx1; struct plot_data *start = pi->entry + idx1;
stop = pi->entry + idx2; struct plot_data *stop = pi->entry + idx2;
avg_speed = 0; int avg_speed = 0;
max_asc_speed = 0; int max_asc_speed = 0;
max_desc_speed = 0; int max_desc_speed = 0;
delta_depth = abs(start->depth - stop->depth); int delta_depth = abs(start->depth - stop->depth);
delta_time = abs(start->sec - stop->sec); int delta_time = abs(start->sec - stop->sec);
avg_depth = 0; int avg_depth = 0;
max_depth = 0; int max_depth = 0;
min_depth = INT_MAX; int min_depth = INT_MAX;
last_sec = start->sec; int last_sec = start->sec;
volume_t cylinder_volume = { .mliter = 0, }; volume_t cylinder_volume = { .mliter = 0, };
int *start_pressures = (int *)calloc((size_t)pi->nr_cylinders, sizeof(int)); std::vector<int> start_pressures(pi->nr_cylinders, 0);
int *last_pressures = (int *)calloc((size_t)pi->nr_cylinders, sizeof(int)); std::vector<int> last_pressures(pi->nr_cylinders, 0);
int *bar_used = (int *)calloc((size_t)pi->nr_cylinders, sizeof(int)); std::vector<int> bar_used(pi->nr_cylinders, 0);
int *volumes_used = (int *)calloc((size_t)pi->nr_cylinders, sizeof(int)); std::vector<int> volumes_used(pi->nr_cylinders, 0);
bool *cylinder_is_used = (bool *)calloc((size_t)pi->nr_cylinders, sizeof(bool)); std::vector<char> cylinder_is_used(pi->nr_cylinders, false);
data = start; struct plot_data *data = start;
for (int i = idx1; i < idx2; ++i) { for (int i = idx1; i < idx2; ++i) {
data = pi->entry + i; data = pi->entry + i;
if (sum) if (sum)
@ -1625,47 +1615,38 @@ extern "C" void compare_samples(const struct dive *d, const struct plot_info *pi
last_sec = data->sec; last_sec = data->sec;
} }
free(start_pressures);
free(last_pressures);
avg_depth /= stop->sec - start->sec; avg_depth /= stop->sec - start->sec;
avg_speed /= stop->sec - start->sec; avg_speed /= stop->sec - start->sec;
snprintf_loc(buf, bufsize, translate("gettextFromC", "ΔT:%d:%02dmin"), delta_time / 60, delta_time % 60); std::string l = casprintf_loc(translate("gettextFromC", "ΔT:%d:%02dmin"), delta_time / 60, delta_time % 60);
memcpy(buf2, buf, bufsize);
depthvalue = get_depth_units(delta_depth, NULL, &depth_unit); depthvalue = get_depth_units(delta_depth, NULL, &depth_unit);
snprintf_loc(buf, bufsize, translate("gettextFromC", "%s ΔD:%.1f%s"), buf2, depthvalue, depth_unit); l += space + casprintf_loc(translate("gettextFromC", "ΔD:%.1f%s"), depthvalue, depth_unit);
memcpy(buf2, buf, bufsize);
depthvalue = get_depth_units(min_depth, NULL, &depth_unit); depthvalue = get_depth_units(min_depth, NULL, &depth_unit);
snprintf_loc(buf, bufsize, translate("gettextFromC", "%s ↓D:%.1f%s"), buf2, depthvalue, depth_unit); l += space + casprintf_loc(translate("gettextFromC", "↓D:%.1f%s"), depthvalue, depth_unit);
memcpy(buf2, buf, bufsize);
depthvalue = get_depth_units(max_depth, NULL, &depth_unit); depthvalue = get_depth_units(max_depth, NULL, &depth_unit);
snprintf_loc(buf, bufsize, translate("gettextFromC", "%s ↑D:%.1f%s"), buf2, depthvalue, depth_unit); l += space + casprintf_loc(translate("gettextFromC", "↑D:%.1f%s"), depthvalue, depth_unit);
memcpy(buf2, buf, bufsize);
depthvalue = get_depth_units(avg_depth, NULL, &depth_unit); depthvalue = get_depth_units(avg_depth, NULL, &depth_unit);
snprintf_loc(buf, bufsize, translate("gettextFromC", "%s øD:%.1f%s\n"), buf2, depthvalue, depth_unit); l += space + casprintf_loc(translate("gettextFromC", "øD:%.1f%s"), depthvalue, depth_unit);
memcpy(buf2, buf, bufsize); res.push_back(l);
speedvalue = get_vertical_speed_units(abs(max_desc_speed), NULL, &vertical_speed_unit); speedvalue = get_vertical_speed_units(abs(max_desc_speed), NULL, &vertical_speed_unit);
snprintf_loc(buf, bufsize, translate("gettextFromC", "%s ↓V:%.2f%s"), buf2, speedvalue, vertical_speed_unit); l = casprintf_loc(translate("gettextFromC", "↓V:%.2f%s"), speedvalue, vertical_speed_unit);
memcpy(buf2, buf, bufsize);
speedvalue = get_vertical_speed_units(abs(max_asc_speed), NULL, &vertical_speed_unit); speedvalue = get_vertical_speed_units(abs(max_asc_speed), NULL, &vertical_speed_unit);
snprintf_loc(buf, bufsize, translate("gettextFromC", "%s ↑V:%.2f%s"), buf2, speedvalue, vertical_speed_unit); l += space + casprintf_loc(translate("gettextFromC", "↑V:%.2f%s"), speedvalue, vertical_speed_unit);
memcpy(buf2, buf, bufsize);
speedvalue = get_vertical_speed_units(abs(avg_speed), NULL, &vertical_speed_unit); speedvalue = get_vertical_speed_units(abs(avg_speed), NULL, &vertical_speed_unit);
snprintf_loc(buf, bufsize, translate("gettextFromC", "%s øV:%.2f%s"), buf2, speedvalue, vertical_speed_unit); l += space + casprintf_loc(translate("gettextFromC", "øV:%.2f%s"), speedvalue, vertical_speed_unit);
int total_bar_used = 0; int total_bar_used = 0;
int total_volume_used = 0; int total_volume_used = 0;
bool cylindersizes_are_identical = true; bool cylindersizes_are_identical = true;
bool sac_is_determinable = true; bool sac_is_determinable = true;
for (int cylinder_index = 0; cylinder_index < pi->nr_cylinders; cylinder_index++) for (int cylinder_index = 0; cylinder_index < pi->nr_cylinders; cylinder_index++) {
if (cylinder_is_used[cylinder_index]) { if (cylinder_is_used[cylinder_index]) {
total_bar_used += bar_used[cylinder_index]; total_bar_used += bar_used[cylinder_index];
total_volume_used += volumes_used[cylinder_index]; total_volume_used += volumes_used[cylinder_index];
@ -1681,20 +1662,16 @@ extern "C" void compare_samples(const struct dive *d, const struct plot_info *pi
sac_is_determinable = false; sac_is_determinable = false;
} }
} }
free(bar_used); }
free(volumes_used);
free(cylinder_is_used);
// No point printing 'bar used' if we know it's meaningless because cylinders of different size were used // No point printing 'bar used' if we know it's meaningless because cylinders of different size were used
if (cylindersizes_are_identical && total_bar_used) { if (cylindersizes_are_identical && total_bar_used) {
pressurevalue = get_pressure_units(total_bar_used, &pressure_unit); int pressurevalue = get_pressure_units(total_bar_used, &pressure_unit);
memcpy(buf2, buf, bufsize); l += space + casprintf_loc(translate("gettextFromC", "ΔP:%d%s"), pressurevalue, pressure_unit);
snprintf_loc(buf, bufsize, translate("gettextFromC", "%s ΔP:%d%s"), buf2, pressurevalue, pressure_unit);
} }
// We can't calculate the SAC if the volume for some of the cylinders used is unknown // We can't calculate the SAC if the volume for some of the cylinders used is unknown
if (sac_is_determinable && total_volume_used) { if (sac_is_determinable && total_volume_used) {
double volume_value;
int volume_precision; int volume_precision;
const char *volume_unit; const char *volume_unit;
@ -1703,10 +1680,10 @@ extern "C" void compare_samples(const struct dive *d, const struct plot_info *pi
/* milliliters per minute */ /* milliliters per minute */
int sac = lrint(total_volume_used / atm * 60 / delta_time); int sac = lrint(total_volume_used / atm * 60 / delta_time);
memcpy(buf2, buf, bufsize); double volume_value = get_volume_units(sac, &volume_precision, &volume_unit);
volume_value = get_volume_units(sac, &volume_precision, &volume_unit); l += space + casprintf_loc(translate("gettextFromC", "SAC:%.*f%s/min"), volume_precision, volume_value, volume_unit);
snprintf_loc(buf, bufsize, translate("gettextFromC", "%s SAC:%.*f%s/min"), buf2, volume_precision, volume_value, volume_unit);
} }
res.push_back(l);
free(buf2); return res;
} }

View file

@ -97,11 +97,9 @@ struct plot_info {
#define AMB_PERCENTAGE 50.0 #define AMB_PERCENTAGE 50.0
extern void compare_samples(const struct dive *d, const struct plot_info *pi, int idx1, int idx2, char *buf, int bufsize, bool sum);
extern void init_plot_info(struct plot_info *pi); extern void init_plot_info(struct plot_info *pi);
/* when planner_dc is non-null, this is called in planner mode. */ /* when planner_dc is non-null, this is called in planner mode. */
extern void create_plot_info_new(const struct dive *dive, const struct divecomputer *dc, struct plot_info *pi, const struct deco_state *planner_ds); extern void create_plot_info_new(const struct dive *dive, const struct divecomputer *dc, struct plot_info *pi, const struct deco_state *planner_ds);
extern int get_plot_details_new(const struct dive *d, const struct plot_info *pi, int time, struct membuffer *);
extern void free_plot_info_data(struct plot_info *pi); extern void free_plot_info_data(struct plot_info *pi);
/* /*
@ -145,5 +143,13 @@ static inline int get_plot_pressure(const struct plot_info *pi, int idx, int cyl
#ifdef __cplusplus #ifdef __cplusplus
} }
// C++ only formatting functions
#include <string>
#include <vector>
// Returns index of sample and array of strings describing the dive details at given time
std::pair<int, std::vector<std::string>> get_plot_details_new(const struct dive *d, const struct plot_info *pi, int time);
std::vector<std::string> compare_samples(const struct dive *d, const struct plot_info *pi, int idx1, int idx2, bool sum);
#endif #endif
#endif // PROFILE_H #endif // PROFILE_H

View file

@ -222,8 +222,6 @@ void ToolTipItem::setTimeAxis(DiveCartesianAxis *axis)
void ToolTipItem::refresh(const dive *d, const QPointF &pos, bool inPlanner) void ToolTipItem::refresh(const dive *d, const QPointF &pos, bool inPlanner)
{ {
struct membufferpp mb;
if(refreshTime.elapsed() < 40) if(refreshTime.elapsed() < 40)
return; return;
refreshTime.start(); refreshTime.start();
@ -233,7 +231,7 @@ void ToolTipItem::refresh(const dive *d, const QPointF &pos, bool inPlanner)
lastTime = time; lastTime = time;
clear(); clear();
int idx = get_plot_details_new(d, &pInfo, time, &mb); auto [idx, lines] = get_plot_details_new(d, &pInfo, time);
tissues.fill(); tissues.fill();
painter.setPen(QColor(0, 0, 0, 0)); painter.setPen(QColor(0, 0, 0, 0));
@ -253,7 +251,13 @@ void ToolTipItem::refresh(const dive *d, const QPointF &pos, bool inPlanner)
painter.setPen(QColor(0, 0, 0, 127)); painter.setPen(QColor(0, 0, 0, 127));
for (int i = 0; i < 16; i++) for (int i = 0; i < 16; i++)
painter.drawLine(i, 60, i, 60 - entry->percentages[i] / 2); painter.drawLine(i, 60, i, 60 - entry->percentages[i] / 2);
entryToolTip.second->setPlainText(QString::fromUtf8(mb.buffer, mb.len)); QString text;
for (const std::string &s: lines) {
if (!text.isEmpty())
text += '\n';
text += QString::fromStdString(s);
}
entryToolTip.second->setPlainText(text);
} }
entryToolTip.first->setPixmap(tissues); entryToolTip.first->setPixmap(tissues);

View file

@ -93,7 +93,6 @@ void RulerItem2::settingsChanged(bool value)
void RulerItem2::recalculate() void RulerItem2::recalculate()
{ {
char buffer[500];
QPointF tmp; QPointF tmp;
QFont font; QFont font;
QFontMetrics fm(font); QFontMetrics fm(font);
@ -112,8 +111,13 @@ void RulerItem2::recalculate()
} }
QLineF line(startPoint, endPoint); QLineF line(startPoint, endPoint);
setLine(line); setLine(line);
compare_samples(dive, pInfo, source->idx, dest->idx, buffer, 500, 1);
text = QString(buffer); QString text;
for (const std::string &s: compare_samples(dive, pInfo, source->idx, dest->idx, 1)) {
if (!text.isEmpty())
text += '\n';
text += QString::fromStdString(s);
}
// draw text // draw text
QGraphicsView *view = scene()->views().first(); QGraphicsView *view = scene()->views().first();