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