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