Engauge Digitizer  2
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
DlgSettingsSegments.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 "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 (nullptr),
44  m_viewPreview (nullptr),
45  m_modelSegmentsBefore (nullptr),
46  m_modelSegmentsAfter (nullptr),
47  m_loading (false)
48 {
49  LOG4CPP_INFO_S ((*mainCat)) << "DlgSettingsSegments::DlgSettingsSegments";
50 
51  QWidget *subPanel = createSubPanel ();
52  finishPanel (subPanel);
53 }
54 
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(QString ("%1:").arg (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(QString ("%1:").arg (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 (QString ("%1:").arg (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(QString ("%1:").arg (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(QString ("%1:").arg (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,
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 = 0, yLast = 0;
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 = qFloor (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;
194  xEnd = IMAGE_WIDTH - margin;
195  for (x = xStart; x < xEnd; x++) {
196  double s = double (x - xStart) / double (xEnd - xStart);
197  if (s <= 0.25) {
198  y = qFloor (yCenter - yHeight * (4.0 * s));
199  } else if (s < 0.75) {
200  y = qFloor (yCenter - yHeight * (1.0 - 4.0 * (s - 0.25)));
201  } else {
202  y = qFloor (yCenter + yHeight * (1.0 - 4 * (s - 0.75)));
203  }
204 
205  if (!isFirst) {
206  painter.drawLine (xLast, yLast, x, y);
207  }
208  isFirst = false;
209  xLast = x;
210  yLast = y;
211  }
212 
213  return image;
214 }
215 
217 {
218  LOG4CPP_INFO_S ((*mainCat)) << "DlgSettingsSegments::createSubPanel";
219 
220  QWidget *subPanel = new QWidget ();
221  QGridLayout *layout = new QGridLayout (subPanel);
222  subPanel->setLayout (layout);
223 
224  layout->setColumnStretch (0, 1); // Empty first column
225  layout->setColumnStretch (1, 0); // Labels
226  layout->setColumnStretch (2, 0); // User controls
227  layout->setColumnStretch (3, 1); // Empty last column
228 
229  int row = 0;
230  createControls(layout, row);
231  createPreview (layout, row);
232  QPixmap pixmap = QPixmap::fromImage (createPreviewImage());
233  m_scenePreview->addPixmap (pixmap);
234 
235  return subPanel;
236 }
237 
239 {
240  LOG4CPP_INFO_S ((*mainCat)) << "DlgSettingsSegments::handleOk";
241 
243  cmdMediator ().document(),
244  *m_modelSegmentsBefore,
245  *m_modelSegmentsAfter);
246  cmdMediator ().push (cmd);
247 
248  hide ();
249 }
250 
252 {
253  LOG4CPP_INFO_S ((*mainCat)) << "DlgSettingsSegments::load";
254 
255  // Loading starts here
256  m_loading = true;
257 
258  setCmdMediator (cmdMediator);
259 
260  // Flush old data
261  delete m_modelSegmentsBefore;
262  delete m_modelSegmentsAfter;
263 
264  // Save new data
265  m_modelSegmentsBefore = new DocumentModelSegments (cmdMediator.document());
266  m_modelSegmentsAfter = new DocumentModelSegments (cmdMediator.document());
267 
268  // Sanity checks. Incoming defaults must be acceptable to the local limits
269  ENGAUGE_ASSERT (MIN_LENGTH_MIN <= m_modelSegmentsAfter->minLength ());
270  ENGAUGE_ASSERT (MIN_LENGTH_MAX >= m_modelSegmentsAfter->minLength ());
271  ENGAUGE_ASSERT (POINT_SEPARATION_MIN <= m_modelSegmentsAfter->pointSeparation());
272  ENGAUGE_ASSERT (POINT_SEPARATION_MAX >= m_modelSegmentsAfter->pointSeparation());
273 
274  // Populate controls
275  m_spinPointSeparation->setValue (qFloor (m_modelSegmentsAfter->pointSeparation()));
276  m_spinMinLength->setValue (qFloor (m_modelSegmentsAfter->minLength()));
277  m_chkFillCorners->setChecked (m_modelSegmentsAfter->fillCorners ());
278  m_spinLineWidth->setValue (qFloor (m_modelSegmentsAfter->lineWidth()));
279 
280  int indexLineColor = m_cmbLineColor->findData(QVariant (m_modelSegmentsAfter->lineColor()));
281  ENGAUGE_ASSERT (indexLineColor >= 0);
282  m_cmbLineColor->setCurrentIndex(indexLineColor);
283 
284  // Loading finishes here
285  m_loading = false;
286 
287  updateControls();
288  enableOk (false); // Disable Ok button since there not yet any changes
289  updatePreview();
290 }
291 
293 {
294  if (!smallDialogs) {
295  setMinimumHeight (MINIMUM_HEIGHT);
296  }
297 }
298 
299 void DlgSettingsSegments::slotFillCorners (int state)
300 {
301  LOG4CPP_INFO_S ((*mainCat)) << "DlgSettingsSegments::slotFillCorner";
302 
303  m_modelSegmentsAfter->setFillCorners(state == Qt::Checked);
304  updateControls();
305  updatePreview();
306 }
307 
308 void DlgSettingsSegments::slotLineColor (const QString &)
309 {
310  LOG4CPP_INFO_S ((*mainCat)) << "DlgSettingsSegments::slotLineColor";
311 
312  m_modelSegmentsAfter->setLineColor(static_cast<ColorPalette> (m_cmbLineColor->currentData().toInt()));
313  updateControls();
314  updatePreview();
315 }
316 
317 void DlgSettingsSegments::slotLineWidth (int lineWidth)
318 {
319  LOG4CPP_INFO_S ((*mainCat)) << "DlgSettingsSegments::slotLineWidth";
320 
321  m_modelSegmentsAfter->setLineWidth(lineWidth);
322  updateControls();
323  updatePreview();
324 }
325 
326 void DlgSettingsSegments::slotMinLength (const QString &minLength)
327 {
328  LOG4CPP_INFO_S ((*mainCat)) << "DlgSettingsSegments::slotMinLength";
329 
330  m_modelSegmentsAfter->setMinLength(minLength.toDouble());
331  updateControls();
332  updatePreview();
333 }
334 
335 void DlgSettingsSegments::slotPointSeparation (const QString &pointSeparation)
336 {
337  LOG4CPP_INFO_S ((*mainCat)) << "DlgSettingsSegments::slotPointSeparation";
338 
339  m_modelSegmentsAfter->setPointSeparation(pointSeparation.toDouble());
340  updateControls();
341  updatePreview();
342 }
343 
344 void DlgSettingsSegments::updateControls()
345 {
346  enableOk (true);
347 }
348 
349 void DlgSettingsSegments::updatePreview()
350 {
351  LOG4CPP_INFO_S ((*mainCat)) << "DlgSettingsSegments::updatePreview"
352  << " loading=" << (m_loading ? "true" : "false");
353 
354  const QString ARBITRARY_IDENTIFIER ("");
355  const QColor COLOR (Qt::blue);
356  const int RADIUS = 5;
357  GeometryWindow *NULL_GEOMETRY_WINDOW = nullptr;
358  const bool NO_DIALOG = false; // If true then dueling modal dialogs will trigger infinite loops in QSpinBox up/down
359 
360  if (!m_loading) {
361 
362  SegmentFactory segmentFactory (*m_scenePreview,
363  mainWindow().isGnuplot());
364 
365  clearPoints();
366  segmentFactory.clearSegments (m_segments);
367 
368  // Create new segments
369  segmentFactory.makeSegments (createPreviewImage(),
370  *m_modelSegmentsAfter,
371  m_segments,
372  NO_DIALOG);
373 
374  // Make the segment visible
375  QList<Segment*>::iterator itrS;
376  for (itrS = m_segments.begin(); itrS != m_segments.end(); itrS++) {
377  Segment *segment = *itrS;
378  segment->slotHover (true);
379  }
380 
381  // Create some points
382  PointStyle pointStyle (POINT_SHAPE_CROSS,
383  RADIUS,
384  qFloor (BRUSH_WIDTH),
386  QPolygonF polygon = pointStyle.polygon();
387  QList<QPoint> points = segmentFactory.fillPoints (*m_modelSegmentsAfter,
388  m_segments);
389 
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 }
ColorPalette lineColor() const
Get method for line color.
virtual void setSmallDialogs(bool smallDialogs)
If false then dialogs have a minimum size so all controls are visible.
const int IMAGE_WIDTH
const double TWOPI
void setLineColor(ColorPalette lineColor)
Set method for line color.
void setMinLength(double minLength)
Set method for min length.
const int POINT_SEPARATION_MIN
void setCmdMediator(CmdMediator &cmdMediator)
Store CmdMediator for easy access by the leaf class.
const double BRUSH_WIDTH
const int MIN_LENGTH_MIN
#define LOG4CPP_INFO_S(logger)
Definition: convenience.h:18
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
void finishPanel(QWidget *subPanel, int minimumWidth=MINIMUM_DIALOG_WIDTH, int minimumHeightOrZero=0)
Add Ok and Cancel buttons to subpanel to get the whole dialog.
double pointSeparation() const
Get method for point separation.
virtual void load(CmdMediator &cmdMediator)
Load settings from Document.
void setLineWidth(double lineWidth)
Set method for line width.
Factory class for Segment objects.
void slotHover(bool hover)
Slot for hover enter/leave events in the associated SegmentLines.
Definition: Segment.cpp:528
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
Selectable piecewise-defined line that follows a filtered line in the image.
Definition: Segment.h:21
const int IMAGE_HEIGHT
double lineWidth() const
Get method for line width.
Graphics item for drawing a circular or polygonal Point.
Definition: GraphicsPoint.h:43
log4cpp::Category * mainCat
Definition: Logger.cpp:14
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.
const int POINT_SEPARATION_MAX
Command queue stack.
Definition: CmdMediator.h:23
void populateColorComboWithTransparent(QComboBox &combo)
Add colors in color palette to combobox, with transparent entry at end.
DlgSettingsSegments(MainWindow &mainWindow)
Single constructor.
double minLength() const
Get method for min length.
const int MINIMUM_HEIGHT
Model for DlgSettingsSegments and CmdSettingsSegments.
Abstract base class for all Settings dialogs.
virtual void handleOk()
Process slotOk.
const int MIN_LENGTH_MAX
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:91
CmdMediator & cmdMediator()
Provide access to Document information wrapped inside CmdMediator.
#define ENGAUGE_ASSERT(cond)
Drop in replacement for Q_ASSERT if defined(QT_NO_DEBUG) &amp;&amp; !defined(QT_FORCE_ASSERTS) define ENGAUGE...
Definition: EngaugeAssert.h:20
virtual void createOptionalSaveDefault(QHBoxLayout *layout)
Let subclass define an optional Save As Default button.
bool fillCorners() const
Get method for fill corners.