Engauge Digitizer 2
Loading...
Searching...
No Matches
DigitizeStatePointMatch.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 "CmdAddPointGraph.h"
8#include "CmdMediator.h"
9#include "ColorFilter.h"
10#include "CurveStyles.h"
11#include "DigitizeStateContext.h"
12#include "DigitizeStatePointMatch.h"
13#include "EngaugeAssert.h"
14#include "EnumsToQt.h"
15#include "GraphicsPoint.h"
16#include "GraphicsScene.h"
17#include "GraphicsView.h"
18#include "Logger.h"
19#include "MainWindow.h"
20#include "OrdinalGenerator.h"
21#include "PointMatchAlgorithm.h"
22#include "PointStyle.h"
23#include <QApplication>
24#include <QCursor>
25#include <QGraphicsEllipseItem>
26#include <QGraphicsScene>
27#include <QImage>
28#include <qmath.h>
29#include <QMessageBox>
30#include <QPen>
31
32const double Z_VALUE = 200.0;
33
36 m_outline (0),
37 m_candidatePoint (0)
38{
39}
40
41DigitizeStatePointMatch::~DigitizeStatePointMatch ()
42{
43}
44
49
51 DigitizeState /* previousState */)
52{
53 LOG4CPP_INFO_S ((*mainCat)) << "DigitizeStatePointMatch::begin";
54
55 setCursor(cmdMediator);
56 context().setDragMode(QGraphicsView::NoDrag);
58
59 // Add outline that will move with the cursor
60 m_outline = new QGraphicsEllipseItem ();
61 context().mainWindow().scene().addItem (m_outline);
62 m_outline->setPen (QPen (Qt::black));
63 m_outline->setVisible (true);
64 m_outline->setZValue (Z_VALUE);
65}
66
67void DigitizeStatePointMatch::createPermanentPoint (CmdMediator *cmdMediator,
68 const QPointF &posScreen)
69{
70 // Create command to add point
71 OrdinalGenerator ordinalGenerator;
72 Document &document = cmdMediator->document ();
73 const Transformation &transformation = context ().mainWindow ().transformation();
74 QUndoCommand *cmd = new CmdAddPointGraph (context ().mainWindow(),
75 document,
76 context ().mainWindow().selectedGraphCurve(),
77 posScreen,
78 ordinalGenerator.generateCurvePointOrdinal(document,
79 transformation,
80 posScreen,
81 activeCurve ()));
82 context().appendNewCmd(cmdMediator,
83 cmd);
84
85}
86
87void DigitizeStatePointMatch::createTemporaryPoint (CmdMediator *cmdMediator,
88 const QPoint &posScreen)
89{
90 LOG4CPP_DEBUG_S ((*mainCat)) << "DigitizeStatePointMatch::createTemporaryPoint";
91
92 const DocumentModelPointMatch &modelPointMatch = cmdMediator->document().modelPointMatch();
93
94 // Get point style for current graph, and then override with candidate color
95 const CurveStyles &curveStyles = cmdMediator->document().modelCurveStyles();
96 PointStyle pointStyle = curveStyles.pointStyle (activeCurve());
97 pointStyle.setPaletteColor (modelPointMatch.paletteColorCandidate());
98
99 // Temporary point that user can see while DlgEditPoint is active
101 pointStyle,
102 posScreen);
103
104 context().mainWindow().scene().removeTemporaryPointIfExists(); // Only one temporary point at a time is allowed
105
107 point);
108 m_posCandidatePoint = posScreen;
109}
110
111QCursor DigitizeStatePointMatch::cursor(CmdMediator * /* cmdMediator */) const
112{
113 LOG4CPP_DEBUG_S ((*mainCat)) << "DigitizeStatePointMatch::cursor";
114
115 return QCursor (Qt::ArrowCursor);
116}
117
119{
120 LOG4CPP_INFO_S ((*mainCat)) << "DigitizeStatePointMatch::end";
121
122 // Remove candidate point which may or may not exist at this point
124
125 // Remove outline before leaving state
126 ENGAUGE_CHECK_PTR (m_outline);
127 context().mainWindow().scene().removeItem (m_outline);
128 m_outline = 0;
129}
130
131QList<PointMatchPixel> DigitizeStatePointMatch::extractSamplePointPixels (const QImage &img,
132 const DocumentModelPointMatch &modelPointMatch,
133 const QPointF &posScreen) const
134{
135 LOG4CPP_INFO_S ((*mainCat)) << "DigitizeStatePointMatch::extractSamplePointPixels";
136
137 // All points inside modelPointMatch.maxPointSize() are collected, whether or not they
138 // are on or off. Originally only the on points were collected, but obvious mismatches
139 // were happening (example, 3x3 point would appear to be found in several places inside 8x32 rectangle)
140 QList<PointMatchPixel> samplePointPixels;
141
142 int radiusMax = modelPointMatch.maxPointSize() / 2;
143
144 ColorFilter colorFilter;
145 for (int xOffset = -radiusMax; xOffset <= radiusMax; xOffset++) {
146 for (int yOffset = -radiusMax; yOffset <= radiusMax; yOffset++) {
147
148 int x = posScreen.x() + xOffset;
149 int y = posScreen.y() + yOffset;
150 int radius = qSqrt (xOffset * xOffset + yOffset * yOffset);
151
152 if (radius <= radiusMax) {
153
154 bool pixelIsOn = colorFilter.pixelFilteredIsOn (img,
155 x,
156 y);
157
158 PointMatchPixel point (xOffset,
159 yOffset,
160 pixelIsOn);
161
162 samplePointPixels.push_back (point);
163 }
164 }
165 }
166
167 return samplePointPixels;
168}
169
171{
172 LOG4CPP_INFO_S ((*mainCat)) << "DigitizeStatePointMatch::handleCurveChange";
173}
174
176 Qt::Key key,
177 bool /* atLeastOneSelectedItem */)
178{
179 LOG4CPP_INFO_S ((*mainCat)) << "DigitizeStatePointMatch::handleKeyPress"
180 << " key=" << QKeySequence (key).toString ().toLatin1 ().data ();
181
182 // The selected key button has to be compatible with GraphicsView::keyPressEvent
183 if (key == Qt::Key_Right) {
184
185 promoteCandidatePointToPermanentPoint (cmdMediator); // This removes the current temporary point
186
187 popCandidatePoint(cmdMediator); // This creates a new temporary point
188
189 }
190}
191
193 QPointF posScreen)
194{
195// LOG4CPP_DEBUG_S ((*mainCat)) << "DigitizeStatePointMatch::handleMouseMove";
196
197 const DocumentModelPointMatch &modelPointMatch = cmdMediator->document().modelPointMatch();
198
199 m_outline->setRect (posScreen.x() - modelPointMatch.maxPointSize() / 2.0,
200 posScreen.y() - modelPointMatch.maxPointSize() / 2.0,
201 modelPointMatch.maxPointSize(),
202 modelPointMatch.maxPointSize());
203
204 const QImage &img = context().mainWindow().imageFiltered();
205 int radiusLimit = cmdMediator->document().modelGeneral().cursorSize();
206 bool pixelShouldBeOn = pixelIsOnInImage (img,
207 posScreen.x(),
208 posScreen.y(),
209 radiusLimit);
210
211 QColor penColorIs = m_outline->pen().color();
212 bool pixelIsOn = (penColorIs.red () != penColorIs.green()); // Considered on if not gray scale
213 if (pixelShouldBeOn != pixelIsOn) {
214 QColor penColorShouldBe (pixelShouldBeOn ? Qt::green : Qt::black);
215 m_outline->setPen (QPen (penColorShouldBe));
216 }
217}
218
220 QPointF /* posScreen */)
221{
222 LOG4CPP_INFO_S ((*mainCat)) << "DigitizeStatePointMatch::handleMousePress";
223}
224
226 QPointF posScreen)
227{
228 LOG4CPP_INFO_S ((*mainCat)) << "DigitizeStatePointMatch::handleMouseRelease";
229
230 createPermanentPoint (cmdMediator,
231 posScreen);
232
233 findPointsAndShowFirstCandidate (cmdMediator,
234 posScreen);
235}
236
237void DigitizeStatePointMatch::findPointsAndShowFirstCandidate (CmdMediator *cmdMediator,
238 const QPointF &posScreen)
239{
240 LOG4CPP_INFO_S ((*mainCat)) << "DigitizeStatePointMatch::findPointsAndShowFirstCandidate";
241
242 const DocumentModelPointMatch &modelPointMatch = cmdMediator->document().modelPointMatch();
243 const QImage &img = context().mainWindow().imageFiltered();
244
245 QList<PointMatchPixel> samplePointPixels = extractSamplePointPixels (img,
246 modelPointMatch,
247 posScreen);
248
249 QString curveName = activeCurve();
250 const Document &doc = cmdMediator->document();
251 const Curve *curve = doc.curveForCurveName (curveName);
252
253 // The point match algorithm takes a few seconds, so set the cursor so user knows we are processing
254 QApplication::setOverrideCursor(Qt::WaitCursor);
255
256 PointMatchAlgorithm pointMatchAlgorithm (context().isGnuplot());
257 m_candidatePoints = pointMatchAlgorithm.findPoints (samplePointPixels,
258 img,
259 modelPointMatch,
260 curve->points());
261
262 QApplication::restoreOverrideCursor(); // Heavy duty processing has finished
263 context().mainWindow().showTemporaryMessage ("Right arrow adds next matched point");
264
265 popCandidatePoint (cmdMediator);
266}
267
268bool DigitizeStatePointMatch::pixelIsOnInImage (const QImage &img,
269 int x,
270 int y,
271 int radiusLimit) const
272{
273 ColorFilter filter;
274
275 // Examine all nearby pixels
276 bool pixelShouldBeOn = false;
277 for (int xOffset = -radiusLimit; xOffset <= radiusLimit; xOffset++) {
278 for (int yOffset = -radiusLimit; yOffset <= radiusLimit; yOffset++) {
279
280 int radius = qSqrt (xOffset * xOffset + yOffset * yOffset);
281
282 if (radius <= radiusLimit) {
283
284 int xNearby = x + xOffset;
285 int yNearby = y + yOffset;
286
287 if ((0 <= xNearby) &&
288 (0 <= yNearby) &&
289 (xNearby < img.width()) &&
290 (yNearby < img.height())) {
291
292 if (filter.pixelFilteredIsOn (img,
293 xNearby,
294 yNearby)) {
295
296 pixelShouldBeOn = true;
297 break;
298 }
299 }
300 }
301 }
302 }
303
304 return pixelShouldBeOn;
305}
306
307void DigitizeStatePointMatch::popCandidatePoint (CmdMediator *cmdMediator)
308{
309 LOG4CPP_DEBUG_S ((*mainCat)) << "DigitizeStatePointMatch::popCandidatePoint";
310
311 if (m_candidatePoints.count() > 0) {
312
313 // Pop next point from list onto screen
314 QPoint posScreen = m_candidatePoints.first();
315 m_candidatePoints.pop_front ();
316
317 createTemporaryPoint(cmdMediator,
318 posScreen);
319
320 } else {
321
322 // No more points. Inform user
323 QMessageBox::information (0,
324 QObject::tr ("Point Match"),
325 QObject::tr ("There are no more matching points"));
326
327 }
328}
329
330void DigitizeStatePointMatch::promoteCandidatePointToPermanentPoint(CmdMediator *cmdMediator)
331{
332 createPermanentPoint (cmdMediator,
333 m_posCandidatePoint);
334}
335
337{
338 return "DigitizeStatePointMatch";
339}
340
342 const DocumentModelDigitizeCurve & /*modelDigitizeCurve */)
343{
344 LOG4CPP_INFO_S ((*mainCat)) << "DigitizeStatePointMatch::updateModelDigitizeCurve";
345}
346
348{
349 LOG4CPP_INFO_S ((*mainCat)) << "DigitizeStatePointMatch::updateModelSegments";
350}
Command for adding one graph point.
Command queue stack.
Definition CmdMediator.h:24
Document & document()
Provide the Document to commands, primarily for undo/redo processing.
Class for filtering image to remove unimportant information.
Definition ColorFilter.h:19
bool pixelFilteredIsOn(const QImage &image, int x, int y) const
Return true if specified filtered pixel is on.
Model for DlgSettingsCurveProperties and CmdSettingsCurveProperties.
Definition CurveStyles.h:23
const PointStyle pointStyle(const QString &curveName) const
Get method for copying one point style. Cannot return just a reference or else there is a warning abo...
Container for one set of digitized Points.
Definition Curve.h:33
const Points points() const
Return a shallow copy of the Points.
Definition Curve.cpp:392
Base class for all digitizing states. This serves as an interface to DigitizeStateContext.
DigitizeStateContext & context()
Reference to the DigitizeStateContext that contains all the DigitizeStateAbstractBase subclasses,...
void setCursor(CmdMediator *cmdMediator)
Update the cursor according to the current state.
Container for all DigitizeStateAbstractBase subclasses. This functions as the context class in a stan...
void setDragMode(QGraphicsView::DragMode dragMode)
Set QGraphicsView drag mode (in m_view). Called from DigitizeStateAbstractBase subclasses.
void appendNewCmd(CmdMediator *cmdMediator, QUndoCommand *cmd)
Append just-created QUndoCommand to command stack. This is called from DigitizeStateAbstractBase subc...
MainWindow & mainWindow()
Reference to the MainWindow, without const.
virtual QString state() const
State name for debugging.
virtual void handleMouseMove(CmdMediator *cmdMediator, QPointF posScreen)
Handle a mouse move. This is part of an experiment to see if augmenting the cursor in Point Match mod...
virtual QCursor cursor(CmdMediator *cmdMediator) const
Returns the state-specific cursor shape.
virtual void handleCurveChange(CmdMediator *cmdMediator)
Handle the selection of a new curve. At a minimum, DigitizeStateSegment will generate a new set of Se...
virtual void handleMouseRelease(CmdMediator *cmdMediator, QPointF posScreen)
Handle a mouse release that was intercepted earlier.
virtual void handleMousePress(CmdMediator *cmdMediator, QPointF posScreen)
Handle a mouse press that was intercepted earlier.
virtual QString activeCurve() const
Name of the active Curve. This can include AXIS_CURVE_NAME.
virtual void updateModelDigitizeCurve(CmdMediator *cmdMediator, const DocumentModelDigitizeCurve &modelDigitizeCurve)
Update the digitize curve settings.
virtual void begin(CmdMediator *cmdMediator, DigitizeState previousState)
Method that is called at the exact moment a state is entered.
virtual void handleKeyPress(CmdMediator *cmdMediator, Qt::Key key, bool atLeastOneSelectedItem)
Handle a key press that was intercepted earlier.
DigitizeStatePointMatch(DigitizeStateContext &context)
Single constructor.
virtual void updateModelSegments(const DocumentModelSegments &modelSegments)
Update the segments given the new settings.
virtual void end()
Method that is called at the exact moment a state is exited. Typically called just before begin for t...
Model for DlgSettingsDigitizeCurve and CmdSettingsDigitizeCurve.
int cursorSize() const
Get method for effective cursor size.
Model for DlgSettingsPointMatch and CmdSettingsPointMatch.
ColorPalette paletteColorCandidate() const
Get method for candidate color.
double maxPointSize() const
Get method for max point size.
Model for DlgSettingsSegments and CmdSettingsSegments.
Storage of one imported image and the data attached to that image.
Definition Document.h:41
DocumentModelGeneral modelGeneral() const
Get method for DocumentModelGeneral.
Definition Document.cpp:639
DocumentModelPointMatch modelPointMatch() const
Get method for DocumentModelPointMatch.
Definition Document.cpp:653
CurveStyles modelCurveStyles() const
Get method for CurveStyles.
Definition Document.cpp:618
const Curve * curveForCurveName(const QString &curveName) const
See CurvesGraphs::curveForCurveNames, although this also works for AXIS_CURVE_NAME.
Definition Document.cpp:298
Graphics item for drawing a circular or polygonal Point.
void addTemporaryPoint(const QString &identifier, GraphicsPoint *point)
Add one temporary point to m_graphicsLinesForCurves. Non-temporary points are handled by the updateLi...
void removeTemporaryPointIfExists()
Remove temporary point if it exists.
GraphicsPoint * createPoint(const QString &identifier, const PointStyle &pointStyle, const QPointF &posScreen)
Create one QGraphicsItem-based object that represents one Point. It is NOT added to m_graphicsLinesFo...
void showTemporaryMessage(const QString &temporaryMessage)
Show temporary message in status bar.
void updateViewsOfSettings(const QString &activeCurve)
Update curve-specific view of settings. Private version gets active curve name from DigitizeStateCont...
QImage imageFiltered() const
Background image that has been filtered for the current curve. This asserts if a curve-specific image...
QString selectedGraphCurve() const
Curve name that is currently selected in m_cmbCurve.
GraphicsScene & scene()
Scene container for the QImage and QGraphicsItems.
Transformation transformation() const
Return read-only copy of transformation.
Utility class for generating ordinal numbers.
double generateCurvePointOrdinal(const Document &document, const Transformation &transformation, const QPointF &posScreen, const QString &curveName)
Select ordinal so new point curve passes smoothly through existing points.
Algorithm returning a list of points that match the specified point.
Single on or off pixel out of the pixels that define the point match mode's candidate point.
Details for a specific Point.
Definition PointStyle.h:21
void setPaletteColor(ColorPalette paletteColor)
Set method for point color.
static QString temporaryPointIdentifier()
Point identifier for temporary point that is used by DigitzeStateAxis.
Definition Point.cpp:501
Affine transformation between screen and graph coordinates, based on digitized axis points.