Engauge Digitizer 2
Loading...
Searching...
No Matches
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"
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
26const int MINIMUM_HEIGHT = 540;
27const int MIN_LENGTH_MIN = 1;
28const int MIN_LENGTH_MAX = 10000;
30const int POINT_SEPARATION_MAX = 10000;
31
32const int IMAGE_WIDTH = 400;
33const int IMAGE_HEIGHT = 350;
34
35const double TWOPI = 2.0 * 3.1415926535;
36
37const double BRUSH_WIDTH = 2.0;
38
40 DlgSettingsAbstractBase (tr ("Segment Fill"),
41 "DlgSettingsSegments",
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
60void 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
73void 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
133void DlgSettingsSegments::createOptionalSaveDefault (QHBoxLayout * /* layout */)
134{
135}
136
137void 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
158QImage DlgSettingsSegments::createPreviewImage () const
159{
160 LOG4CPP_INFO_S ((*mainCat)) << "DlgSettingsSegments::createPreviewImage";
161
162 QImage image (IMAGE_WIDTH,
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
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
270 ENGAUGE_ASSERT (MIN_LENGTH_MAX >= m_modelSegmentsAfter->minLength ());
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
299void 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
308void 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
317void DlgSettingsSegments::slotLineWidth (int lineWidth)
318{
319 LOG4CPP_INFO_S ((*mainCat)) << "DlgSettingsSegments::slotLineWidth";
320
321 m_modelSegmentsAfter->setLineWidth(lineWidth);
322 updateControls();
323 updatePreview();
324}
325
326void 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
335void 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
344void DlgSettingsSegments::updateControls()
345{
346 enableOk (true);
347}
348
349void 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,
399 NULL_GEOMETRY_WINDOW);
400
401 m_points.push_back (graphicsPoint);
402 }
403 }
404}
ColorPalette
@ COLOR_PALETTE_BLUE
const int MINIMUM_HEIGHT
const int IMAGE_WIDTH
const int IMAGE_HEIGHT
const int MIN_LENGTH_MAX
const int POINT_SEPARATION_MIN
const int POINT_SEPARATION_MAX
const double BRUSH_WIDTH
const int MIN_LENGTH_MIN
const double TWOPI
#define ENGAUGE_ASSERT(cond)
Drop in replacement for Q_ASSERT if defined(QT_NO_DEBUG) && !defined(QT_FORCE_ASSERTS) define ENGAUGE...
log4cpp::Category * mainCat
Definition Logger.cpp:14
@ POINT_SHAPE_CROSS
Definition PointShape.h:14
Command queue stack.
Definition CmdMediator.h:24
Command for DlgSettingsSegments.
DlgSettingsAbstractBase(const QString &title, const QString &dialogName, MainWindow &mainWindow)
Single constructor.
void setCmdMediator(CmdMediator &cmdMediator)
Store CmdMediator for easy access by the leaf class.
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 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.
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.
virtual void setSmallDialogs(bool smallDialogs)
If false then dialogs have a minimum size so all controls are visible.
Model for DlgSettingsSegments and CmdSettingsSegments.
void setFillCorners(bool fillCorners)
Set method for fill corners.
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:92
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:15
@ VIEW_ASPECT_RATIO_VARIABLE
Definition ViewPreview.h:22
#define LOG4CPP_INFO_S(logger)
Definition convenience.h:18