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 // Qt::Key_Right is reserved by this state for adding points so we cannot also call
215 // handleKeyPressArrow (which also uses Qt::Key_Right for a different purpose). So
216 // moving point match points around with the direction arrows is not possible
217
218 // The selected key button has to be compatible with GraphicsView::keyPressEvent
219 if (key == Qt::Key_Right) {
220
221 promoteCandidatePointToPermanentPoint (cmdMediator); // This removes the current temporary point
222
223 popCandidatePoint(cmdMediator); // This creates a new temporary point
224
225 }
226}
227
229 QPointF posScreen)
230{
231// LOG4CPP_DEBUG_S ((*mainCat)) << "DigitizeStatePointMatch::handleMouseMove";
232
233 const DocumentModelPointMatch &modelPointMatch = cmdMediator->document().modelPointMatch();
234
235 m_outline->setRect (posScreen.x() - modelPointMatch.maxPointSize() / 2.0,
236 posScreen.y() - modelPointMatch.maxPointSize() / 2.0,
237 modelPointMatch.maxPointSize(),
238 modelPointMatch.maxPointSize());
239
240 const QImage &img = context().mainWindow().imageFiltered();
241 int radiusLimit = cmdMediator->document().modelGeneral().cursorSize();
242 bool pixelShouldBeOn = pixelIsOnInImage (img,
243 qFloor (posScreen.x()),
244 qFloor (posScreen.y()),
245 radiusLimit);
246
247 QColor penColorIs = m_outline->pen().color();
248 bool pixelIsOn = (penColorIs.red () != penColorIs.green()); // Considered on if not gray scale
249 if (pixelShouldBeOn != pixelIsOn) {
250 QColor penColorShouldBe (pixelShouldBeOn ? Qt::green : Qt::black);
251 m_outline->setPen (QPen (penColorShouldBe));
252 }
253}
254
256 QPointF /* posScreen */)
257{
258 LOG4CPP_INFO_S ((*mainCat)) << "DigitizeStatePointMatch::handleMousePress";
259}
260
262 QPointF posScreen)
263{
264 LOG4CPP_INFO_S ((*mainCat)) << "DigitizeStatePointMatch::handleMouseRelease";
265
266 createPermanentPoint (cmdMediator,
267 posScreen);
268
269 findPointsAndShowFirstCandidate (cmdMediator,
270 posScreen);
271}
272
273void DigitizeStatePointMatch::findPointsAndShowFirstCandidate (CmdMediator *cmdMediator,
274 const QPointF &posScreen)
275{
276 LOG4CPP_INFO_S ((*mainCat)) << "DigitizeStatePointMatch::findPointsAndShowFirstCandidate";
277
278 const DocumentModelPointMatch &modelPointMatch = cmdMediator->document().modelPointMatch();
279 const QImage &img = context().mainWindow().imageFiltered();
280
281 QList<PointMatchPixel> samplePointPixels = extractSamplePointPixels (img,
282 modelPointMatch,
283 posScreen);
284
285 QString curveName = activeCurve();
286 const Document &doc = cmdMediator->document();
287 const Curve *curve = doc.curveForCurveName (curveName);
288
289 // The point match algorithm takes a few seconds, so set the cursor so user knows we are processing
290 QApplication::setOverrideCursor(Qt::WaitCursor);
291
292 PointMatchAlgorithm pointMatchAlgorithm (context().isGnuplot());
293 m_candidatePoints = pointMatchAlgorithm.findPoints (samplePointPixels,
294 img,
295 modelPointMatch,
296 curve->points());
297
298 QApplication::restoreOverrideCursor(); // Heavy duty processing has finished
299 context().mainWindow().showTemporaryMessage ("Right arrow adds next matched point");
300
301 popCandidatePoint (cmdMediator);
302}
303
304bool DigitizeStatePointMatch::pixelIsOnInImage (const QImage &img,
305 int x,
306 int y,
307 int radiusLimit) const
308{
309 ColorFilter filter;
310
311 // Examine all nearby pixels
312 bool pixelShouldBeOn = false;
313 for (int xOffset = -radiusLimit; xOffset <= radiusLimit; xOffset++) {
314 for (int yOffset = -radiusLimit; yOffset <= radiusLimit; yOffset++) {
315
316 int radius = qFloor (qSqrt (xOffset * xOffset + yOffset * yOffset));
317
318 if (radius <= radiusLimit) {
319
320 int xNearby = x + xOffset;
321 int yNearby = y + yOffset;
322
323 if ((0 <= xNearby) &&
324 (0 <= yNearby) &&
325 (xNearby < img.width()) &&
326 (yNearby < img.height())) {
327
328 if (filter.pixelFilteredIsOn (img,
329 xNearby,
330 yNearby)) {
331
332 pixelShouldBeOn = true;
333 break;
334 }
335 }
336 }
337 }
338 }
339
340 return pixelShouldBeOn;
341}
342
343void DigitizeStatePointMatch::popCandidatePoint (CmdMediator *cmdMediator)
344{
345 LOG4CPP_DEBUG_S ((*mainCat)) << "DigitizeStatePointMatch::popCandidatePoint";
346
347 if (m_candidatePoints.count() > 0) {
348
349 // Pop next point from list onto screen
350 QPoint posScreen = m_candidatePoints.first();
351 m_candidatePoints.pop_front ();
352
353 createTemporaryPoint(cmdMediator,
354 posScreen);
355
356 } else {
357
358 // No more points. Inform user
359 QMessageBox::information (nullptr,
360 QObject::tr ("Point Match"),
361 QObject::tr ("There are no more matching points"));
362
363 }
364}
365
366void DigitizeStatePointMatch::promoteCandidatePointToPermanentPoint(CmdMediator *cmdMediator)
367{
368 createPermanentPoint (cmdMediator,
369 m_posCandidatePoint);
370}
371
373{
374 return "DigitizeStatePointMatch";
375}
376
378{
379 LOG4CPP_INFO_S ((*mainCat)) << "DigitizeStatePointMatch::updateAfterPointAddition";
380}
381
383 const DocumentModelDigitizeCurve & /*modelDigitizeCurve */)
384{
385 LOG4CPP_INFO_S ((*mainCat)) << "DigitizeStatePointMatch::updateModelDigitizeCurve";
386}
387
389{
390 LOG4CPP_INFO_S ((*mainCat)) << "DigitizeStatePointMatch::updateModelSegments";
391}
const double Z_VALUE
DigitizeState
Set of possible states of Digitize toolbar.
#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.
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
DigitizeStateAbstractBase(DigitizeStateContext &context)
Single constructor.
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:735
DocumentModelPointMatch modelPointMatch() const
Get method for DocumentModelPointMatch.
Definition Document.cpp:770
CurveStyles modelCurveStyles() const
Get method for CurveStyles.
Definition Document.cpp:714
const Curve * curveForCurveName(const QString &curveName) const
See CurvesGraphs::curveForCurveNames, although this also works for AXIS_CURVE_NAME.
Definition Document.cpp:341
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.
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