]> Devoid-pointer.net GitWeb - anyanka.git/commitdiff
Rework scale drawing logic to avoid horizontally overflowing labels. Use
authorMichal Malý <madcatxster@devoid-pointer.net>
Thu, 9 Oct 2014 23:09:34 +0000 (01:09 +0200)
committerMichal Malý <madcatxster@devoid-pointer.net>
Thu, 9 Oct 2014 23:09:34 +0000 (01:09 +0200)
same font everywhere.

signaldrawer.cpp
signaldrawer.h

index 013088d2bdd7bb5917d96ff103b67b2695964e13..afb4dd5b07326ad57d1becd1ff91a8e5673af35c 100644 (file)
@@ -29,7 +29,7 @@ const QString SignalDrawer::ME_SENDER_STR("SignalDrawer");
 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),
@@ -39,6 +39,7 @@ SignalDrawer::SignalDrawer(std::shared_ptr<SignalController> controller, const C
   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() + "]"),
@@ -59,34 +60,40 @@ bool SignalDrawer::draw(const double fromX, const double fromY, const double toX
   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;
     }
   }
@@ -131,7 +138,6 @@ bool SignalDrawer::setDimensions(const int width, const int height)
 
   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;
@@ -141,6 +147,20 @@ bool SignalDrawer::setDimensions(const int width, const int 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);
@@ -206,13 +226,94 @@ bool SignalDrawer::drawPeaks(QPixmap* const 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");
@@ -221,18 +322,27 @@ bool SignalDrawer::drawScale(const SignalController::Axis axis, QPixmap* const t
 
   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;
@@ -241,20 +351,20 @@ bool SignalDrawer::drawScale(const SignalController::Axis axis, QPixmap* const t
   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;
@@ -262,13 +372,12 @@ bool SignalDrawer::drawScale(const SignalController::Axis axis, QPixmap* const t
     }
 
   }
-  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);
 
@@ -279,21 +388,25 @@ void SignalDrawer::renderTimeScaleText(QPainter* const p, const double rel, cons
   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) {
@@ -306,16 +419,17 @@ void SignalDrawer::renderTimeScaleTick(QPainter* const p, const double rel, cons
   }
 }
 
-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;
   }
 }
@@ -373,7 +487,7 @@ bool SignalDrawer::renderGraph(QPixmap* const target)
   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;
   }
@@ -383,7 +497,6 @@ bool SignalDrawer::renderGraph(QPixmap* const target)
   }
 
   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);
@@ -520,7 +633,7 @@ double SignalDrawer::linesIntersection(const double k1, const double q1, const d
 
 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;
 }
 
@@ -533,9 +646,9 @@ int SignalDrawer::relToYPix(const double rel)
 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;
 }
 
index 57894795da3543a33ff24f86a285faa2479c9f2a..77c3b3c77f2e170456f7e6e9f15e64c50db6fedf 100644 (file)
@@ -60,7 +60,7 @@ public:
   bool drawAxisLabel(const SignalController::Axis axis, QPixmap* const target);
   bool drawGraph(QPixmap* const target);
   bool drawPeaks(QPixmap* const target);
-  bool drawScale(const SignalController::Axis axis, QPixmap* const target);
+  bool drawScale(QPixmap* const target);
   QRegion erasePeak(const PeakDrawData& pd);
   int dwidth() const { return m_width; }
   bool renderGraph(QPixmap* const fresh);
@@ -81,13 +81,20 @@ protected:
   double m_relYMin;
   std::shared_ptr<GraphDrawData> m_gdData;
 
+  bool clearBackground(QPixmap* const target);
   void copyPixmapRegion(const QRegion& region, QPixmap* const source, QPixmap* const target);
   QPixmap* createFreshPixmap() { return new QPixmap(m_width, m_height); }
+  void drawLeadingSubticks(const RulerDrawData& rd, std::function<void (const double, const double, const TickType)> drawFunc, const double relMin);
+  void drawScaleBySubticks(const RulerDrawData& rd, std::function<void (const double, const double, const TickType)> drawFunc, const double fromSubRel,
+                           const double fromAbsVal, const double toSubRel);
+  void drawScaleByTicks(const RulerDrawData& rd, std::function<void (const double, const double, const TickType)> drawFunc, const double relMax, bool drawSubTicks);
+  void drawTimeScale(QPainter* const p);
+  void drawValueScale(QPainter* const p);
   void renderFresh(QPixmap* const fresh);
-  void renderTimeScaleText(QPainter*const p, const double rel, const double value);
-  void renderValueScaleText(QPainter*const p, const double rel, const double value);
-  void renderTimeScaleTick(QPainter*const p, const double rel, const TickType tt);
-  void renderValueScaleTick(QPainter*const p, const double rel, const TickType tt);
+  void renderTimeScaleText(QPainter*const p, const double rel, const double value, const TickType tt);
+  void renderValueScaleText(QPainter*const p, int& maxTextWidth, const double rel, const double value, const TickType tt);
+  void renderTimeScaleTick(QPainter*const p, const double rel, const double time, const TickType tt);
+  void renderValueScaleTick(QPainter*const p, const double rel, const double time, const TickType tt);
 
   int relToXPix(const double rel);
   int relToYPix(const double rel);
@@ -109,6 +116,7 @@ private:
   int m_width;
   int m_gHeight;
   int m_gWidth;
+  int m_leftGraphOffset;
 
   double m_oldRelXMax;
   double m_oldRelXMin;