const int SignalDrawer::AXIS_LABEL_BORDER_OFFSET(2);
const int SignalDrawer::AXIS_LABEL_SCALE_OFFSET(3);
const int SignalDrawer::SCALE_MARGIN_TIME(16);
-const int SignalDrawer::SCALE_MARGIN_VALUE(48);
+const int SignalDrawer::SCALE_MARGIN_VALUE(12);
SignalDrawer::SignalDrawer(std::shared_ptr<SignalController> controller, const Constraints& constraints) :
m_controller(controller),
m_relXMin(constraints.fromX),
m_relYMax(constraints.toY),
m_relYMin(constraints.fromY),
+ m_leftGraphOffset(0),
m_axisLabelFont(QFont("arial", 10)),
m_axisLabelFM(QFontMetrics(m_axisLabelFont)),
m_xAxisLabelText("[" + controller->signal()->xunitAsString() + "]"),
bool ret;
setNewRelativeConstraints(fromX, fromY, toX, toY);
- m_xAxisLabelRect = m_axisLabelFM.boundingRect(m_xAxisLabelText);
- m_yAxisLabelRect = m_axisLabelFM.boundingRect(m_yAxisLabelText);
- m_xAxisLabelRect.moveTo(m_width - m_axisLabelFM.width(m_xAxisLabelText) - AXIS_LABEL_BORDER_OFFSET, m_gHeight - AXIS_LABEL_SCALE_OFFSET);
- m_yAxisLabelRect.moveTo(SCALE_MARGIN_VALUE + AXIS_LABEL_SCALE_OFFSET, m_axisLabelFM.height() - m_axisLabelFM.underlinePos());
fresh = createFreshPixmap();
if (fresh == nullptr)
return false;
- /* Draw the signal itself */
- if (flags(GraphLayers::GRAPH & layers)) {
- ret = drawGraph(fresh);
- if (!ret) {
- Logger::log(Logger::Level::WARNING, ME_SENDER_STR, __QFUNC__ + " cannot draw graph");
- goto error_out;
- }
+ /* Fill with white */
+ ret = clearBackground(fresh);
+ if (!ret) {
+ Logger::log(Logger::Level::WARNING, ME_SENDER_STR, __QFUNC__ + " cannot clear background");
+ goto error_out;
}
/* Draw the scale */
if (flags(GraphLayers::SCALE & layers)) {
- ret = drawScale(SignalController::Axis::VALUE, fresh);
+ ret = drawScale(fresh);
if (!ret) {
- Logger::log(Logger::Level::WARNING, ME_SENDER_STR, __QFUNC__ + " cannot draw scale for Y axis");
+ Logger::log(Logger::Level::WARNING, ME_SENDER_STR, __QFUNC__ + " cannot draw scales");
goto error_out;
}
- ret = drawScale(SignalController::Axis::TIME, fresh);
+ m_xAxisLabelRect = m_axisLabelFM.boundingRect(m_xAxisLabelText);
+ m_yAxisLabelRect = m_axisLabelFM.boundingRect(m_yAxisLabelText);
+ m_xAxisLabelRect.moveTo(m_width - m_axisLabelFM.width(m_xAxisLabelText) - AXIS_LABEL_BORDER_OFFSET, m_gHeight - AXIS_LABEL_SCALE_OFFSET);
+ m_yAxisLabelRect.moveTo(m_leftGraphOffset + AXIS_LABEL_SCALE_OFFSET, m_axisLabelFM.height() - m_axisLabelFM.underlinePos());
+ } else {
+ m_gWidth = m_width;
+ m_leftGraphOffset = 0;
+ }
+
+
+ /* Draw the signal itself */
+ if (flags(GraphLayers::GRAPH & layers)) {
+ ret = drawGraph(fresh);
if (!ret) {
- Logger::log(Logger::Level::WARNING, ME_SENDER_STR, __QFUNC__ + " cannot draw scale for X axis");
+ Logger::log(Logger::Level::WARNING, ME_SENDER_STR, __QFUNC__ + " cannot draw graph");
goto error_out;
}
}
m_height = height;
m_width = width;
- m_gWidth = width - SCALE_MARGIN_VALUE;
m_gHeight = height - SCALE_MARGIN_TIME;
//qDebug() << __QFUNC__ << width << height << m_width << m_height;
/** Protected methods **/
+bool SignalDrawer::clearBackground(QPixmap* const target)
+{
+ QPainter p;
+
+ if (target == nullptr)
+ return false;
+
+ p.begin(target);
+ p.fillRect(0, 0, m_width, m_height, Qt::white);
+ p.end();
+
+ return true;
+}
+
void SignalDrawer::copyPixmapRegion(const QRegion& reg, QPixmap* const source, QPixmap* const target)
{
QPainter p(target);
return true;
}
-bool SignalDrawer::drawScale(const SignalController::Axis axis, QPixmap* const target)
+void SignalDrawer::drawLeadingSubticks(const RulerDrawData& rd, std::function<void (const double, const double, const TickType)> drawFunc,
+ const double relMin)
+{
+ double subRelMin = rd.firstTickRel;
+ double subAbsMin = rd.firstTickAbs;
+ while ((subRelMin -= rd.relStep / 10) >= relMin)
+ subAbsMin -= rd.step / 10;
+
+ drawScaleBySubticks(rd, drawFunc, subRelMin, subAbsMin, rd.firstTickRel);
+}
+
+void SignalDrawer::drawScaleBySubticks(const RulerDrawData& rd, std::function<void (const double, const double, const TickType)> drawFunc, const double fromSubRel,
+ const double fromAbsVal, const double toSubRel)
+{
+ const double subRelStep = rd.relStep / 10;
+ const double subAbsStep = rd.step / 10;
+ double subRel = fromSubRel;
+ double absVal = fromAbsVal;
+
+ int ctr = 11 - ((toSubRel - fromSubRel) / subRelStep);
+
+ while (subRel < toSubRel) {
+ subRel += subRelStep;
+ absVal += subAbsStep;
+ drawFunc(subRel, absVal, (ctr++ == 5) ? TickType::TICK : TickType::SUBTICK);
+ }
+}
+
+void SignalDrawer::drawScaleByTicks(const RulerDrawData& rd, std::function<void (const double, const double, const TickType)> drawFunc, const double relMax, bool drawSubTicks)
+{
+ double absVal = rd.firstTickAbs;
+ for (double rel = rd.firstTickRel; rel <= relMax; rel += rd.relStep) {
+ drawFunc(rel, absVal, TickType::TICK);
+
+ /* Draw subticks ticks */
+ if (drawSubTicks)
+ drawScaleBySubticks(rd, drawFunc, rel, absVal, rel + rd.relStep);
+
+ absVal += rd.step;
+ }
+}
+
+void SignalDrawer::drawTimeScale(QPainter* const p)
+{
+ std::function<void (const double, const double, const TickType)> textDrawFunc;
+ std::function<void (const double, const double, const TickType)> tickDrawFunc;
+
+ textDrawFunc = std::bind(&SignalDrawer::renderTimeScaleText, this, p, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3);
+ tickDrawFunc = std::bind(&SignalDrawer::renderTimeScaleTick, this, p, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3);
+
+ RulerDrawData rd = m_controller->getRulerDrawData(m_relXMin, m_relXMax, SignalController::Axis::TIME);
+
+ /* Draw text first - we need to know how wide is the largest numeric cue */
+ drawScaleByTicks(rd, textDrawFunc, m_relXMax, false);
+
+ /* Draw subticks before first big tick */
+ drawLeadingSubticks(rd, tickDrawFunc, m_relXMin);
+ drawScaleByTicks(rd, tickDrawFunc, m_relXMax, true);
+ p->drawLine(m_leftGraphOffset, m_gHeight + 1, m_width, m_gHeight + 1);
+}
+
+void SignalDrawer::drawValueScale(QPainter* const p)
+{
+ std::function<void (const double, const double, const TickType)> textDrawFunc;
+ std::function<void (const double, const double, const TickType)> tickDrawFunc;
+ int maxCueWidth = 0;
+
+ textDrawFunc = std::bind(&SignalDrawer::renderValueScaleText, this, p, std::ref(maxCueWidth), std::placeholders::_1, std::placeholders::_2, std::placeholders::_3);
+ tickDrawFunc = std::bind(&SignalDrawer::renderValueScaleTick, this, p, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3);
+
+ RulerDrawData rd = m_controller->getRulerDrawData(m_relYMin, m_relYMax, SignalController::Axis::VALUE);
+
+ /* Draw text first - we need to know how wide is the largest numeric cue */
+ drawScaleByTicks(rd, textDrawFunc, m_relYMax, false);
+
+ /* Maximum width of numeric cues is now known - calculate offset from the left */
+ m_leftGraphOffset = maxCueWidth + SCALE_MARGIN_VALUE;
+ m_gWidth = m_width - m_leftGraphOffset;
+
+ /* Draw subticks before first big tick */
+ drawLeadingSubticks(rd, tickDrawFunc, m_relYMin);
+ drawScaleByTicks(rd, tickDrawFunc, m_relYMax, true);
+ p->drawLine(m_leftGraphOffset, 0, m_leftGraphOffset, m_gHeight);
+}
+
+bool SignalDrawer::drawScale(QPixmap* const target)
{
QPainter p;
- double absVal;
- double relMin, relMax;
- std::function<void (const double, const TickType)> tickDrawFunc;
- std::function<void (const double, const double)> textDrawFunc;
if (target == nullptr) {
Logger::log(Logger::Level::WARNING, ME_SENDER_STR, __QFUNC__ + " null pointer to pixmap");
p.begin(target);
p.setFont(m_axisLabelFont);
+ /* VALUE scale must be drawn first - left offset of the graph depends on it */
+ drawValueScale(&p);
+ drawTimeScale(&p);
+ p.end();
+
+ return true;
+
+ /*
switch (axis) {
case SignalController::Axis::TIME:
relMin = m_relXMin; relMax = m_relXMax;
tickDrawFunc = std::bind(&SignalDrawer::renderTimeScaleTick, this, &p, std::placeholders::_1, std::placeholders::_2);
- textDrawFunc = std::bind(&SignalDrawer::renderTimeScaleText, this, &p, std::placeholders::_1, std::placeholders::_2);
+ textDrawFunc = std::bind(&SignalDrawer::renderTimeScaleText, this, &p, 0, std::placeholders::_1, std::placeholders::_2);
p.drawLine(SCALE_MARGIN_VALUE, m_gHeight + 1, m_gWidth + SCALE_MARGIN_VALUE, m_gHeight + 1);
break;
- case SignalController::Axis::VALUE:
+ case SignalController::Axis::VALUE: /* VALUE axis is drawn first in order to determine offset from the left border *
relMin = m_relYMin; relMax = m_relYMax;
tickDrawFunc = std::bind(&SignalDrawer::renderValueScaleTick, this, &p, std::placeholders::_1, std::placeholders::_2);
- textDrawFunc = std::bind(&SignalDrawer::renderValueScaleText, this, &p, std::placeholders::_1, std::placeholders::_2);
- p.drawLine(SCALE_MARGIN_VALUE - 1, 0, SCALE_MARGIN_VALUE - 1, m_gHeight);
+ textDrawFunc = std::bind(&SignalDrawer::renderValueScaleText, this, &p, maxTextWidth, std::placeholders::_1, std::placeholders::_2);
+ /* Offset from the left is now k
+ p.drawLine(SCALE_MARGIN_VALUE + maxTextWidth, 0, SCALE_MARGIN_VALUE + maxTextWidth, m_gHeight);
break;
default:
return false;
RulerDrawData rd = m_controller->getRulerDrawData(relMin, relMax, axis);
//qDebug() << __QFUNC__ << "step" << rd.step << "relStep" << rd.relStep;
- /* Draw subticks before the first tick */
+ /* Draw subticks before the first tick *
{
int ctr = 10;
for (double subRel = rd.firstTickRel; subRel >= relMin; subRel -= rd.relStep / 10)
tickDrawFunc(subRel, (--ctr == 5) ? TickType::TICK : TickType::SUBTICK);
}
- /* Draw ticks */
+ /* Draw ticks *
absVal = rd.firstTickAbs;
for (double rel = rd.firstTickRel; rel <= relMax; rel += rd.relStep) {
tickDrawFunc(rel, TickType::TICK);
textDrawFunc(rel, absVal);
absVal += rd.step;
- /* Draw subticks ticks */
+ /* Draw subticks ticks *
double subRel = rel;
for (uint i = 1; i < 10; i++) {
subRel += rd.relStep / 10;
}
}
- p.end();
-
- return true;
+ p.end();*/
}
-void SignalDrawer::renderTimeScaleText(QPainter* const p, const double rel, const double value)
+void SignalDrawer::renderTimeScaleText(QPainter* const p, const double rel, const double value, const TickType tt)
{
+ Q_UNUSED(tt);
const int xPix = relToXPix(rel);
const QString text = m_locale.toString(value, 'f', 3);
p->drawText(xPix + 2, m_gHeight + 15, m_locale.toString(value, 'f', 3));
}
-void SignalDrawer::renderValueScaleText(QPainter* const p, const double rel, const double value)
+void SignalDrawer::renderValueScaleText(QPainter* const p, int& maxCueWidth, const double rel, const double value, const TickType tt)
{
+ Q_UNUSED(tt);
const int yPix = relToYPix(rel);
const QString text = m_locale.toString(value, 'f', 2);
/* Do not draw labels that would overflow */
QRect br = p->fontMetrics().boundingRect(text);
- if (yPix - br.height() < 0)
+ if (yPix - (br.height() / 2) < 0)
return;
- p->drawText(1, yPix + 5, m_locale.toString(value, 'f', 2));
+ if (maxCueWidth < br.width())
+ maxCueWidth = br.width();
+ p->drawText(1, yPix + (br.height() / 2), m_locale.toString(value, 'f', 2));
}
-void SignalDrawer::renderTimeScaleTick(QPainter* const p, const double rel, const TickType tt)
+void SignalDrawer::renderTimeScaleTick(QPainter* const p, const double rel, const double time, const TickType tt)
{
+ Q_UNUSED(time);
const int xPix = relToXPix(rel);
switch (tt) {
}
}
-void SignalDrawer::renderValueScaleTick(QPainter* const p, const double rel, const TickType tt)
+void SignalDrawer::renderValueScaleTick(QPainter* const p, const double rel, const double time, const TickType tt)
{
+ Q_UNUSED(time);
const int yPix = relToYPix(rel);
switch (tt) {
case TickType::TICK:
- p->drawLine(SCALE_MARGIN_VALUE - 6, yPix, SCALE_MARGIN_VALUE - 1, yPix);
+ p->drawLine(m_leftGraphOffset - 6, yPix, m_leftGraphOffset - 1, yPix);
break;
case TickType::SUBTICK:
- p->drawLine(SCALE_MARGIN_VALUE - 4, yPix, SCALE_MARGIN_VALUE - 1, yPix);
+ p->drawLine(m_leftGraphOffset - 4, yPix, m_leftGraphOffset - 1, yPix);
break;
}
}
QPainter p;
int fromXPix, fromYPix;
- if (m_width < SCALE_MARGIN_VALUE || m_height < SCALE_MARGIN_VALUE) {
+ if (m_width < m_leftGraphOffset || m_height < SCALE_MARGIN_TIME) {
Logger::log(Logger::Level::WARNING, ME_SENDER_STR, __QFUNC__ + " invalid dimensions (" + QString::number(m_width) + ", " + QString::number(m_height) + ")");
return false;
}
}
p.begin(target);
- p.fillRect(0, 0, m_width, m_height, Qt::white);
p.setPen(QColor(Qt::blue));
fromXPix = relToXPix(m_gdData->ddata[0].first);
int SignalDrawer::relToXPix(const double rel)
{
- const double ret = m_gWidth / (m_relXMax - m_relXMin) * (rel - m_relXMin) + SCALE_MARGIN_VALUE;
+ const double ret = m_gWidth / (m_relXMax - m_relXMin) * (rel - m_relXMin) + m_leftGraphOffset;
return ret;
}
double SignalDrawer::xPixToRel(const int pix)
{
double ret;
- if (pix < SCALE_MARGIN_VALUE)
+ if (pix < m_leftGraphOffset)
return m_relXMin;
- ret = ((pix - SCALE_MARGIN_VALUE) * (m_relXMax - m_relXMin) / m_gWidth) + m_relXMin;
+ ret = ((pix - m_leftGraphOffset) * (m_relXMax - m_relXMin) / m_gWidth) + m_relXMin;
return ret;
}