Graphic DateTime axisType improvements

This commit is contained in:
2022-11-23 18:40:44 +03:00
parent 99a5276573
commit 05356f9489
2 changed files with 105 additions and 31 deletions

View File

@@ -433,6 +433,7 @@ void Graphic::canvasPaintEvent() {
gridborder += QPoint(font_sz.width(), font_sz.height());
if (hasLblY) gridborder += QPoint(font_sz.height(), 0);
if (hasLblX) gridborder += QPoint(0, font_sz.height());
if (axis_type_x == DateTime) gridborder += QPoint(0, font_sz.height());
}
painter->setClipping(true);
painter->setClipRect(QRect(gridborder.x(), 0, wid - gridborder.x(), hei - gridborder.y()));
@@ -1285,27 +1286,53 @@ void Graphic::drawGrid() {
}
} else {
int cur_scl[7] = {0,0,0,0,0,0,0};
step = splitRangeDate(range, wid / gridx / font_sz.width() * 1.4, &df, cur_scl);
start = roundTo(canvas2realX(right), step);
px = start + step;
QDateTime cd = QDateTime::fromMSecsSinceEpoch(px * grid_numbers_x);
roundDateTime(cd, cur_scl);
addDateTime(cd, cur_scl);
const char * formats[2];
step = splitRangeDate(range, wid / gridx / font_sz.width() * 1.4, formats[0], formats[1], cur_scl);
if (step > 0.) {
QDateTime cd = QDateTime::fromMSecsSinceEpoch(canvas2realX(gbx) * grid_numbers_x), cdp, cdc;
//qDebug() << step << range << int(wid / gridx / font_sz.width() * 1.4) << cd;
roundDateTime(cd, cur_scl);
cdp = cd;
//qDebug() << cd << cur_scl[0] << cur_scl[1] << cur_scl[2] << cur_scl[3] << cur_scl[4] << cur_scl[5] << cur_scl[6];
struct Anchor {
int pix_x[2];
QDateTime date;
};
QVector<Anchor> areas;
cnt = 1000;
int area_start = gbx;
while (cnt-- > 0) {
addDateTime(cd, cur_scl, -1);
addDateTime(cd, cur_scl);
cdc = cd;
roundDateTime(cdc, cur_scl);
cx = real2canvasX(cd.toMSecsSinceEpoch() / grid_numbers_x);
if (cx > right) continue;
if (cx < gbx) break;
if (cx < gbx) continue;
if (cdp != cdc) {
areas << Anchor{{area_start, qMin(cx, right)}, cdp};
area_start = areas.back().pix_x[1];
cdp = cdc;
}
if (cx > right) break;
painter->setPen(grid_pen);
painter->drawLine(cx, hei + 5, cx, 0);
painter->setPen(text_color);
int dx = -font_sz.height() / 4.;
painter->setFont(nf);
str.first = cd.toString(df);
str.first = cd.toString(formats[0]);
painter->drawText(cx + dx, cy, str.first);
}
if (area_start < right)
areas << Anchor{{area_start, right}, cdc};
painter->setPen(grid_pen);
for (const auto & a: areas) {
painter->drawLine(a.pix_x[0], hei + 5, a.pix_x[0], cy - font_sz.height());
}
painter->setPen(text_color);
painter->setFont(nf);
for (const auto & a: areas) {
QRect r(a.pix_x[0], cy - (2 * font_sz.height()), a.pix_x[1] - a.pix_x[0], font_sz.height());
painter->drawText(r, Qt::AlignCenter | Qt::TextDontClip, a.date.toString(formats[1]));
}
}
}
painter->setPen(text_color);
@@ -1573,6 +1600,33 @@ void Graphic::drawPause() {
painter->setClipping(true);
}
double roundToSteps(double value, const QVector<double> & steps) {
double ret = value, min_err = -1.;
for (double v: steps) {
double sv = qRound64(value / v) * v;
double err = qAbs<double>(value - sv);
if (min_err < 0 || min_err > err) {
min_err = err;
ret = sv;
}
}
if (ret < steps[0]) ret = steps[0];
return ret;
}
double roundToNearest(double value, const QVector<double> & values) {
double ret = value, min_err = -1.;
for (double v: values) {
double err = qAbs<double>(value - v);
if (min_err < 0 || min_err > err) {
min_err = err;
ret = v;
}
}
if (ret < values[0]) ret = values[0];
return ret;
}
double Graphic::splitRange(double range, int count) {
double digits, step, tln;
@@ -1587,45 +1641,65 @@ double Graphic::splitRange(double range, int count) {
break;
}
}
double step5 = qRound(step / 5.) * 5., step10 = qRound(step / 10.) * 10.;
double err5 = qAbs<double>(step - step5), err10 = qAbs<double>(step - step10);
step = (err5 < err10 ? step5 : step10) * digits;
step = roundToSteps(step, {5., 10.}) * digits;
return step;
}
double Graphic::splitRangeDate(double range, int count, QString * format, int step[7]) {
double Graphic::splitRangeDate(double range, int count, const char *& up_format, const char *& lo_format, int step[7]) {
static const qint64
to_sec = 1000LL,
to_min = 1000LL * 60,
to_hour = 1000LL * 60 * 60,
to_day = 1000LL * 60 * 60 * 24,
to_month = 1000LL * 60 * 60 * 24 * 30,
to_year = 1000LL * 60 * 60 * 24 * 30 * 12;
static const struct {
const char * upper;
const char * lower;
} formats[] = {
{"ss.zzz 's'" , "yyyy MMM dd(ddd) h:mm:ss"},
{"ss 's'" , "yyyy MMM dd(ddd) h:mm"},
{"mm 'm'" , "yyyy MMM dd(ddd) h 'h' "},
{"h 'h'" , "yyyy MMM dd(ddd)"},
{"dd(ddd)" , "yyyy MMM"},
{"MMM" , "yyyy"},
{"yyyy" , ""}
};
double ret = splitRange(range, count);
if (ret < 1000. * 1) {*format = "ss.zzz"; step[0] = ret;}
else if (ret < 1000. * 60) {*format = "h:m:ss"; step[1] = qRound(ret / 1000);}
else if (ret < 1000. * 60 * 60) {*format = "h:mm"; step[2] = qRound(ret / 1000 / 60);}
else if (ret < 1000. * 60 * 60 * 24) {*format = "dd(ddd) hh"; step[3] = qRound(ret / 1000 / 60 / 60);}
else if (ret < 1000. * 60 * 60 * 24 * 30) {*format = "MMM dd"; step[4] = qRound(ret / 1000 / 60 / 60 / 24);}
else if (ret < 1000. * 60 * 60 * 24 * 30 * 12) {*format = "yyyy MMM"; step[5] = qRound(ret / 1000 / 60 / 60 / 24 / 30);}
else {*format = "yyyy"; step[6] = qRound(ret / 1000 / 60 / 60 / 24 / 30 / 12);}
int format_index = 6;
if (ret < to_sec / 3) {format_index = 0; step[0] = qRound64(ret);}
else if (ret < to_min / 2) {format_index = 1; step[1] = roundToNearest(ret / to_sec , {1, 2, 5, 10, 15, 20, 30});}
else if (ret < to_hour / 2) {format_index = 2; step[2] = roundToNearest(ret / to_min , {1, 2, 5, 10, 15, 20, 30});}
else if (ret < to_day / 2) {format_index = 3; step[3] = roundToNearest(ret / to_hour , {1, 2, 3, 4, 6, 8, 12});}
else if (ret < to_month ) {format_index = 4; step[4] = roundToNearest(ret / to_day , {1, 2, 5, 10});}
else if (ret < to_year ) {format_index = 5; step[5] = roundToNearest(ret / to_month, {1, 2, 3, 4, 6});}
else {format_index = 6; step[6] = qRound64(ret);}
up_format = formats[format_index].upper;
lo_format = formats[format_index].lower;
return ret;
}
double Graphic::roundTo(double value, double round_to) {
if (round_to == 0.) return value;
return qRound(value / round_to) * round_to;
return qRound64(value / round_to) * round_to;
}
void Graphic::roundDateTime(QDateTime & dt, int c[7]) {
QDate d(dt.date()); QTime t(dt.time());
if (c[1] != 0) t.setHMS(t.hour(), t.minute(), t.second());
if (c[2] != 0) t.setHMS(t.hour(), t.minute(), 0);
if (c[3] != 0) t.setHMS(t.hour(), 0, 0);
if (c[4] != 0) {t.setHMS(0, 0, 0); d.setDate(d.year(), d.month(), d.day());}
if (c[5] != 0) {t.setHMS(0, 0, 0); d.setDate(d.year(), d.month(), 1);}
if (c[6] != 0) {t.setHMS(0, 0, 0); d.setDate(d.year(), 1, 1);}
if (c[0] != 0) t.setHMS(t.hour(), t.minute(), t.second());
if (c[1] != 0) t.setHMS(t.hour(), t.minute(), 0);
if (c[2] != 0) t.setHMS(t.hour(), 0, 0);
if (c[3] != 0) {t.setHMS(0, 0, 0); d.setDate(d.year(), d.month(), d.day());}
if (c[4] != 0) {t.setHMS(0, 0, 0); d.setDate(d.year(), d.month(), 1);}
if (c[5] != 0 || c[6] != 0) {t.setHMS(0, 0, 0); d.setDate(d.year(), 1, 1);}
dt = QDateTime(d, t);
}
void Graphic::addDateTime(QDateTime & dt, int c[7], int mul) {
void Graphic::addDateTime(QDateTime & dt, int c[7], qint64 mul) {
if (c[0] != 0) dt = dt.addMSecs(mul * c[0]);
if (c[1] != 0) dt = dt.addSecs(mul * c[1]);
if (c[2] != 0) dt = dt.addSecs(mul * c[2] * 60);

View File

@@ -388,10 +388,10 @@ protected:
void setRectToLines();
void checkLines();
double splitRange(double range, int count = 1);
double splitRangeDate(double range, int count = 1, QString * format = 0, int step[7] = 0);
double splitRangeDate(double range, int count, const char *& up_format, const char *& lo_format, int step[7]);
double roundTo(double value, double round_to);
void roundDateTime(QDateTime & dt, int c[7]);
void addDateTime(QDateTime & dt, int c[7], int mul = 1);
void addDateTime(QDateTime & dt, int c[7], qint64 mul = 1);
QPointF absPoint(QPointF point) {return QPointF(qAbs(point.x()), qAbs(point.y()));}
QString pointCoords(QPointF point, bool x = true, bool y = true);
QPair<QString, QString> gridMark(double v) const;