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