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