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