Engauge Digitizer 2
Loading...
Searching...
No Matches
DigitizeStatePointMatch.cpp
Go to the documentation of this file.
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"
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#include <QSize>
32#include "Transformation.h"
33
34const double Z_VALUE = 200.0;
35
38 m_outline (nullptr),
39 m_candidatePoint (nullptr)
40{
41}
42
46
51
53 DigitizeState /* previousState */)
54{
55 LOG4CPP_INFO_S ((*mainCat)) << "DigitizeStatePointMatch::begin";
56
57 setCursor(cmdMediator);
58 context().setDragMode(QGraphicsView::NoDrag);
61
62 // Add outline that will move with the cursor
63 m_outline = new QGraphicsEllipseItem ();
64 context().mainWindow().scene().addItem (m_outline);
65 m_outline->setPen (QPen (Qt::black));
66 m_outline->setVisible (true);
67 m_outline->setZValue (Z_VALUE);
68}
69
71 const QSize &viewSize) const
72{
73 return canPasteProtected (transformation,
74 viewSize);
75}
76
77void DigitizeStatePointMatch::createPermanentPoint (CmdMediator *cmdMediator,
78 const QPointF &posScreen)
79{
80 // Create command to add point
81 OrdinalGenerator ordinalGenerator;
82 Document &document = cmdMediator->document ();
83 const Transformation &transformation = context ().mainWindow ().transformation();
84 QUndoCommand *cmd = new CmdAddPointGraph (context ().mainWindow(),
85 document,
86 context ().mainWindow().selectedGraphCurve(),
87 posScreen,
88 ordinalGenerator.generateCurvePointOrdinal(document,
89 transformation,
90 posScreen,
91 activeCurve ()));
92 context().appendNewCmd(cmdMediator,
93 cmd);
94
95}
96
97void DigitizeStatePointMatch::createTemporaryPoint (CmdMediator *cmdMediator,
98 const QPoint &posScreen)
99{
100 LOG4CPP_DEBUG_S ((*mainCat)) << "DigitizeStatePointMatch::createTemporaryPoint";
101
102 GeometryWindow *NULL_GEOMETRY_WINDOW = nullptr;
103
104 const DocumentModelPointMatch &modelPointMatch = cmdMediator->document().modelPointMatch();
105
106 // Get point style for current graph, and then override with candidate color
107 const CurveStyles &curveStyles = cmdMediator->document().modelCurveStyles();
108 PointStyle pointStyle = curveStyles.pointStyle (activeCurve());
109 pointStyle.setPaletteColor (modelPointMatch.paletteColorCandidate());
110
111 // Temporary point that user can see while DlgEditPoint is active
113 pointStyle,
114 posScreen,
115 NULL_GEOMETRY_WINDOW);
116
117 context().mainWindow().scene().removeTemporaryPointIfExists(); // Only one temporary point at a time is allowed
118
120 point);
121 m_posCandidatePoint = posScreen;
122}
123
124QCursor DigitizeStatePointMatch::cursor(CmdMediator * /* cmdMediator */) const
125{
126 LOG4CPP_DEBUG_S ((*mainCat)) << "DigitizeStatePointMatch::cursor";
127
128 return QCursor (Qt::ArrowCursor);
129}
130
132{
133 LOG4CPP_INFO_S ((*mainCat)) << "DigitizeStatePointMatch::end";
134
135 // Remove candidate point which may or may not exist at this point
137
138 // Remove outline before leaving state
139 ENGAUGE_CHECK_PTR (m_outline);
140 context().mainWindow().scene().removeItem (m_outline);
141 m_outline = nullptr;
142}
143
144QList<PointMatchPixel> DigitizeStatePointMatch::extractSamplePointPixels (const QImage &img,
145 const DocumentModelPointMatch &modelPointMatch,
146 const QPointF &posScreen) const
147{
148 LOG4CPP_INFO_S ((*mainCat)) << "DigitizeStatePointMatch::extractSamplePointPixels";
149
150 // All points inside modelPointMatch.maxPointSize() are collected, whether or not they
151 // are on or off. Originally only the on points were collected, but obvious mismatches
152 // were happening (example, 3x3 point would appear to be found in several places inside 8x32 rectangle)
153 QList<PointMatchPixel> samplePointPixels;
154
155 int radiusMax = qFloor (modelPointMatch.maxPointSize() / 2);
156
157 ColorFilter colorFilter;
158 for (int xOffset = -radiusMax; xOffset <= radiusMax; xOffset++) {
159 for (int yOffset = -radiusMax; yOffset <= radiusMax; yOffset++) {
160
161 int x = qFloor (posScreen.x() + xOffset);
162 int y = qFloor (posScreen.y() + yOffset);
163 int radius = qFloor (qSqrt (xOffset * xOffset + yOffset * yOffset));
164
165 if (radius <= radiusMax) {
166
167 bool pixelIsOn = colorFilter.pixelFilteredIsOn (img,
168 x,
169 y);
170
171 PointMatchPixel point (xOffset,
172 yOffset,
173 pixelIsOn);
174
175 samplePointPixels.push_back (point);
176 }
177 }
178 }
179
180 return samplePointPixels;
181}
182
184{
185 return false;
186}
187
189 const QString &pointIdentifier)
190{
191 LOG4CPP_INFO_S ((*mainCat)) << "DigitizeStatePointMatch::handleContextMenuEventAxis "
192 << " point=" << pointIdentifier.toLatin1 ().data ();
193}
194
196 const QStringList &pointIdentifiers)
197{
198 LOG4CPP_INFO_S ((*mainCat)) << "DigitizeStatePointMatch ::handleContextMenuEventGraph "
199 << "points=" << pointIdentifiers.join(",").toLatin1 ().data ();
200}
201
203{
204 LOG4CPP_INFO_S ((*mainCat)) << "DigitizeStatePointMatch::handleCurveChange";
205}
206
208 Qt::Key key,
209 bool /* atLeastOneSelectedItem */)
210{
211 LOG4CPP_INFO_S ((*mainCat)) << "DigitizeStatePointMatch::handleKeyPress"
212 << " key=" << QKeySequence (key).toString ().toLatin1 ().data ();
213
214 // The selected key button has to be compatible with GraphicsView::keyPressEvent
215 if (key == Qt::Key_Right) {
216
217 promoteCandidatePointToPermanentPoint (cmdMediator); // This removes the current temporary point
218
219 popCandidatePoint(cmdMediator); // This creates a new temporary point
220
221 }
222}
223
225 QPointF posScreen)
226{
227// LOG4CPP_DEBUG_S ((*mainCat)) << "DigitizeStatePointMatch::handleMouseMove";
228
229 const DocumentModelPointMatch &modelPointMatch = cmdMediator->document().modelPointMatch();
230
231 m_outline->setRect (posScreen.x() - modelPointMatch.maxPointSize() / 2.0,
232 posScreen.y() - modelPointMatch.maxPointSize() / 2.0,
233 modelPointMatch.maxPointSize(),
234 modelPointMatch.maxPointSize());
235
236 const QImage &img = context().mainWindow().imageFiltered();
237 int radiusLimit = cmdMediator->document().modelGeneral().cursorSize();
238 bool pixelShouldBeOn = pixelIsOnInImage (img,
239 qFloor (posScreen.x()),
240 qFloor (posScreen.y()),
241 radiusLimit);
242
243 QColor penColorIs = m_outline->pen().color();
244 bool pixelIsOn = (penColorIs.red () != penColorIs.green()); // Considered on if not gray scale
245 if (pixelShouldBeOn != pixelIsOn) {
246 QColor penColorShouldBe (pixelShouldBeOn ? Qt::green : Qt::black);
247 m_outline->setPen (QPen (penColorShouldBe));
248 }
249}
250
252 QPointF /* posScreen */)
253{
254 LOG4CPP_INFO_S ((*mainCat)) << "DigitizeStatePointMatch::handleMousePress";
255}
256
258 QPointF posScreen)
259{
260 LOG4CPP_INFO_S ((*mainCat)) << "DigitizeStatePointMatch::handleMouseRelease";
261
262 createPermanentPoint (cmdMediator,
263 posScreen);
264
265 findPointsAndShowFirstCandidate (cmdMediator,
266 posScreen);
267}
268
269void DigitizeStatePointMatch::findPointsAndShowFirstCandidate (CmdMediator *cmdMediator,
270 const QPointF &posScreen)
271{
272 LOG4CPP_INFO_S ((*mainCat)) << "DigitizeStatePointMatch::findPointsAndShowFirstCandidate";
273
274 const DocumentModelPointMatch &modelPointMatch = cmdMediator->document().modelPointMatch();
275 const QImage &img = context().mainWindow().imageFiltered();
276
277 QList<PointMatchPixel> samplePointPixels = extractSamplePointPixels (img,
278 modelPointMatch,
279 posScreen);
280
281 QString curveName = activeCurve();
282 const Document &doc = cmdMediator->document();
283 const Curve *curve = doc.curveForCurveName (curveName);
284
285 // The point match algorithm takes a few seconds, so set the cursor so user knows we are processing
286 QApplication::setOverrideCursor(Qt::WaitCursor);
287
288 PointMatchAlgorithm pointMatchAlgorithm (context().isGnuplot());
289 m_candidatePoints = pointMatchAlgorithm.findPoints (samplePointPixels,
290 img,
291 modelPointMatch,
292 curve->points());
293
294 QApplication::restoreOverrideCursor(); // Heavy duty processing has finished
295 context().mainWindow().showTemporaryMessage ("Right arrow adds next matched point");
296
297 popCandidatePoint (cmdMediator);
298}
299
300bool DigitizeStatePointMatch::pixelIsOnInImage (const QImage &img,
301 int x,
302 int y,
303 int radiusLimit) const
304{
305 ColorFilter filter;
306
307 // Examine all nearby pixels
308 bool pixelShouldBeOn = false;
309 for (int xOffset = -radiusLimit; xOffset <= radiusLimit; xOffset++) {
310 for (int yOffset = -radiusLimit; yOffset <= radiusLimit; yOffset++) {
311
312 int radius = qFloor (qSqrt (xOffset * xOffset + yOffset * yOffset));
313
314 if (radius <= radiusLimit) {
315
316 int xNearby = x + xOffset;
317 int yNearby = y + yOffset;
318
319 if ((0 <= xNearby) &&
320 (0 <= yNearby) &&
321 (xNearby < img.width()) &&
322 (yNearby < img.height())) {
323
324 if (filter.pixelFilteredIsOn (img,
325 xNearby,
326 yNearby)) {
327
328 pixelShouldBeOn = true;
329 break;
330 }
331 }
332 }
333 }
334 }
335
336 return pixelShouldBeOn;
337}
338
339void DigitizeStatePointMatch::popCandidatePoint (CmdMediator *cmdMediator)
340{
341 LOG4CPP_DEBUG_S ((*mainCat)) << "DigitizeStatePointMatch::popCandidatePoint";
342
343 if (m_candidatePoints.count() > 0) {
344
345 // Pop next point from list onto screen
346 QPoint posScreen = m_candidatePoints.first();
347 m_candidatePoints.pop_front ();
348
349 createTemporaryPoint(cmdMediator,
350 posScreen);
351
352 } else {
353
354 // No more points. Inform user
355 QMessageBox::information (nullptr,
356 QObject::tr ("Point Match"),
357 QObject::tr ("There are no more matching points"));
358
359 }
360}
361
362void DigitizeStatePointMatch::promoteCandidatePointToPermanentPoint(CmdMediator *cmdMediator)
363{
364 createPermanentPoint (cmdMediator,
365 m_posCandidatePoint);
366}
367
369{
370 return "DigitizeStatePointMatch";
371}
372
374{
375 LOG4CPP_INFO_S ((*mainCat)) << "DigitizeStatePointMatch::updateAfterPointAddition";
376}
377
379 const DocumentModelDigitizeCurve & /*modelDigitizeCurve */)
380{
381 LOG4CPP_INFO_S ((*mainCat)) << "DigitizeStatePointMatch::updateModelDigitizeCurve";
382}
383
385{
386 LOG4CPP_INFO_S ((*mainCat)) << "DigitizeStatePointMatch::updateModelSegments";
387}
DigitizeState
Set of possible states of Digitize toolbar.
const double Z_VALUE
#define ENGAUGE_CHECK_PTR(ptr)
Drop in replacement for Q_CHECK_PTR.
log4cpp::Category * mainCat
Definition Logger.cpp:14
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:21
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:34
const Points points() const
Return a shallow copy of the Points.
Definition Curve.cpp:455
Base class for all digitizing states. This serves as an interface to DigitizeStateContext.
bool canPasteProtected(const Transformation &transformation, const QSize &viewSize) const
Protected version of canPaste method. Some, but not all, leaf classes use this method.
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 handleContextMenuEventGraph(CmdMediator *cmdMediator, const QStringList &pointIdentifiers)
Handle a right click, on a graph point, that was intercepted earlier.
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 bool canPaste(const Transformation &transformation, const QSize &viewSize) const
Return true if there is good data in the clipboard for pasting, and that is compatible with the curre...
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 handleContextMenuEventAxis(CmdMediator *cmdMediator, const QString &pointIdentifier)
Handle a right click, on an axis point, that was intercepted earlier.
virtual void updateModelDigitizeCurve(CmdMediator *cmdMediator, const DocumentModelDigitizeCurve &modelDigitizeCurve)
Update the digitize curve settings.
virtual bool guidelinesAreSelectable() const
Enable/disable guidelines according to state.
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.
virtual void updateAfterPointAddition()
Update graphics attributes after possible new points. This is useful for highlight opacity.
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:44
DocumentModelGeneral modelGeneral() const
Get method for DocumentModelGeneral.
Definition Document.cpp:726
DocumentModelPointMatch modelPointMatch() const
Get method for DocumentModelPointMatch.
Definition Document.cpp:754
CurveStyles modelCurveStyles() const
Get method for CurveStyles.
Definition Document.cpp:705
const Curve * curveForCurveName(const QString &curveName) const
See CurvesGraphs::curveForCurveNames, although this also works for AXIS_CURVE_NAME.
Definition Document.cpp:338
Window that displays the geometry information, as a table, for the current curve.
Graphics item for drawing a circular or polygonal Point.
GraphicsPoint * createPoint(const QString &identifier, const PointStyle &pointStyle, const QPointF &posScreen, GeometryWindow *geometryWindow)
Create one QGraphicsItem-based object that represents one Point. It is NOT added to m_graphicsLinesFo...
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.
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...
void handleGuidelinesActiveChange(bool active)
Handle Guidelines active status toggle.
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:519
Affine transformation between screen and graph coordinates, based on digitized axis points.
#define LOG4CPP_INFO_S(logger)
Definition convenience.h:18
#define LOG4CPP_DEBUG_S(logger)
Definition convenience.h:20