profile: clip DiveProfileItems

Avoid "overshooting" of the profile items by linearly clipping
the first and last segment to the boundaries of the time-axis.

Sadly, quite a lot of code, because every profile item is
slightly different.

In particular the pressure-segment handling was rewritten.
It now stores the begin and end of each segment to draw
the appropriate text items.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
This commit is contained in:
Berthold Stoeger 2021-10-09 14:56:12 +02:00 committed by Dirk Hohndel
parent 56f6e5051f
commit 073059f5ab
2 changed files with 216 additions and 116 deletions

View file

@ -26,6 +26,44 @@ void AbstractProfilePolygonItem::clear()
texts.clear(); texts.clear();
} }
static std::pair<double,double> clip(double x1, double y1, double x2, double y2, double x)
{
double rel = fabs(x2 - x1) > 1e-10 ? (x - x1) / (x2 - x1) : 0.5;
return { x, (y2 - y1) * rel + y1 };
}
void AbstractProfilePolygonItem::clipStart(double &x, double &y, double next_x, double next_y) const
{
if (x < hAxis.minimum())
std::tie(x, y) = clip(x, y, next_x, next_y, hAxis.minimum());
}
void AbstractProfilePolygonItem::clipStop(double &x, double &y, double prev_x, double prev_y) const
{
if (x > hAxis.maximum())
std::tie(x, y) = clip(prev_x, prev_y, x, y, hAxis.maximum());
}
std::pair<double, double> AbstractProfilePolygonItem::getPoint(int i) const
{
double x = dataModel.index(i, hDataColumn).data().toReal();
double y = dataModel.index(i, vDataColumn).data().toReal();
// Do clipping of first and last value
if (i == from && i < to) {
double next_x = dataModel.index(i+1, hDataColumn).data().toReal();
double next_y = dataModel.index(i+1, vDataColumn).data().toReal();
clipStart(x, y, next_x, next_y);
}
if (i == to - 1 && i > 0) {
double prev_x = dataModel.index(i-1, hDataColumn).data().toReal();
double prev_y = dataModel.index(i-1, vDataColumn).data().toReal();
clipStop(x, y, prev_x, prev_y);
}
return { x, y };
}
void AbstractProfilePolygonItem::makePolygon(int fromIn, int toIn) void AbstractProfilePolygonItem::makePolygon(int fromIn, int toIn)
{ {
from = fromIn; from = fromIn;
@ -38,8 +76,8 @@ void AbstractProfilePolygonItem::makePolygon(int fromIn, int toIn)
// to our coordinates, store. no painting is done here. // to our coordinates, store. no painting is done here.
QPolygonF poly; QPolygonF poly;
for (int i = from; i < to; i++) { for (int i = from; i < to; i++) {
double horizontalValue = dataModel.index(i, hDataColumn).data().toReal(); auto [horizontalValue, verticalValue] = getPoint(i);
double verticalValue = dataModel.index(i, vDataColumn).data().toReal();
if (i == from) { if (i == from) {
QPointF point(hAxis.posAtValue(horizontalValue), vAxis.posAtValue(0.0)); QPointF point(hAxis.posAtValue(horizontalValue), vAxis.posAtValue(0.0));
poly.append(point); poly.append(point);
@ -175,7 +213,7 @@ void DiveHeartrateItem::replot(const dive *, int fromIn, int toIn, bool)
from = fromIn; from = fromIn;
to = toIn; to = toIn;
int last = -300, last_printed_hr = 0, sec = 0; int last = -300, last_printed_hr = 0;
struct sec_hr { struct sec_hr {
int sec; int sec;
int hr; int hr;
@ -187,11 +225,12 @@ void DiveHeartrateItem::replot(const dive *, int fromIn, int toIn, bool)
// Ignore empty values. a heart rate of 0 would be a bad sign. // Ignore empty values. a heart rate of 0 would be a bad sign.
QPolygonF poly; QPolygonF poly;
for (int i = from; i < to; i++) { for (int i = from; i < to; i++) {
int hr = dataModel.index(i, vDataColumn).data().toInt(); auto [sec_double, hr_double] = getPoint(i);
int hr = lrint(hr_double);
if (!hr) if (!hr)
continue; continue;
sec = dataModel.index(i, hDataColumn).data().toInt(); int sec = lrint(sec_double);
QPointF point(hAxis.posAtValue(sec), vAxis.posAtValue(hr)); QPointF point(hAxis.posAtValue(sec_double), vAxis.posAtValue(hr_double));
poly.append(point); poly.append(point);
if (hr == hist[2].hr) if (hr == hist[2].hr)
// same as last one, no point in looking at printing // same as last one, no point in looking at printing
@ -263,7 +302,7 @@ void DiveTemperatureItem::replot(const dive *, int fromIn, int toIn, bool)
from = fromIn; from = fromIn;
to = toIn; to = toIn;
int last = -300, last_printed_temp = 0, sec = 0, last_valid_temp = 0; double last = -300.0, last_printed_temp = 0.0, last_valid_temp = 0.0, sec = 0.0;
std::vector<std::pair<int, int>> textItems; std::vector<std::pair<int, int>> textItems;
qDeleteAll(texts); qDeleteAll(texts);
@ -271,35 +310,34 @@ void DiveTemperatureItem::replot(const dive *, int fromIn, int toIn, bool)
// Ignore empty values. things do not look good with '0' as temperature in kelvin... // Ignore empty values. things do not look good with '0' as temperature in kelvin...
QPolygonF poly; QPolygonF poly;
for (int i = from; i < to; i++) { for (int i = from; i < to; i++) {
int mkelvin = dataModel.index(i, vDataColumn).data().toInt(); auto [sec, mkelvin] = getPoint(i);
if (!mkelvin) if (mkelvin < 1.0)
continue; continue;
last_valid_temp = mkelvin;
sec = dataModel.index(i, hDataColumn).data().toInt();
QPointF point(hAxis.posAtValue(sec), vAxis.posAtValue(mkelvin)); QPointF point(hAxis.posAtValue(sec), vAxis.posAtValue(mkelvin));
poly.append(point); poly.append(point);
last_valid_temp = sec;
/* don't print a temperature /* don't print a temperature
* if it's been less than 5min and less than a 2K change OR * if it's been less than 5min and less than a 2K change OR
* if it's been less than 2min OR if the change from the * if it's been less than 2min OR if the change from the
* last print is less than .4K (and therefore less than 1F) */ * last print is less than .4K (and therefore less than 1F) */
if (((sec < last + 300) && (abs(mkelvin - last_printed_temp) < 2000)) || if (((sec < last + 300.0) && (fabs(mkelvin - last_printed_temp) < 2000.0)) ||
(sec < last + 120) || (sec < last + 120.0) ||
(abs(mkelvin - last_printed_temp) < 400)) (fabs(mkelvin - last_printed_temp) < 400.0))
continue; continue;
last = sec; last = sec;
if (mkelvin > 200000) if (mkelvin > 200000.0)
textItems.push_back({ sec, mkelvin }); textItems.push_back({ static_cast<int>(sec), static_cast<int>(mkelvin) });
last_printed_temp = mkelvin; last_printed_temp = mkelvin;
} }
setPolygon(poly); setPolygon(poly);
/* it would be nice to print the end temperature, if it's /* print the end temperature, if it's different or if the
* different or if the last temperature print has been more * last temperature print has been more than a quarter of the
* than a quarter of the dive back */ * dive back */
if (last_valid_temp > 200000 && if (last_valid_temp > 200000.0 &&
((abs(last_valid_temp - last_printed_temp) > 500) || ((double)last / (double)sec < 0.75))) { ((fabs(last_valid_temp - last_printed_temp) > 500.0) || (last < 0.75 * sec))) {
textItems.push_back({ sec, last_valid_temp }); textItems.push_back({ static_cast<int>(sec), static_cast<int>(last_valid_temp) });
} }
for (size_t i = 0; i < textItems.size(); ++i) { for (size_t i = 0; i < textItems.size(); ++i) {
@ -340,7 +378,30 @@ DiveMeanDepthItem::DiveMeanDepthItem(const DivePlotDataModel &model, const DiveC
pen.setCosmetic(true); pen.setCosmetic(true);
pen.setWidth(2); pen.setWidth(2);
setPen(pen); setPen(pen);
lastRunningSum = 0.0; }
// Apparently, there can be samples without mean depth? If not, remove these functions.
std::pair<double,double> DiveMeanDepthItem::getMeanDepth(int i) const
{
for ( ; i >= 0; --i) {
const plot_data &entry = dataModel.data().entry[i];
if (entry.running_sum > 0)
return { static_cast<double>(entry.sec),
static_cast<double>(entry.running_sum) / entry.sec };
}
return { 0.0, 0.0 };
}
std::pair<double,double> DiveMeanDepthItem::getNextMeanDepth(int first) const
{
int last = dataModel.data().nr;
for (int i = first + 1; i < last; ++i) {
const plot_data &entry = dataModel.data().entry[i];
if (entry.running_sum > 0)
return { static_cast<double>(entry.sec),
static_cast<double>(entry.running_sum) / entry.sec };
}
return getMeanDepth(first);
} }
void DiveMeanDepthItem::replot(const dive *, int fromIn, int toIn, bool) void DiveMeanDepthItem::replot(const dive *, int fromIn, int toIn, bool)
@ -348,22 +409,32 @@ void DiveMeanDepthItem::replot(const dive *, int fromIn, int toIn, bool)
from = fromIn; from = fromIn;
to = toIn; to = toIn;
double meandepthvalue = 0.0; double prevSec = 0.0, prevMeanDepth = 0.0;
QPolygonF poly; QPolygonF poly;
plot_data *entry = dataModel.data().entry + from; for (int i = from; i < to; i++) {
for (int i = from; i < to; i++, entry++) { auto [sec, meanDepth] = getMeanDepth(i);
// Ignore empty values // Ignore empty values
if (entry->running_sum == 0 || entry->sec == 0) if (meanDepth == 0)
continue; continue;
if (i == from && i < to) {
auto [sec2, meanDepth2] = getNextMeanDepth(i);
if (meanDepth2 > 0.0)
clipStart(sec, meanDepth, sec2, meanDepth2);
}
if (i == to - 1 && i > 0)
clipStop(sec, meanDepth, prevSec, prevMeanDepth);
meandepthvalue = entry->running_sum / entry->sec;
QPointF point(hAxis.posAtValue(entry->sec), vAxis.posAtValue(meandepthvalue)); QPointF point(hAxis.posAtValue(sec), vAxis.posAtValue(meanDepth));
poly.append(point); poly.append(point);
prevSec = sec;
prevMeanDepth = meanDepth;
} }
lastRunningSum = meandepthvalue;
setPolygon(poly); setPolygon(poly);
createTextItem(); if (prevMeanDepth > 0.0)
createTextItem(prevSec, prevMeanDepth);
} }
@ -377,15 +448,13 @@ void DiveMeanDepthItem::paint(QPainter *painter, const QStyleOptionGraphicsItem*
painter->restore(); painter->restore();
} }
void DiveMeanDepthItem::createTextItem() void DiveMeanDepthItem::createTextItem(double lastSec, double lastMeanDepth)
{ {
plot_data *entry = dataModel.data().entry;
int sec = to > 0 ? entry[to-1].sec : 0;
qDeleteAll(texts); qDeleteAll(texts);
texts.clear(); texts.clear();
DiveTextItem *text = new DiveTextItem(dpr, 0.8, Qt::AlignRight | Qt::AlignTop, this); DiveTextItem *text = new DiveTextItem(dpr, 0.8, Qt::AlignRight | Qt::AlignTop, this);
text->set(get_depth_string(lrint(lastRunningSum), true), getColor(TEMP_TEXT)); text->set(get_depth_string(lrint(lastMeanDepth), true), getColor(TEMP_TEXT));
text->setPos(QPointF(hAxis.posAtValue(sec) + 1, vAxis.posAtValue(lastRunningSum))); text->setPos(QPointF(hAxis.posAtValue(lastSec) + dpr, vAxis.posAtValue(lastMeanDepth)));
texts.append(text); texts.append(text);
} }
@ -396,21 +465,37 @@ void DiveGasPressureItem::replot(const dive *d, int fromIn, int toIn, bool in_pl
const struct plot_info *pInfo = &dataModel.data(); const struct plot_info *pInfo = &dataModel.data();
std::vector<int> plotted_cyl(pInfo->nr_cylinders, false); std::vector<int> plotted_cyl(pInfo->nr_cylinders, false);
std::vector<int> last_plotted(pInfo->nr_cylinders, 0); std::vector<double> last_plotted(pInfo->nr_cylinders, 0.0);
std::vector<std::vector<Entry>> poly(pInfo->nr_cylinders); std::vector<Segment> act_segments(pInfo->nr_cylinders);
QPolygonF boundingPoly; QPolygonF boundingPoly;
polygons.clear(); segments.clear();
for (int i = from; i < to; i++) { for (int i = from; i < to; i++) {
const struct plot_data *entry = pInfo->entry + i; const struct plot_data *entry = pInfo->entry + i;
for (int cyl = 0; cyl < pInfo->nr_cylinders; cyl++) { for (int cyl = 0; cyl < pInfo->nr_cylinders; cyl++) {
int mbar = get_plot_pressure(pInfo, i, cyl); double mbar = static_cast<double>(get_plot_pressure(pInfo, i, cyl));
int time = entry->sec; double time = static_cast<double>(entry->sec);
if (!mbar) if (mbar < 1.0)
continue; continue;
if (i == from && i < to - 1) {
double mbar2 = static_cast<double>(get_plot_pressure(pInfo, i+1, cyl));
double time2 = static_cast<double>(entry[1].sec);
if (mbar2 < 1.0)
continue;
clipStart(time, mbar, time2, mbar2);
}
if (i == to - 1 && i > from) {
double mbar2 = static_cast<double>(get_plot_pressure(pInfo, i-1, cyl));
double time2 = static_cast<double>(entry[-1].sec);
if (mbar2 < 1.0)
continue;
clipStop(time, mbar, time2, mbar2);
}
QPointF point(hAxis.posAtValue(time), vAxis.posAtValue(mbar)); QPointF point(hAxis.posAtValue(time), vAxis.posAtValue(mbar));
boundingPoly.push_back(point); boundingPoly.push_back(point);
@ -421,46 +506,50 @@ void DiveGasPressureItem::replot(const dive *d, int fromIn, int toIn, bool in_pl
else else
color = MED_GRAY_HIGH_TRANS; color = MED_GRAY_HIGH_TRANS;
} else { } else {
if (mbar < 0) if (mbar < 0.0)
color = MAGENTA; color = MAGENTA;
else else
color = getPressureColor(entry->density); color = getPressureColor(entry->density);
} }
if (plotted_cyl[cyl]) { if (!act_segments[cyl].polygon.empty()) {
/* Have we used this cylinder in the last two minutes? Continue */ /* Have we used this cylinder in the last two minutes? Continue */
if (time - last_plotted[cyl] <= 2*60) { if (time - act_segments[cyl].last.time <= 2*60) {
poly[cyl].push_back({ point, color }); act_segments[cyl].polygon.push_back({ point, color });
last_plotted[cyl] = time; act_segments[cyl].last.time = time;
act_segments[cyl].last.pressure = mbar;
continue; continue;
} }
/* Finish the previous one, start a new one */ /* Finish the previous one, start a new one */
polygons.push_back(std::move(poly[cyl])); act_segments[cyl].cyl = cyl;
poly[cyl].clear(); segments.push_back(std::move(act_segments[cyl]));
act_segments[cyl] = Segment();
} }
plotted_cyl[cyl] = true; plotted_cyl[cyl] = true;
last_plotted[cyl] = time; act_segments[cyl].polygon.push_back({ point, color });
poly[cyl].push_back({ point, color }); act_segments[cyl].last.time = time;
act_segments[cyl].last.pressure = mbar;
if (act_segments[cyl].first.pressure == 0.0) {
act_segments[cyl].first.time = time;
act_segments[cyl].first.pressure = mbar;
}
} }
} }
for (int cyl = 0; cyl < pInfo->nr_cylinders; cyl++) { for (int cyl = 0; cyl < pInfo->nr_cylinders; cyl++) {
if (!plotted_cyl[cyl]) if (act_segments[cyl].polygon.empty())
continue; continue;
polygons.push_back(poly[cyl]); act_segments[cyl].cyl = cyl;
segments.push_back(std::move(act_segments[cyl]));
} }
setPolygon(boundingPoly); setPolygon(boundingPoly);
qDeleteAll(texts); qDeleteAll(texts);
texts.clear(); texts.clear();
std::vector<int> seen_cyl(pInfo->nr_cylinders, false); // These are offset values used to print the gas labels and pressures on a
std::vector<int> last_pressure(pInfo->nr_cylinders, 0);
std::vector<int> last_time(pInfo->nr_cylinders, 0);
// These are offset values used to print the gas lables and pressures on a
// dive profile at appropriate Y-coordinates. We alternate aligning the // dive profile at appropriate Y-coordinates. We alternate aligning the
// label and the gas pressure above and under the pressure line. // label and the gas pressure above and under the pressure line.
// The values are historical, and we could try to pick the over/under // The values are historical, and we could try to pick the over/under
@ -473,54 +562,35 @@ void DiveGasPressureItem::replot(const dive *d, int fromIn, int toIn, bool in_pl
double labelHeight = DiveTextItem::fontHeight(dpr, 1.0); double labelHeight = DiveTextItem::fontHeight(dpr, 1.0);
for (int i = from; i < to; i++) { for (const Segment &segment: segments) {
const struct plot_data *entry = pInfo->entry + i; // Magic Y offset depending on whether we're aliging
// the top of the text or the bottom of the text to
// the pressure line.
double value_y_offset = -0.5 * dpr;
double label_y_offset = alignVar & Qt::AlignTop ? labelHeight : -labelHeight;
gasmix gas = get_cylinder(d, segment.cyl)->gasmix;
plotPressureValue(segment.first.pressure, segment.first.time, alignVar, value_y_offset);
plotGasValue(segment.first.pressure, segment.first.time, gas, alignVar, label_y_offset);
for (int cyl = 0; cyl < pInfo->nr_cylinders; cyl++) { // For each cylinder, on right hand side of the curve, write cylinder pressure
int mbar = get_plot_pressure(pInfo, i, cyl); plotPressureValue(segment.last.pressure, segment.last.time, alignVar | Qt::AlignLeft, value_y_offset);
if (!mbar) /* Alternate alignment as we see cylinder use.. */
continue; alignVar ^= Qt::AlignTop | Qt::AlignBottom;
if (!seen_cyl[cyl]) {
// Magic Y offset depending on whether we're aliging
// the top of the text or the bottom of the text to
// the pressure line.
double value_y_offset = -0.5;
double label_y_offset = alignVar & Qt::AlignTop ? labelHeight : -labelHeight;
plotPressureValue(mbar, entry->sec, alignVar, value_y_offset);
plotGasValue(mbar, entry->sec, get_cylinder(d, cyl)->gasmix, alignVar, label_y_offset);
seen_cyl[cyl] = true;
/* Alternate alignment as we see cylinder use.. */
align[cyl] = alignVar;
alignVar ^= Qt::AlignTop | Qt::AlignBottom;
}
last_pressure[cyl] = mbar;
last_time[cyl] = entry->sec;
}
}
// For each cylinder, on right hand side of profile, write cylinder pressure
for (int cyl = 0; cyl < pInfo->nr_cylinders; cyl++) {
if (last_time[cyl]) {
double value_y_offset = -0.5;
plotPressureValue(last_pressure[cyl], last_time[cyl], align[cyl] | Qt::AlignLeft, value_y_offset);
}
} }
} }
void DiveGasPressureItem::plotPressureValue(int mbar, int sec, QFlags<Qt::AlignmentFlag> align, double pressure_offset) void DiveGasPressureItem::plotPressureValue(double mbar, double sec, QFlags<Qt::AlignmentFlag> align, double pressure_offset)
{ {
const char *unit; const char *unit;
int pressure = get_pressure_units(mbar, &unit); int pressure = get_pressure_units(lrint(mbar), &unit);
DiveTextItem *text = new DiveTextItem(dpr, 1.0, align, this); DiveTextItem *text = new DiveTextItem(dpr, 1.0, align, this);
text->set(QString("%1%2").arg(pressure).arg(unit), getColor(PRESSURE_TEXT)); text->set(QString("%1%2").arg(pressure).arg(unit), getColor(PRESSURE_TEXT));
text->setPos(hAxis.posAtValue(sec), vAxis.posAtValue(mbar) + pressure_offset); text->setPos(hAxis.posAtValue(sec), vAxis.posAtValue(mbar) + pressure_offset);
texts.push_back(text); texts.push_back(text);
} }
void DiveGasPressureItem::plotGasValue(int mbar, int sec, struct gasmix gasmix, QFlags<Qt::AlignmentFlag> align, double gasname_offset) void DiveGasPressureItem::plotGasValue(double mbar, double sec, struct gasmix gasmix, QFlags<Qt::AlignmentFlag> align, double gasname_offset)
{ {
QString gas = get_gas_string(gasmix); QString gas = get_gas_string(gasmix);
DiveTextItem *text = new DiveTextItem(dpr, 1.0, align, this); DiveTextItem *text = new DiveTextItem(dpr, 1.0, align, this);
@ -537,11 +607,11 @@ void DiveGasPressureItem::paint(QPainter *painter, const QStyleOptionGraphicsIte
pen.setCosmetic(true); pen.setCosmetic(true);
pen.setWidth(2); pen.setWidth(2);
painter->save(); painter->save();
for (const std::vector<Entry> &poly: polygons) { for (const Segment &segment: segments) {
for (size_t i = 1; i < poly.size(); i++) { for (size_t i = 1; i < segment.polygon.size(); i++) {
pen.setBrush(poly[i].col); pen.setBrush(segment.polygon[i].col);
painter->setPen(pen); painter->setPen(pen);
painter->drawLine(poly[i - 1].pos, poly[i].pos); painter->drawLine(segment.polygon[i - 1].pos, segment.polygon[i].pos);
} }
} }
painter->restore(); painter->restore();
@ -583,6 +653,27 @@ DiveReportedCeiling::DiveReportedCeiling(const DivePlotDataModel &model, const D
{ {
} }
std::pair<double,double> DiveReportedCeiling::getTimeValue(int i) const
{
const plot_data &entry = dataModel.data().entry[i];
int value = entry.in_deco && entry.stopdepth ? std::min(entry.stopdepth, entry.depth) : 0;
return { static_cast<double>(entry.sec), static_cast<double>(value) };
}
std::pair<double, double> DiveReportedCeiling::getPoint(int i) const
{
auto [x,y] = getTimeValue(i);
if (i == from && i < to) {
auto [next_x, next_y] = getTimeValue(i + 1);
clipStart(x, y, next_x, next_y);
}
if (i == to - 1 && i > 0) {
auto [prev_x, prev_y] = getTimeValue(i - 1);
clipStop(x, y, prev_x, prev_y);
}
return { x, y };
}
void DiveReportedCeiling::replot(const dive *, int fromIn, int toIn, bool) void DiveReportedCeiling::replot(const dive *, int fromIn, int toIn, bool)
{ {
from = fromIn; from = fromIn;
@ -590,14 +681,12 @@ void DiveReportedCeiling::replot(const dive *, int fromIn, int toIn, bool)
QPolygonF p; QPolygonF p;
for (int i = from; i < to; i++) { for (int i = from; i < to; i++) {
const plot_data &entry = dataModel.data().entry[i]; auto [sec, value] = getPoint(i);
if (i == from) if (i == from)
p.append(QPointF(hAxis.posAtValue(entry.sec), vAxis.posAtValue(0))); p.append(QPointF(hAxis.posAtValue(sec), vAxis.posAtValue(0.0)));
if (entry.in_deco && entry.stopdepth) { p.append(QPointF(hAxis.posAtValue(sec), vAxis.posAtValue(value)));
p.append(QPointF(hAxis.posAtValue(entry.sec), vAxis.posAtValue(std::min(entry.stopdepth, entry.depth)))); if (i == to - 1)
} else { p.append(QPointF(hAxis.posAtValue(sec), vAxis.posAtValue(0)));
p.append(QPointF(hAxis.posAtValue(entry.sec), vAxis.posAtValue(0)));
}
} }
setPolygon(p); setPolygon(p);
QLinearGradient pat(0, p.boundingRect().top(), 0, p.boundingRect().bottom()); QLinearGradient pat(0, p.boundingRect().top(), 0, p.boundingRect().bottom());
@ -625,7 +714,6 @@ void PartialPressureGasItem::replot(const dive *, int fromIn, int toIn, bool)
from = fromIn; from = fromIn;
to = toIn; to = toIn;
plot_data *entry = dataModel.data().entry + from;
QPolygonF poly; QPolygonF poly;
QPolygonF alertpoly; QPolygonF alertpoly;
alertPolygons.clear(); alertPolygons.clear();
@ -636,9 +724,8 @@ void PartialPressureGasItem::replot(const dive *, int fromIn, int toIn, bool)
if (thresholdPtrMin) if (thresholdPtrMin)
threshold_min = *thresholdPtrMin; threshold_min = *thresholdPtrMin;
bool inAlertFragment = false; bool inAlertFragment = false;
for (int i = from; i < to; i++, entry++) { for (int i = from; i < to; i++) {
double value = dataModel.index(i, vDataColumn).data().toDouble(); auto [time, value] = getPoint(i);
int time = dataModel.index(i, hDataColumn).data().toInt();
QPointF point(hAxis.posAtValue(time), vAxis.posAtValue(value)); QPointF point(hAxis.posAtValue(time), vAxis.posAtValue(value));
poly.push_back(point); poly.push_back(point);
if (thresholdPtrMax && value >= threshold_max) { if (thresholdPtrMax && value >= threshold_max) {
@ -664,9 +751,6 @@ void PartialPressureGasItem::replot(const dive *, int fromIn, int toIn, bool)
} }
} }
setPolygon(poly); setPolygon(poly);
/*
createPPLegend(trUtf8("pN₂"), getColor(PN2), legendPos);
*/
} }
void PartialPressureGasItem::paint(QPainter *painter, const QStyleOptionGraphicsItem*, QWidget*) void PartialPressureGasItem::paint(QPainter *painter, const QStyleOptionGraphicsItem*, QWidget*)

View file

@ -40,6 +40,9 @@ public:
protected: protected:
void makePolygon(int from, int to); void makePolygon(int from, int to);
void clipStart(double &x, double &y, double next_x, double next_y) const;
void clipStop(double &x, double &y, double prev_x, double prev_y) const;
std::pair<double, double> getPoint(int i) const;
const DiveCartesianAxis &hAxis; const DiveCartesianAxis &hAxis;
const DiveCartesianAxis &vAxis; const DiveCartesianAxis &vAxis;
const DivePlotDataModel &dataModel; const DivePlotDataModel &dataModel;
@ -74,8 +77,9 @@ public:
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = 0) override; void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = 0) override;
private: private:
void createTextItem(); void createTextItem(double lastSec, double lastMeanDepth);
double lastRunningSum; std::pair<double,double> getMeanDepth(int i) const;
std::pair<double,double> getNextMeanDepth(int i) const;
QString visibilityKey; QString visibilityKey;
}; };
@ -111,13 +115,22 @@ public:
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = 0) override; void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = 0) override;
private: private:
void plotPressureValue(int mbar, int sec, QFlags<Qt::AlignmentFlag> align, double offset); void plotPressureValue(double mbar, double sec, QFlags<Qt::AlignmentFlag> align, double offset);
void plotGasValue(int mbar, int sec, struct gasmix gasmix, QFlags<Qt::AlignmentFlag> align, double offset); void plotGasValue(double mbar, double sec, struct gasmix gasmix, QFlags<Qt::AlignmentFlag> align, double offset);
struct PressureEntry {
double time = 0.0;
double pressure = 0.0;
};
struct Entry { struct Entry {
QPointF pos; QPointF pos;
QColor col; QColor col;
}; };
std::vector<std::vector<Entry>> polygons; struct Segment {
int cyl;
std::vector<Entry> polygon;
PressureEntry first, last;
};
std::vector<Segment> segments;
}; };
class DiveCalculatedCeiling : public AbstractProfilePolygonItem { class DiveCalculatedCeiling : public AbstractProfilePolygonItem {
@ -137,6 +150,9 @@ public:
DiveReportedCeiling(const DivePlotDataModel &model, const DiveCartesianAxis &hAxis, int hColumn, const DiveCartesianAxis &vAxis, int vColumn, double dpr); DiveReportedCeiling(const DivePlotDataModel &model, const DiveCartesianAxis &hAxis, int hColumn, const DiveCartesianAxis &vAxis, int vColumn, double dpr);
void replot(const dive *d, int from, int to, bool in_planner) override; void replot(const dive *d, int from, int to, bool in_planner) override;
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = 0) override; void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = 0) override;
private:
std::pair<double,double> getTimeValue(int i) const;
std::pair<double, double> getPoint(int i) const;
}; };
class DiveCalculatedTissue : public DiveCalculatedCeiling { class DiveCalculatedTissue : public DiveCalculatedCeiling {