Engauge Digitizer  2
DlgSettingsSegments.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 "CmdSettingsSegments.h"
9 #include "DlgSettingsSegments.h"
10 #include "EngaugeAssert.h"
11 #include "GeometryWindow.h"
12 #include "Logger.h"
13 #include "MainWindow.h"
14 #include "PointStyle.h"
15 #include <QCheckBox>
16 #include <QComboBox>
17 #include <QGridLayout>
18 #include <QGraphicsScene>
19 #include <QLabel>
20 #include <qmath.h>
21 #include <QSpinBox>
22 #include "Segment.h"
23 #include "SegmentFactory.h"
24 #include "ViewPreview.h"
25 
26 const int MINIMUM_HEIGHT = 540;
27 const int MIN_LENGTH_MIN = 1;
28 const int MIN_LENGTH_MAX = 10000;
29 const int POINT_SEPARATION_MIN = 5;
30 const int POINT_SEPARATION_MAX = 10000;
31 
32 const int IMAGE_WIDTH = 400;
33 const int IMAGE_HEIGHT = 350;
34 
35 const double TWOPI = 2.0 * 3.1415926535;
36 
37 const double BRUSH_WIDTH = 2.0;
38 
40  DlgSettingsAbstractBase (tr ("Segment Fill"),
41  "DlgSettingsSegments",
42  mainWindow),
43  m_scenePreview (0),
44  m_viewPreview (0),
45  m_modelSegmentsBefore (0),
46  m_modelSegmentsAfter (0),
47  m_loading (false)
48 {
49  LOG4CPP_INFO_S ((*mainCat)) << "DlgSettingsSegments::DlgSettingsSegments";
50 
51  QWidget *subPanel = createSubPanel ();
52  finishPanel (subPanel);
53 }
54 
55 DlgSettingsSegments::~DlgSettingsSegments()
56 {
57  LOG4CPP_INFO_S ((*mainCat)) << "DlgSettingsSegments::~DlgSettingsSegments";
58 }
59 
60 void DlgSettingsSegments::clearPoints ()
61 {
62  LOG4CPP_INFO_S ((*mainCat)) << "DlgSettingsSegments::clearPoints";
63 
64  QList<GraphicsPoint*>::iterator itrP;
65  for (itrP = m_points.begin(); itrP != m_points.end(); itrP++) {
66  GraphicsPoint *point = *itrP;
67  delete point;
68  }
69 
70  m_points.clear();
71 }
72 
73 void DlgSettingsSegments::createControls (QGridLayout *layout,
74  int &row)
75 {
76  LOG4CPP_INFO_S ((*mainCat)) << "DlgSettingsSegments::createControls";
77 
78  QLabel *labelMinLength = new QLabel(tr ("Minimum length (points):"));
79  layout->addWidget(labelMinLength, row, 1);
80 
81  m_spinMinLength = new QSpinBox;
82  m_spinMinLength->setRange (MIN_LENGTH_MIN, MIN_LENGTH_MAX);
83  m_spinMinLength->setWhatsThis (tr ("Select a minimum number of points in a segment.\n\n"
84  "Only segments with more points will be created.\n\n"
85  "This value should be as large as possible to reduce memory usage. This value has "
86  "a lower limit"));
87  connect (m_spinMinLength, SIGNAL (valueChanged (const QString &)), this, SLOT (slotMinLength (const QString &)));
88  layout->addWidget(m_spinMinLength, row++, 2);
89 
90  QLabel *labelPointSeparation = new QLabel(tr ("Point separation (pixels):"));
91  layout->addWidget (labelPointSeparation, row, 1);
92 
93  m_spinPointSeparation = new QSpinBox;
94  m_spinPointSeparation->setRange (POINT_SEPARATION_MIN, POINT_SEPARATION_MAX);
95  m_spinPointSeparation->setWhatsThis (tr ("Select a point separation in pixels.\n\n"
96  "Successive points added to a segment will be separated by this number of pixels. "
97  "If Fill Corners is enabled, then additional points will be inserted at corners so some points "
98  "will be closer.\n\n"
99  "This value has a lower limit"));
100  connect (m_spinPointSeparation, SIGNAL (valueChanged (const QString &)), this, SLOT (slotPointSeparation (const QString &)));
101  layout->addWidget (m_spinPointSeparation, row++, 2);
102 
103  QLabel *labelFillCorners = new QLabel (tr ("Fill corners:"));
104  layout->addWidget (labelFillCorners, row, 1);
105 
106  m_chkFillCorners = new QCheckBox;
107  m_chkFillCorners->setWhatsThis (tr ("Fill corners.\n\n"
108  "In addition to the points placed at regular intervals, this option causes a point to be "
109  "placed at each corner. This option can capture important information in piecewise linear graphs, "
110  "but gradually curving graphs may not benefit from the additional points"));
111  connect (m_chkFillCorners, SIGNAL (stateChanged (int)), this, SLOT (slotFillCorners (int)));
112  layout->addWidget (m_chkFillCorners, row++, 2);
113 
114  QLabel *labelLineWidth = new QLabel(tr ("Line width:"));
115  layout->addWidget (labelLineWidth, row, 1);
116 
117  m_spinLineWidth = new QSpinBox;
118  m_spinLineWidth->setWhatsThis (tr ("Select a size for the lines drawn along a segment"));
119  m_spinLineWidth->setMinimum(1);
120  connect (m_spinLineWidth, SIGNAL (valueChanged (int)), this, SLOT (slotLineWidth (int)));
121  layout->addWidget (m_spinLineWidth, row++, 2);
122 
123  QLabel *labelLineColor = new QLabel(tr ("Line color:"));
124  layout->addWidget (labelLineColor, row, 1);
125 
126  m_cmbLineColor = new QComboBox;
127  m_cmbLineColor->setWhatsThis (tr ("Select a color for the lines drawn along a segment"));
128  populateColorComboWithTransparent (*m_cmbLineColor);
129  connect (m_cmbLineColor, SIGNAL (activated (const QString &)), this, SLOT (slotLineColor (const QString &))); // activated() ignores code changes
130  layout->addWidget (m_cmbLineColor, row++, 2);
131 }
132 
133 void DlgSettingsSegments::createOptionalSaveDefault (QHBoxLayout * /* layout */)
134 {
135 }
136 
137 void DlgSettingsSegments::createPreview (QGridLayout *layout,
138  int &row)
139 {
140  LOG4CPP_INFO_S ((*mainCat)) << "DlgSettingsSegments::createPreview";
141 
142  QLabel *labelPreview = new QLabel (tr ("Preview"));
143  layout->addWidget (labelPreview, row++, 0, 1, 4);
144 
145  m_scenePreview = new QGraphicsScene (this);
146  m_viewPreview = new ViewPreview (m_scenePreview,
147  ViewPreview::VIEW_ASPECT_RATIO_VARIABLE,
148  this);
149  m_viewPreview->setWhatsThis (tr ("Preview window shows the shortest line that can be segment filled, "
150  "and the effects of current settings on segments and points generated by segment fill"));
151  m_viewPreview->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
152  m_viewPreview->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
153  m_viewPreview->setMinimumHeight (MINIMUM_PREVIEW_HEIGHT);
154 
155  layout->addWidget (m_viewPreview, row++, 0, 1, 4);
156 }
157 
158 QImage DlgSettingsSegments::createPreviewImage () const
159 {
160  LOG4CPP_INFO_S ((*mainCat)) << "DlgSettingsSegments::createPreviewImage";
161 
162  QImage image (IMAGE_WIDTH,
163  IMAGE_HEIGHT,
164  QImage::Format_RGB32);
165  image.fill (Qt::white);
166  QPainter painter (&image);
167  painter.setRenderHint(QPainter::Antialiasing);
168  painter.setPen (QPen (QBrush (Qt::black), BRUSH_WIDTH));
169 
170  int margin = IMAGE_WIDTH / 15;
171  int yCenter = IMAGE_HEIGHT / 2;
172  int yHeight = IMAGE_HEIGHT / 4;
173  int x, y, xLast, yLast;
174  bool isFirst;
175 
176  // Draw sinusoid
177  isFirst = true;
178  int xStart = margin, xEnd = IMAGE_WIDTH / 2 - margin;
179  for (x = xStart; x < xEnd; x++) {
180  double s = (double) (x - xStart) / (double) (xEnd - xStart);
181  int y = yCenter - yHeight * qSin (TWOPI * s);
182 
183  if (!isFirst) {
184  painter.drawLine (xLast, yLast, x, y);
185  }
186  isFirst = false;
187  xLast = x;
188  yLast = y;
189  }
190 
191  // Draw triangular waveform that looks like sinusoid straightened up into line segments
192  isFirst = true;
193  xStart = IMAGE_WIDTH / 2 + margin, xEnd = IMAGE_WIDTH - margin;
194  for (x = xStart; x < xEnd; x++) {
195  double s = (double) (x - xStart) / (double) (xEnd - xStart);
196  if (s <= 0.25) {
197  y = yCenter - yHeight * (4.0 * s);
198  } else if (s < 0.75) {
199  y = yCenter - yHeight * (1.0 - 4.0 * (s - 0.25));
200  } else {
201  y = yCenter + yHeight * (1.0 - 4 * (s - 0.75));
202  }
203 
204  if (!isFirst) {
205  painter.drawLine (xLast, yLast, x, y);
206  }
207  isFirst = false;
208  xLast = x;
209  yLast = y;
210  }
211 
212  return image;
213 }
214 
216 {
217  LOG4CPP_INFO_S ((*mainCat)) << "DlgSettingsSegments::createSubPanel";
218 
219  QWidget *subPanel = new QWidget ();
220  QGridLayout *layout = new QGridLayout (subPanel);
221  subPanel->setLayout (layout);
222 
223  layout->setColumnStretch (0, 1); // Empty first column
224  layout->setColumnStretch (1, 0); // Labels
225  layout->setColumnStretch (2, 0); // User controls
226  layout->setColumnStretch (3, 1); // Empty last column
227 
228  int row = 0;
229  createControls(layout, row);
230  createPreview (layout, row);
231  QPixmap pixmap = QPixmap::fromImage (createPreviewImage());
232  m_scenePreview->addPixmap (pixmap);
233 
234  return subPanel;
235 }
236 
238 {
239  LOG4CPP_INFO_S ((*mainCat)) << "DlgSettingsSegments::handleOk";
240 
242  cmdMediator ().document(),
243  *m_modelSegmentsBefore,
244  *m_modelSegmentsAfter);
245  cmdMediator ().push (cmd);
246 
247  hide ();
248 }
249 
251 {
252  LOG4CPP_INFO_S ((*mainCat)) << "DlgSettingsSegments::load";
253 
254  // Loading starts here
255  m_loading = true;
256 
257  setCmdMediator (cmdMediator);
258 
259  // Flush old data
260  if (m_modelSegmentsBefore != 0) {
261  delete m_modelSegmentsBefore;
262  }
263  if (m_modelSegmentsAfter != 0) {
264  delete m_modelSegmentsAfter;
265  }
266 
267  // Save new data
268  m_modelSegmentsBefore = new DocumentModelSegments (cmdMediator.document());
269  m_modelSegmentsAfter = new DocumentModelSegments (cmdMediator.document());
270 
271  // Sanity checks. Incoming defaults must be acceptable to the local limits
272  ENGAUGE_ASSERT (MIN_LENGTH_MIN <= m_modelSegmentsAfter->minLength ());
273  ENGAUGE_ASSERT (MIN_LENGTH_MAX >= m_modelSegmentsAfter->minLength ());
274  ENGAUGE_ASSERT (POINT_SEPARATION_MIN <= m_modelSegmentsAfter->pointSeparation());
275  ENGAUGE_ASSERT (POINT_SEPARATION_MAX >= m_modelSegmentsAfter->pointSeparation());
276 
277  // Populate controls
278  m_spinPointSeparation->setValue (m_modelSegmentsAfter->pointSeparation());
279  m_spinMinLength->setValue (m_modelSegmentsAfter->minLength());
280  m_chkFillCorners->setChecked (m_modelSegmentsAfter->fillCorners ());
281  m_spinLineWidth->setValue (m_modelSegmentsAfter->lineWidth());
282 
283  int indexLineColor = m_cmbLineColor->findData(QVariant (m_modelSegmentsAfter->lineColor()));
284  ENGAUGE_ASSERT (indexLineColor >= 0);
285  m_cmbLineColor->setCurrentIndex(indexLineColor);
286 
287  // Loading finishes here
288  m_loading = false;
289 
290  updateControls();
291  enableOk (false); // Disable Ok button since there not yet any changes
292  updatePreview();
293 }
294 
296 {
297  if (!smallDialogs) {
298  setMinimumHeight (MINIMUM_HEIGHT);
299  }
300 }
301 
302 void DlgSettingsSegments::slotFillCorners (int state)
303 {
304  LOG4CPP_INFO_S ((*mainCat)) << "DlgSettingsSegments::slotFillCorner";
305 
306  m_modelSegmentsAfter->setFillCorners(state == Qt::Checked);
307  updateControls();
308  updatePreview();
309 }
310 
311 void DlgSettingsSegments::slotLineColor (const QString &)
312 {
313  LOG4CPP_INFO_S ((*mainCat)) << "DlgSettingsSegments::slotLineColor";
314 
315  m_modelSegmentsAfter->setLineColor((ColorPalette) m_cmbLineColor->currentData().toInt());
316  updateControls();
317  updatePreview();
318 }
319 
320 void DlgSettingsSegments::slotLineWidth (int lineWidth)
321 {
322  LOG4CPP_INFO_S ((*mainCat)) << "DlgSettingsSegments::slotLineWidth";
323 
324  m_modelSegmentsAfter->setLineWidth(lineWidth);
325  updateControls();
326  updatePreview();
327 }
328 
329 void DlgSettingsSegments::slotMinLength (const QString &minLength)
330 {
331  LOG4CPP_INFO_S ((*mainCat)) << "DlgSettingsSegments::slotMinLength";
332 
333  m_modelSegmentsAfter->setMinLength(minLength.toDouble());
334  updateControls();
335  updatePreview();
336 }
337 
338 void DlgSettingsSegments::slotPointSeparation (const QString &pointSeparation)
339 {
340  LOG4CPP_INFO_S ((*mainCat)) << "DlgSettingsSegments::slotPointSeparation";
341 
342  m_modelSegmentsAfter->setPointSeparation(pointSeparation.toDouble());
343  updateControls();
344  updatePreview();
345 }
346 
347 void DlgSettingsSegments::updateControls()
348 {
349  enableOk (true);
350 }
351 
352 void DlgSettingsSegments::updatePreview()
353 {
354  LOG4CPP_INFO_S ((*mainCat)) << "DlgSettingsSegments::updatePreview"
355  << " loading=" << (m_loading ? "true" : "false");
356 
357  const QString ARBITRARY_IDENTIFIER ("");
358  const QColor COLOR (Qt::blue);
359  const int RADIUS = 5;
360  GeometryWindow *NULL_GEOMETRY_WINDOW = 0;
361 
362  if (!m_loading) {
363 
364  SegmentFactory segmentFactory (*m_scenePreview,
365  mainWindow().isGnuplot());
366 
367  clearPoints();
368  segmentFactory.clearSegments (m_segments);
369 
370  // Create new segments
371  segmentFactory.makeSegments (createPreviewImage(),
372  *m_modelSegmentsAfter,
373  m_segments);
374 
375  // Make the segment visible
376  QList<Segment*>::iterator itrS;
377  for (itrS = m_segments.begin(); itrS != m_segments.end(); itrS++) {
378  Segment *segment = *itrS;
379  segment->slotHover (true);
380  }
381 
382  // Create some points
383  PointStyle pointStyle (POINT_SHAPE_CROSS,
384  RADIUS,
385  BRUSH_WIDTH,
386  COLOR_PALETTE_BLUE);
387  QPolygonF polygon = pointStyle.polygon();
388  QList<QPoint> points = segmentFactory.fillPoints (*m_modelSegmentsAfter,
389  m_segments);
390  QList<QPoint>::iterator itrP;
391  for (itrP = points.begin(); itrP != points.end(); itrP++) {
392  QPoint pos = *itrP;
393  GraphicsPoint *graphicsPoint = new GraphicsPoint (*m_scenePreview,
394  ARBITRARY_IDENTIFIER,
395  pos,
396  COLOR,
397  polygon,
398  BRUSH_WIDTH,
399  NULL_GEOMETRY_WINDOW);
400 
401  m_points.push_back (graphicsPoint);
402  }
403  }
404 }
virtual void setSmallDialogs(bool smallDialogs)
If false then dialogs have a minimum size so all controls are visible.
double pointSeparation() const
Get method for point separation.
void setLineColor(ColorPalette lineColor)
Set method for line color.
void setMinLength(double minLength)
Set method for min length.
double lineWidth() const
Get method for line width.
void setCmdMediator(CmdMediator &cmdMediator)
Store CmdMediator for easy access by the leaf class.
double minLength() const
Get method for min length.
bool fillCorners() const
Get method for fill corners.
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
QList< QPoint > fillPoints(const DocumentModelSegments &modelSegments, QList< Segment *> segments)
Return segment fill points for all segments, for previewing.
void finishPanel(QWidget *subPanel, int minimumWidth=MINIMUM_DIALOG_WIDTH, int minimumHeightOrZero=0)
Add Ok and Cancel buttons to subpanel to get the whole dialog.
void makeSegments(const QImage &imageFiltered, const DocumentModelSegments &modelSegments, QList< Segment *> &segments, bool useDlg=true)
Main entry point for creating all Segments for the filtered image.
virtual void load(CmdMediator &cmdMediator)
Load settings from Document.
void setLineWidth(double lineWidth)
Set method for line width.
QPolygonF polygon() const
Return the polygon for creating a QGraphicsPolygonItem. The size is determined by the radius...
Definition: PointStyle.cpp:155
Factory class for Segment objects.
void slotHover(bool hover)
Slot for hover enter/leave events in the associated SegmentLines.
Definition: Segment.cpp:526
Class that modifies QGraphicsView to automatically expand/shrink the view to fit the window...
Definition: ViewPreview.h:14
void setFillCorners(bool fillCorners)
Set method for fill corners.
Details for a specific Point.
Definition: PointStyle.h:20
void clearSegments(QList< Segment *> &segments)
Remove the segments created by makeSegments.
Selectable piecewise-defined line that follows a filtered line in the image.
Definition: Segment.h:21
Graphics item for drawing a circular or polygonal Point.
Definition: GraphicsPoint.h:43
static int MINIMUM_PREVIEW_HEIGHT
Dialog layout constant that guarantees preview has sufficent room.
Command for DlgSettingsSegments.
void enableOk(bool enable)
Let leaf subclass control the Ok button.
Command queue stack.
Definition: CmdMediator.h:23
ColorPalette lineColor() const
Get method for line color.
void populateColorComboWithTransparent(QComboBox &combo)
Add colors in color palette to combobox, with transparent entry at end.
DlgSettingsSegments(MainWindow &mainWindow)
Single constructor.
Model for DlgSettingsSegments and CmdSettingsSegments.
Abstract base class for all Settings dialogs.
virtual void handleOk()
Process slotOk.
virtual QWidget * createSubPanel()
Create dialog-specific panel to which base class will add Ok and Cancel buttons.
void setPointSeparation(double pointSeparation)
Set method for point separation.
MainWindow & mainWindow()
Get method for MainWindow.
Main window consisting of menu, graphics scene, status bar and optional toolbars as a Single Document...
Definition: MainWindow.h:89
CmdMediator & cmdMediator()
Provide access to Document information wrapped inside CmdMediator.
virtual void createOptionalSaveDefault(QHBoxLayout *layout)
Let subclass define an optional Save As Default button.