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);
60
61 // Add outline that will move with the cursor
62 m_outline = new QGraphicsEllipseItem ();
63 context().mainWindow().scene().addItem (m_outline);
64 m_outline->setPen (QPen (Qt::black));
65 m_outline->setVisible (true);
66 m_outline->setZValue (Z_VALUE);
67}
68
70 const QSize &viewSize) const
71{
72 return canPasteProtected (transformation,
73 viewSize);
74}
75
76void DigitizeStatePointMatch::createPermanentPoint (CmdMediator *cmdMediator,
77 const QPointF &posScreen)
78{
79 // Create command to add point
80 OrdinalGenerator ordinalGenerator;
81 Document &document = cmdMediator->document ();
82 const Transformation &transformation = context ().mainWindow ().transformation();
83 QUndoCommand *cmd = new CmdAddPointGraph (context ().mainWindow(),
84 document,
85 context ().mainWindow().selectedGraphCurve(),
86 posScreen,
87 ordinalGenerator.generateCurvePointOrdinal(document,
88 transformation,
89 posScreen,
90 activeCurve ()));
91 context().appendNewCmd(cmdMediator,
92 cmd);
93
94}
95
96void DigitizeStatePointMatch::createTemporaryPoint (CmdMediator *cmdMediator,
97 const QPoint &posScreen)
98{
99 LOG4CPP_DEBUG_S ((*mainCat)) << "DigitizeStatePointMatch::createTemporaryPoint";
100
101 GeometryWindow *NULL_GEOMETRY_WINDOW = nullptr;
102
103 const DocumentModelPointMatch &modelPointMatch = cmdMediator->document().modelPointMatch();
104
105 // Get point style for current graph, and then override with candidate color
106 const CurveStyles &curveStyles = cmdMediator->document().modelCurveStyles();
107 PointStyle pointStyle = curveStyles.pointStyle (activeCurve());
108 pointStyle.setPaletteColor (modelPointMatch.paletteColorCandidate());
109
110 // Temporary point that user can see while DlgEditPoint is active
112 pointStyle,
113 posScreen,
114 NULL_GEOMETRY_WINDOW);
115
116 context().mainWindow().scene().removeTemporaryPointIfExists(); // Only one temporary point at a time is allowed
117
119 point);
120 m_posCandidatePoint = posScreen;
121}
122
123QCursor DigitizeStatePointMatch::cursor(CmdMediator * /* cmdMediator */) const
124{
125 LOG4CPP_DEBUG_S ((*mainCat)) << "DigitizeStatePointMatch::cursor";
126
127 return QCursor (Qt::ArrowCursor);
128}
129
131{
132 LOG4CPP_INFO_S ((*mainCat)) << "DigitizeStatePointMatch::end";
133
134 // Remove candidate point which may or may not exist at this point
136
137 // Remove outline before leaving state
138 ENGAUGE_CHECK_PTR (m_outline);
139 context().mainWindow().scene().removeItem (m_outline);
140 m_outline = nullptr;
141}
142
143QList<PointMatchPixel> DigitizeStatePointMatch::extractSamplePointPixels (const QImage &img,
144 const DocumentModelPointMatch &modelPointMatch,
145 const QPointF &posScreen) const
146{
147 LOG4CPP_INFO_S ((*mainCat)) << "DigitizeStatePointMatch::extractSamplePointPixels";
148
149 // All points inside modelPointMatch.maxPointSize() are collected, whether or not they
150 // are on or off. Originally only the on points were collected, but obvious mismatches
151 // were happening (example, 3x3 point would appear to be found in several places inside 8x32 rectangle)
152 QList<PointMatchPixel> samplePointPixels;
153
154 int radiusMax = qFloor (modelPointMatch.maxPointSize() / 2);
155
156 ColorFilter colorFilter;
157 for (int xOffset = -radiusMax; xOffset <= radiusMax; xOffset++) {
158 for (int yOffset = -radiusMax; yOffset <= radiusMax; yOffset++) {
159
160 int x = qFloor (posScreen.x() + xOffset);
161 int y = qFloor (posScreen.y() + yOffset);
162 int radius = qFloor (qSqrt (xOffset * xOffset + yOffset * yOffset));
163
164 if (radius <= radiusMax) {
165
166 bool pixelIsOn = colorFilter.pixelFilteredIsOn (img,
167 x,
168 y);
169
170 PointMatchPixel point (xOffset,
171 yOffset,
172 pixelIsOn);
173
174 samplePointPixels.push_back (point);
175 }
176 }
177 }
178
179 return samplePointPixels;
180}
181
183 const QString &pointIdentifier)
184{
185 LOG4CPP_INFO_S ((*mainCat)) << "DigitizeStatePointMatch::handleContextMenuEventAxis "
186 << " point=" << pointIdentifier.toLatin1 ().data ();
187}
188
190 const QStringList &pointIdentifiers)
191{
192 LOG4CPP_INFO_S ((*mainCat)) << "DigitizeStatePointMatch ::handleContextMenuEventGraph "
193 << "points=" << pointIdentifiers.join(",").toLatin1 ().data ();
194}
195
197{
198 LOG4CPP_INFO_S ((*mainCat)) << "DigitizeStatePointMatch::handleCurveChange";
199}
200
202 Qt::Key key,
203 bool /* atLeastOneSelectedItem */)
204{
205 LOG4CPP_INFO_S ((*mainCat)) << "DigitizeStatePointMatch::handleKeyPress"
206 << " key=" << QKeySequence (key).toString ().toLatin1 ().data ();
207
208 // The selected key button has to be compatible with GraphicsView::keyPressEvent
209 if (key == Qt::Key_Right) {
210
211 promoteCandidatePointToPermanentPoint (cmdMediator); // This removes the current temporary point
212
213 popCandidatePoint(cmdMediator); // This creates a new temporary point
214
215 }
216}
217
219 QPointF posScreen)
220{
221// LOG4CPP_DEBUG_S ((*mainCat)) << "DigitizeStatePointMatch::handleMouseMove";
222
223 const DocumentModelPointMatch &modelPointMatch = cmdMediator->document().modelPointMatch();
224
225 m_outline->setRect (posScreen.x() - modelPointMatch.maxPointSize() / 2.0,
226 posScreen.y() - modelPointMatch.maxPointSize() / 2.0,
227 modelPointMatch.maxPointSize(),
228 modelPointMatch.maxPointSize());
229
230 const QImage &img = context().mainWindow().imageFiltered();
231 int radiusLimit = cmdMediator->document().modelGeneral().cursorSize();
232 bool pixelShouldBeOn = pixelIsOnInImage (img,
233 qFloor (posScreen.x()),
234 qFloor (posScreen.y()),
235 radiusLimit);
236
237 QColor penColorIs = m_outline->pen().color();
238 bool pixelIsOn = (penColorIs.red () != penColorIs.green()); // Considered on if not gray scale
239 if (pixelShouldBeOn != pixelIsOn) {
240 QColor penColorShouldBe (pixelShouldBeOn ? Qt::green : Qt::black);
241 m_outline->setPen (QPen (penColorShouldBe));
242 }
243}
244
246 QPointF /* posScreen */)
247{
248 LOG4CPP_INFO_S ((*mainCat)) << "DigitizeStatePointMatch::handleMousePress";
249}
250
252 QPointF posScreen)
253{
254 LOG4CPP_INFO_S ((*mainCat)) << "DigitizeStatePointMatch::handleMouseRelease";
255
256 createPermanentPoint (cmdMediator,
257 posScreen);
258
259 findPointsAndShowFirstCandidate (cmdMediator,
260 posScreen);
261}
262
263void DigitizeStatePointMatch::findPointsAndShowFirstCandidate (CmdMediator *cmdMediator,
264 const QPointF &posScreen)
265{
266 LOG4CPP_INFO_S ((*mainCat)) << "DigitizeStatePointMatch::findPointsAndShowFirstCandidate";
267
268 const DocumentModelPointMatch &modelPointMatch = cmdMediator->document().modelPointMatch();
269 const QImage &img = context().mainWindow().imageFiltered();
270
271 QList<PointMatchPixel> samplePointPixels = extractSamplePointPixels (img,
272 modelPointMatch,
273 posScreen);
274
275 QString curveName = activeCurve();
276 const Document &doc = cmdMediator->document();
277 const Curve *curve = doc.curveForCurveName (curveName);
278
279 // The point match algorithm takes a few seconds, so set the cursor so user knows we are processing
280 QApplication::setOverrideCursor(Qt::WaitCursor);
281
282 PointMatchAlgorithm pointMatchAlgorithm (context().isGnuplot());
283 m_candidatePoints = pointMatchAlgorithm.findPoints (samplePointPixels,
284 img,
285 modelPointMatch,
286 curve->points());
287
288 QApplication::restoreOverrideCursor(); // Heavy duty processing has finished
289 context().mainWindow().showTemporaryMessage ("Right arrow adds next matched point");
290
291 popCandidatePoint (cmdMediator);
292}
293
294bool DigitizeStatePointMatch::pixelIsOnInImage (const QImage &img,
295 int x,
296 int y,
297 int radiusLimit) const
298{
299 ColorFilter filter;
300
301 // Examine all nearby pixels
302 bool pixelShouldBeOn = false;
303 for (int xOffset = -radiusLimit; xOffset <= radiusLimit; xOffset++) {
304 for (int yOffset = -radiusLimit; yOffset <= radiusLimit; yOffset++) {
305
306 int radius = qFloor (qSqrt (xOffset * xOffset + yOffset * yOffset));
307
308 if (radius <= radiusLimit) {
309
310 int xNearby = x + xOffset;
311 int yNearby = y + yOffset;
312
313 if ((0 <= xNearby) &&
314 (0 <= yNearby) &&
315 (xNearby < img.width()) &&
316 (yNearby < img.height())) {
317
318 if (filter.pixelFilteredIsOn (img,
319 xNearby,
320 yNearby)) {
321
322 pixelShouldBeOn = true;
323 break;
324 }
325 }
326 }
327 }
328 }
329
330 return pixelShouldBeOn;
331}
332
333void DigitizeStatePointMatch::popCandidatePoint (CmdMediator *cmdMediator)
334{
335 LOG4CPP_DEBUG_S ((*mainCat)) << "DigitizeStatePointMatch::popCandidatePoint";
336
337 if (m_candidatePoints.count() > 0) {
338
339 // Pop next point from list onto screen
340 QPoint posScreen = m_candidatePoints.first();
341 m_candidatePoints.pop_front ();
342
343 createTemporaryPoint(cmdMediator,
344 posScreen);
345
346 } else {
347
348 // No more points. Inform user
349 QMessageBox::information (nullptr,
350 QObject::tr ("Point Match"),
351 QObject::tr ("There are no more matching points"));
352
353 }
354}
355
356void DigitizeStatePointMatch::promoteCandidatePointToPermanentPoint(CmdMediator *cmdMediator)
357{
358 createPermanentPoint (cmdMediator,
359 m_posCandidatePoint);
360}
361
363{
364 return "DigitizeStatePointMatch";
365}
366
368{
369 LOG4CPP_INFO_S ((*mainCat)) << "DigitizeStatePointMatch::updateAfterPointAddition";
370}
371
373 const DocumentModelDigitizeCurve & /*modelDigitizeCurve */)
374{
375 LOG4CPP_INFO_S ((*mainCat)) << "DigitizeStatePointMatch::updateModelDigitizeCurve";
376}
377
379{
380 LOG4CPP_INFO_S ((*mainCat)) << "DigitizeStatePointMatch::updateModelSegments";
381}
DigitizeState
Set of possible states of Digitize toolbar.
const double Z_VALUE
#define ENGAUGE_CHECK_PTR(ptr)
#endif
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:451
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 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:42
DocumentModelGeneral modelGeneral() const
Get method for DocumentModelGeneral.
Definition Document.cpp:723
DocumentModelPointMatch modelPointMatch() const
Get method for DocumentModelPointMatch.
Definition Document.cpp:744
CurveStyles modelCurveStyles() const
Get method for CurveStyles.
Definition Document.cpp:702
const Curve * curveForCurveName(const QString &curveName) const
See CurvesGraphs::curveForCurveNames, although this also works for AXIS_CURVE_NAME.
Definition Document.cpp:335
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...
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