Engauge Digitizer  2
GridRemoval.cpp
1 #include "DocumentModelGridRemoval.h"
2 #include "EngaugeAssert.h"
3 #include "GridHealer.h"
4 #include "GridRemoval.h"
5 #include "Logger.h"
6 #include <qdebug.h>
7 #include <QImage>
8 #include <qmath.h>
9 #include "Transformation.h"
10 
11 const double EPSILON = 0.000001;
12 
14 {
15 }
16 
17 QPointF GridRemoval::clipX (const QPointF &posUnprojected,
18  double xBoundary,
19  const QPointF &posOther) const
20 {
21  double s = (xBoundary - posUnprojected.x()) / (posOther.x() - posUnprojected.x());
22  ENGAUGE_ASSERT ((-1.0 * EPSILON < s) && (s < 1.0 + EPSILON));
23 
24  return QPointF ((1.0 - s) * posUnprojected.x() + s * posOther.x(),
25  (1.0 - s) * posUnprojected.y() + s * posOther.y());
26 }
27 
28 QPointF GridRemoval::clipY (const QPointF &posUnprojected,
29  double yBoundary,
30  const QPointF &posOther) const
31 {
32  double s = (yBoundary - posUnprojected.y()) / (posOther.y() - posUnprojected.y());
33  ENGAUGE_ASSERT ((-1.0 * EPSILON < s) && (s < 1.0 + EPSILON));
34 
35  return QPointF ((1.0 - s) * posUnprojected.x() + s * posOther.x(),
36  (1.0 - s) * posUnprojected.y() + s * posOther.y());
37 }
38 
39 QPixmap GridRemoval::remove (const Transformation &transformation,
40  const DocumentModelGridRemoval &modelGridRemoval,
41  const QImage &imageBefore)
42 {
43  LOG4CPP_INFO_S ((*mainCat)) << "GridRemoval::remove"
44  << " transformationIsDefined=" << (transformation.transformIsDefined() ? "true" : "false")
45  << " removeDefinedGridLines=" << (modelGridRemoval.removeDefinedGridLines() ? "true" : "false");
46 
47  QImage image = imageBefore;
48 
49  // Make sure grid line removal is wanted, and possible. Otherwise all processing is skipped
50  if (modelGridRemoval.removeDefinedGridLines() &&
51  transformation.transformIsDefined()) {
52 
53  GridHealer gridHealer (imageBefore,
54  modelGridRemoval);
55 
56  double yGraphMin = modelGridRemoval.startY();
57  double yGraphMax = modelGridRemoval.stopY();
58  for (int i = 0; i < modelGridRemoval.countX(); i++) {
59  double xGraph = modelGridRemoval.startX() + i * modelGridRemoval.stepX();
60 
61  // Convert line between graph coordinates (xGraph,yGraphMin) and (xGraph,yGraphMax) to screen coordinates
62  QPointF posScreenMin, posScreenMax;
63  transformation.transformRawGraphToScreen (QPointF (xGraph,
64  yGraphMin),
65  posScreenMin);
66  transformation.transformRawGraphToScreen (QPointF (xGraph,
67  yGraphMax),
68  posScreenMax);
69 
70  removeLine (posScreenMin,
71  posScreenMax,
72  image,
73  gridHealer);
74  }
75 
76  double xGraphMin = modelGridRemoval.startX();
77  double xGraphMax = modelGridRemoval.stopX();
78  for (int j = 0; j < modelGridRemoval.countY(); j++) {
79  double yGraph = modelGridRemoval.startY() + j * modelGridRemoval.stepY();
80 
81  // Convert line between graph coordinates (xGraphMin,yGraph) and (xGraphMax,yGraph) to screen coordinates
82  QPointF posScreenMin, posScreenMax;
83  transformation.transformRawGraphToScreen (QPointF (xGraphMin,
84  yGraph),
85  posScreenMin);
86  transformation.transformRawGraphToScreen (QPointF (xGraphMax,
87  yGraph),
88  posScreenMax);
89 
90  removeLine (posScreenMin,
91  posScreenMax,
92  image,
93  gridHealer);
94  }
95 
96  // Apply the healing process to the image
97  gridHealer.heal (image);
98  }
99 
100  return QPixmap::fromImage (image);
101 }
102 
103 void GridRemoval::removeLine (const QPointF &posMin,
104  const QPointF &posMax,
105  QImage &image,
106  GridHealer &gridHealer)
107 {
108  double w = image.width() - 1; // Inclusive width = exclusive width - 1
109  double h = image.height() - 1; // Inclusive height = exclusive height - 1
110 
111  QPointF pos1 = posMin;
112  QPointF pos2 = posMax;
113 
114  // Throw away all lines that are entirely above or below or left or right to the screen, since
115  // they cannot intersect the screen
116  bool onLeft = (pos1.x() < 0 && pos2.x () < 0);
117  bool onTop = (pos1.y() < 0 && pos2.y () < 0);
118  bool onRight = (pos1.x() > w && pos2.x () > w);
119  bool onBottom = (pos1.y() > h && pos2.y () > h);
120  if (!onLeft && !onTop && !onRight && !onBottom) {
121 
122  // Clip to within the four sides
123  if (pos1.x() < 0) { pos1 = clipX (pos1, 0, pos2); }
124  if (pos2.x() < 0) { pos2 = clipX (pos2, 0, pos1); }
125  if (pos1.y() < 0) { pos1 = clipY (pos1, 0, pos2); }
126  if (pos2.y() < 0) { pos2 = clipY (pos2, 0, pos1); }
127  if (pos1.x() > w) { pos1 = clipX (pos1, w, pos2); }
128  if (pos2.x() > w) { pos2 = clipX (pos2, w, pos1); }
129  if (pos1.y() > h) { pos1 = clipY (pos1, h, pos2); }
130  if (pos2.y() > h) { pos2 = clipY (pos2, h, pos1); }
131 
132  // Is line more horizontal or vertical?
133  double deltaX = qAbs (pos1.x() - pos2.x());
134  double deltaY = qAbs (pos1.y() - pos2.y());
135  if (deltaX > deltaY) {
136 
137  // More horizontal
138  int xMin = qMin (pos1.x(), pos2.x());
139  int xMax = qMax (pos1.x(), pos2.x());
140  int yAtXMin = (pos1.x() < pos2.x() ? pos1.y() : pos2.y());
141  int yAtXMax = (pos1.x() < pos2.x() ? pos2.y() : pos1.y());
142  for (int x = xMin; x <= xMax; x++) {
143  double s = (double) (x - xMin) / (double) (xMax - xMin);
144  double yLine = (1.0 - s) * yAtXMin + s * yAtXMax;
145  for (int yOffset = -1; yOffset <= 1; yOffset++) {
146  int y = (int) (0.5 + yLine + yOffset);
147  image.setPixel (x, y, QColor(Qt::white).rgb());
148  gridHealer.erasePixel (x, y);
149  }
150  }
151  } else {
152 
153  // More vertical
154  int yMin = qMin (pos1.y(), pos2.y());
155  int yMax = qMax (pos1.y(), pos2.y());
156  int xAtYMin = (pos1.y() < pos2.y() ? pos1.x() : pos2.x());
157  int xAtYMax = (pos1.y() < pos2.y() ? pos2.x() : pos1.x());
158  for (int y = yMin; y <= yMax; y++) {
159  double s = (double) (y - yMin) / (double) (yMax - yMin);
160  double xLine = (1.0 - s) * xAtYMin + s * xAtYMax;
161  for (int xOffset = -1; xOffset <= 1; xOffset++) {
162  int x = (int) (0.5 + xLine + xOffset);
163  image.setPixel (x, y, QColor(Qt::white).rgb());
164  gridHealer.erasePixel (x, y);
165  }
166  }
167  }
168  }
169 }
170 
bool removeDefinedGridLines() const
Get method for removing defined grid lines.
double startY() const
Get method for y start.
void erasePixel(int xCol, int yRow)
Remember that pixel was erased since it belongs to an grid line.
Definition: GridHealer.cpp:90
double stepY() const
Get method for y step.
Class that 'heals' the curves after grid lines have been removed.
Definition: GridHealer.h:31
Affine transformation between screen and graph coordinates, based on digitized axis points...
double stopX() const
Get method for x stop.
QPixmap remove(const Transformation &transformation, const DocumentModelGridRemoval &modelGridRemoval, const QImage &imageBefore)
Process QImage into QPixmap, removing the grid lines.
Definition: GridRemoval.cpp:39
double startX() const
Get method for x start.
double stopY() const
Get method for y stop.
int countX() const
Get method for x count.
int countY() const
Get method for y count.
bool transformIsDefined() const
Transform is defined when at least three axis points have been digitized.
double stepX() const
Get method for x step.
void transformRawGraphToScreen(const QPointF &pointRaw, QPointF &pointScreen) const
Transform from raw graph coordinates to linear cartesian graph coordinates, then to screen coordinate...
Model for DlgSettingsGridRemoval and CmdSettingsGridRemoval. The settings are unstable until the user...
GridRemoval()
Single constructor.
Definition: GridRemoval.cpp:13
void heal(QImage &imageToHeal)
Heal the broken curve lines by spanning the gaps across the newly-removed grid lines.
Definition: GridHealer.cpp:147