Engauge Digitizer  2
DlgSettingsPointMatch.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 "CmdMediator.h"
8 #include "CmdSettingsPointMatch.h"
9 #include "DlgSettingsPointMatch.h"
10 #include "EngaugeAssert.h"
11 #include "Logger.h"
12 #include "MainWindow.h"
13 #include <QComboBox>
14 #include <QGraphicsEllipseItem>
15 #include <QGraphicsPixmapItem>
16 #include <QGraphicsRectItem>
17 #include <QGraphicsScene>
18 #include <QGridLayout>
19 #include <QLabel>
20 #include <qmath.h>
21 #include <QPen>
22 #include <QSpinBox>
23 #include "ViewPreview.h"
24 
25 const int POINT_SIZE_MAX = 1024;
26 const int POINT_SIZE_MIN = 5;
27 
29  DlgSettingsAbstractBase (tr ("Point Match"),
30  "DlgSettingsPointMatch",
31  mainWindow),
32  m_scenePreview (0),
33  m_viewPreview (0),
34  m_circle (0),
35  m_modelPointMatchBefore (0),
36  m_modelPointMatchAfter (0)
37 {
38  LOG4CPP_INFO_S ((*mainCat)) << "DlgSettingsPointMatch::DlgSettingsPointMatch";
39 
40  QWidget *subPanel = createSubPanel ();
41  finishPanel (subPanel);
42 }
43 
44 DlgSettingsPointMatch::~DlgSettingsPointMatch()
45 {
46  LOG4CPP_INFO_S ((*mainCat)) << "DlgSettingsPointMatch::~DlgSettingsPointMatch";
47 }
48 
49 QPointF DlgSettingsPointMatch::boxPositionConstraint(const QPointF &posIn)
50 {
51  LOG4CPP_INFO_S ((*mainCat)) << "DlgSettingsPointMatch::boxPositionConstraint";
52 
53  double radius = radiusAlongDiagonal();
54  double diameter = 2.0 * radius;
55 
56  // Do not move any part outside the preview window or else ugly, and unwanted, shifting will occur
57  QPointF pos (posIn);
58  if (pos.x() - radius < 0) {
59  pos.setX (radius);
60  }
61 
62  if (pos.y() - radius < 0) {
63  pos.setY (radius);
64  }
65 
66  if (pos.x() + diameter > m_scenePreview->sceneRect().width ()) {
67  pos.setX (m_scenePreview->sceneRect().width() - diameter);
68  }
69 
70  if (pos.y() + diameter > m_scenePreview->sceneRect().height ()) {
71  pos.setY (m_scenePreview->sceneRect().height() - diameter);
72  }
73 
74  return pos;
75 }
76 
77 void DlgSettingsPointMatch::createControls (QGridLayout *layout,
78  int &row)
79 {
80  LOG4CPP_INFO_S ((*mainCat)) << "DlgSettingsPointMatch::createControls";
81 
82  QLabel *labelPointSize = new QLabel (tr ("Maximum point size (pixels):"));
83  layout->addWidget (labelPointSize, row, 1);
84 
85  m_spinPointSize = new QSpinBox;
86  m_spinPointSize->setWhatsThis (tr ("Select a maximum point size in pixels.\n\n"
87  "Sample match points must fit within a square box, around the cursor, having width and height "
88  "equal to this maximum.\n\n"
89  "This size is also used to determine if a region of pixels that are on, in the processed image, "
90  "should be ignored since that region is wider or taller than this limit.\n\n"
91  "This value has a lower limit"));
92  m_spinPointSize->setMinimum (POINT_SIZE_MIN);
93  m_spinPointSize->setMaximum (POINT_SIZE_MAX);
94  connect (m_spinPointSize, SIGNAL (valueChanged (int)), this, SLOT (slotMaxPointSize (int)));
95  layout->addWidget (m_spinPointSize, row++, 2);
96 
97  QLabel *labelAcceptedPointColor = new QLabel (tr ("Accepted point color:"));
98  layout->addWidget (labelAcceptedPointColor, row, 1);
99 
100  m_cmbAcceptedPointColor = new QComboBox;
101  m_cmbAcceptedPointColor->setWhatsThis (tr ("Select a color for matched points that are accepted"));
102  populateColorComboWithTransparent (*m_cmbAcceptedPointColor);
103  connect (m_cmbAcceptedPointColor, SIGNAL (activated (const QString &)), this, SLOT (slotAcceptedPointColor (const QString &))); // activated() ignores code changes
104  layout->addWidget (m_cmbAcceptedPointColor, row++, 2);
105 
106  QLabel *labelRejectedPointColor = new QLabel (tr ("Rejected point color:"));
107  layout->addWidget (labelRejectedPointColor, row, 1);
108 
109  m_cmbRejectedPointColor = new QComboBox;
110  m_cmbRejectedPointColor->setWhatsThis (tr ("Select a color for matched points that are rejected"));
111  populateColorComboWithTransparent (*m_cmbRejectedPointColor);
112  connect (m_cmbRejectedPointColor, SIGNAL (activated (const QString &)), this, SLOT (slotRejectedPointColor (const QString &))); // activated() ignores code changes
113  layout->addWidget (m_cmbRejectedPointColor, row++, 2);
114 
115  QLabel *labelCandidatePointColor = new QLabel (tr ("Candidate point color:"));
116  layout->addWidget (labelCandidatePointColor, row, 1);
117 
118  m_cmbCandidatePointColor = new QComboBox;
119  m_cmbCandidatePointColor->setWhatsThis (tr ("Select a color for the point being decided upon"));
120  populateColorComboWithTransparent (*m_cmbCandidatePointColor);
121  connect (m_cmbCandidatePointColor, SIGNAL (activated (const QString &)), this, SLOT (slotCandidatePointColor (const QString &))); // activated() ignores code changes
122  layout->addWidget (m_cmbCandidatePointColor, row++, 2);
123 }
124 
125 void DlgSettingsPointMatch::createOptionalSaveDefault (QHBoxLayout * /* layout */)
126 {
127 }
128 
129 void DlgSettingsPointMatch::createPreview (QGridLayout *layout,
130  int &row)
131 {
132  LOG4CPP_INFO_S ((*mainCat)) << "DlgSettingsPointMatch::createPreview";
133 
134  QLabel *labelPreview = new QLabel (tr ("Preview"));
135  layout->addWidget (labelPreview, row++, 0, 1, 4);
136 
137  m_scenePreview = new QGraphicsScene (this);
138  m_viewPreview = new ViewPreview (m_scenePreview,
139  ViewPreview::VIEW_ASPECT_RATIO_VARIABLE,
140  this);
141  m_viewPreview->setWhatsThis (tr ("Preview window shows how current settings affect "
142  "point matching, and how the marked and candidate points are displayed.\n\nThe points are separated "
143  "by the point separation value, and the maximum point size is shown as a box in the center"));
144  m_viewPreview->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
145  m_viewPreview->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
146  m_viewPreview->setMinimumHeight (MINIMUM_PREVIEW_HEIGHT);
147  connect (m_viewPreview, SIGNAL (signalMouseMove (QPointF)), this, SLOT (slotMouseMove (QPointF)));
148 
149  layout->addWidget (m_viewPreview, row++, 0, 1, 4);
150 }
151 
153 {
154  LOG4CPP_INFO_S ((*mainCat)) << "DlgSettingsPointMatch::createSubPanel";
155 
156  QWidget *subPanel = new QWidget ();
157  QGridLayout *layout = new QGridLayout (subPanel);
158  subPanel->setLayout (layout);
159 
160  layout->setColumnStretch(0, 1); // Empty column
161  layout->setColumnStretch(1, 0); // Labels
162  layout->setColumnStretch(2, 0); // Controls
163  layout->setColumnStretch(3, 1); // Empty column
164 
165  int row = 0;
166  createControls (layout, row);
167  createPreview (layout, row);
168  createTemplate ();
169 
170  return subPanel;
171 }
172 
173 void DlgSettingsPointMatch::createTemplate ()
174 {
175  LOG4CPP_INFO_S ((*mainCat)) << "DlgSettingsPointMatch::createTemplate";
176 
177  QPen pen (QBrush (Qt::black), 0);
178 
179  m_circle = new QGraphicsEllipseItem;
180  m_circle->setPen (pen);
181  m_circle->setZValue (100);
182  m_scenePreview->addItem (m_circle);
183 }
184 
186 {
187  LOG4CPP_INFO_S ((*mainCat)) << "DlgSettingsPointMatch::handleOk";
188 
190  cmdMediator ().document(),
191  *m_modelPointMatchBefore,
192  *m_modelPointMatchAfter);
193  cmdMediator ().push (cmd);
194 
195  hide ();
196 }
197 
198 void DlgSettingsPointMatch::initializeBox ()
199 {
200  LOG4CPP_INFO_S ((*mainCat)) << "DlgSettingsPointMatch::initializeBox";
201 
202  m_circle->setPos (cmdMediator().document().pixmap().width () / 2.0,
203  cmdMediator().document().pixmap().height () / 2.0); // Initially box is in center of preview
204 }
205 
207 {
208  LOG4CPP_INFO_S ((*mainCat)) << "DlgSettingsPointMatch::load";
209 
210  setCmdMediator (cmdMediator);
211 
212  // Flush old data
213  if (m_modelPointMatchBefore != 0) {
214  delete m_modelPointMatchBefore;
215  }
216  if (m_modelPointMatchAfter != 0) {
217  delete m_modelPointMatchAfter;
218  }
219 
220  // Save new data
221  m_modelPointMatchBefore = new DocumentModelPointMatch (cmdMediator.document());
222  m_modelPointMatchAfter = new DocumentModelPointMatch (cmdMediator.document());
223 
224  // Sanity checks. Incoming defaults must be acceptable to the local limits
225  ENGAUGE_ASSERT (POINT_SIZE_MIN <= m_modelPointMatchAfter->maxPointSize());
226  ENGAUGE_ASSERT (POINT_SIZE_MAX > m_modelPointMatchAfter->maxPointSize());
227 
228  // Populate controls
229  m_spinPointSize->setValue(m_modelPointMatchAfter->maxPointSize());
230 
231  int indexAccepted = m_cmbAcceptedPointColor->findData(QVariant(m_modelPointMatchAfter->paletteColorAccepted()));
232  ENGAUGE_ASSERT (indexAccepted >= 0);
233  m_cmbAcceptedPointColor->setCurrentIndex(indexAccepted);
234 
235  int indexCandidate = m_cmbCandidatePointColor->findData(QVariant(m_modelPointMatchAfter->paletteColorCandidate()));
236  ENGAUGE_ASSERT (indexCandidate >= 0);
237  m_cmbCandidatePointColor->setCurrentIndex(indexCandidate);
238 
239  int indexRejected = m_cmbRejectedPointColor->findData(QVariant(m_modelPointMatchAfter->paletteColorRejected()));
240  ENGAUGE_ASSERT (indexRejected >= 0);
241  m_cmbRejectedPointColor->setCurrentIndex(indexRejected);
242 
243  initializeBox ();
244 
245  // Fix the preview size using an invisible boundary
246  QGraphicsRectItem *boundary = m_scenePreview->addRect (QRect (0,
247  0,
248  cmdMediator.document().pixmap().width (),
249  cmdMediator.document().pixmap().height ()));
250  boundary->setVisible (false);
251 
252  m_scenePreview->addPixmap (cmdMediator.document().pixmap());
253 
254  updateControls();
255  enableOk (false); // Disable Ok button since there not yet any changes
256  updatePreview();
257 }
258 
259 double DlgSettingsPointMatch::radiusAlongDiagonal () const
260 {
261  double maxPointSize = m_modelPointMatchAfter->maxPointSize();
262 
263  return qSqrt (2.0) * maxPointSize / 2.0;
264 }
265 
266 void DlgSettingsPointMatch::slotAcceptedPointColor (const QString &)
267 {
268  LOG4CPP_INFO_S ((*mainCat)) << "DlgSettingsPointMatch::slotAcceptedPointColor";
269 
270  m_modelPointMatchAfter->setPaletteColorAccepted((ColorPalette) m_cmbAcceptedPointColor->currentData().toInt());
271 
272  updateControls();
273  updatePreview();
274 }
275 
276 void DlgSettingsPointMatch::slotCandidatePointColor (const QString &)
277 {
278  LOG4CPP_INFO_S ((*mainCat)) << "DlgSettingsPointMatch::slotCandidatePointColor";
279 
280  m_modelPointMatchAfter->setPaletteColorCandidate((ColorPalette) m_cmbCandidatePointColor->currentData().toInt());
281  updateControls();
282  updatePreview();
283 }
284 
285 void DlgSettingsPointMatch::slotMaxPointSize (int maxPointSize)
286 {
287  LOG4CPP_INFO_S ((*mainCat)) << "DlgSettingsPointMatch::slotMaxPointSize";
288 
289  m_modelPointMatchAfter->setMaxPointSize(maxPointSize);
290  updateControls();
291  updatePreview();
292 }
293 
294 void DlgSettingsPointMatch::slotMouseMove (QPointF pos)
295 {
296  // Move the box so it follows the mouse move, making sure to keep it entirely inside the view to
297  // prevent autoresizing by QGraphicsView
298  pos = boxPositionConstraint (pos);
299 
300  m_circle->setPos (pos);
301 }
302 
303 void DlgSettingsPointMatch::slotRejectedPointColor (const QString &)
304 {
305  LOG4CPP_INFO_S ((*mainCat)) << "DlgSettingsPointMatch::slotRejectedPointColor";
306 
307  m_modelPointMatchAfter->setPaletteColorRejected((ColorPalette) m_cmbRejectedPointColor->currentData().toInt());
308  updateControls();
309  updatePreview();
310 }
311 
312 void DlgSettingsPointMatch::updateControls()
313 {
314  // All controls in this dialog are always fully validated so the ok button is always enabled (after the first change)
315  enableOk (true);
316 }
317 
318 void DlgSettingsPointMatch::updatePreview()
319 {
320  // Geometry parameters
321  double maxPointSize = m_modelPointMatchAfter->maxPointSize();
322 
323  double xLeft = -1.0 * maxPointSize / 2.0;
324  double yTop = -1.0 * maxPointSize / 2.0;
325 
326  // Update circle size
327  m_circle->setRect (xLeft,
328  yTop,
329  maxPointSize,
330  maxPointSize);
331 }
Model for DlgSettingsPointMatch and CmdSettingsPointMatch.
void setPaletteColorCandidate(ColorPalette paletteColorCandidate)
Set method for candidate color.
void setCmdMediator(CmdMediator &cmdMediator)
Store CmdMediator for easy access by the leaf class.
Document & document()
Provide the Document to commands, primarily for undo/redo processing.
Definition: CmdMediator.cpp:72
Command for DlgSettingsPointMatch.
ColorPalette paletteColorCandidate() const
Get method for candidate color.
Class that modifies QGraphicsView to automatically expand/shrink the view to fit the window...
Definition: ViewPreview.h:14
void setMaxPointSize(double maxPointSize)
Set method for max point size.
ColorPalette paletteColorAccepted() const
Get method for accepted color.
void setPaletteColorRejected(ColorPalette paletteColorRejected)
Set method for rejected color.
static int MINIMUM_PREVIEW_HEIGHT
Dialog layout constant that guarantees preview has sufficent room.
virtual void handleOk()
Process slotOk.
void enableOk(bool enable)
Let leaf subclass control the Ok button.
void finishPanel(QWidget *subPanel, int minimumWidth=MINIMUM_DIALOG_WIDTH)
Add Ok and Cancel buttons to subpanel to get the whole dialog.
Command queue stack.
Definition: CmdMediator.h:23
virtual void load(CmdMediator &cmdMediator)
Load settings from Document.
void populateColorComboWithTransparent(QComboBox &combo)
Add colors in color palette to combobox, with transparent entry at end.
Abstract base class for all Settings dialogs.
virtual QWidget * createSubPanel()
Create dialog-specific panel to which base class will add Ok and Cancel buttons.
QPixmap pixmap() const
Return the image that is being digitized.
Definition: Document.cpp:742
DlgSettingsPointMatch(MainWindow &mainWindow)
Single constructor.
ColorPalette paletteColorRejected() const
Get method for rejected color.
MainWindow & mainWindow()
Get method for MainWindow.
virtual void createOptionalSaveDefault(QHBoxLayout *layout)
Let subclass define an optional Save As Default button.
double maxPointSize() const
Get method for max point size.
Main window consisting of menu, graphics scene, status bar and optional toolbars as a Single Document...
Definition: MainWindow.h:83
CmdMediator & cmdMediator()
Provide access to Document information wrapped inside CmdMediator.
void setPaletteColorAccepted(ColorPalette paletteColorAccepted)
Set method for accepted color.