Engauge Digitizer 2
Loading...
Searching...
No Matches
FittingWindow.cpp
Go to the documentation of this file.
1/******************************************************************************************************
2 * (C) 2016 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 "Curve.h"
9#include "CurveConnectAs.h"
10#include "CurveStyle.h"
11#include "EngaugeAssert.h"
13#include "FittingModel.h"
14#include "FittingStatistics.h"
15#include "FittingWindow.h"
16#include "GeometryModel.h"
17#include "Logger.h"
18#include "MainWindow.h"
19#include "MainWindowModel.h"
20#include <QApplication>
21#include <QClipboard>
22#include <QComboBox>
23#include <QGridLayout>
24#include <QItemSelectionModel>
25#include <QLabel>
26#include <QLineEdit>
27#include <qmath.h>
28#include "Transformation.h"
29#include "WindowTable.h"
30
31const int COLUMN_COEFFICIENTS = 0;
33
35 WindowAbstractBase (mainWindow),
36 m_isLogXTheta (false),
37 m_isLogYRadius (false),
38 m_significantDigits (mainWindow->modelMainWindow().significantDigits ())
39{
40 setVisible (false);
41 setAllowedAreas (Qt::AllDockWidgetAreas);
42 setWindowTitle (tr ("Curve Fitting Window")); // Appears in title bar when undocked
43 setStatusTip (tr ("Curve Fitting Window"));
44 setWhatsThis (tr ("Curve Fitting Window\n\n"
45 "This window applies a curve fit to the currently selected curve.\n\n"
46 "If drag-and-drop is disabled, a rectangular set of cells may be selected by clicking and dragging. Otherwise, if "
47 "drag-and-drop is enabled, a rectangular set of cells may be selected using Click then Shift+Click, since click and drag "
48 "starts the dragging operation. Drag-and-drop mode is set in the Main Window settings"));
49
50 m_coefficients.resize (MAX_POLYNOMIAL_ORDER + 1);
51
52 createWidgets (mainWindow);
53 initializeOrder ();
54 clear ();
55}
56
60
61void FittingWindow::calculateCurveFitAndStatistics ()
62{
64
65 double mse = 0, rms = 0, rSquared = 0;
67 m_pointsConvenient,
68 m_coefficients,
69 mse,
70 rms,
72 qFloor (m_significantDigits));
73
74 m_lblMeanSquareError->setText (QString::number (mse));
75 m_lblRootMeanSquare->setText (QString::number (rms));
76 m_lblRSquared->setText (QString::number (rSquared));
77
78 // Send coefficients to connected classes. Also send the first and last x values
79 if (m_pointsConvenient.size () > 0) {
80 int last = m_pointsConvenient.size () - 1;
81 emit signalCurveFit (m_coefficients,
82 m_pointsConvenient [0].x(),
83 m_pointsConvenient [last].x (),
84 m_isLogXTheta,
85 m_isLogYRadius);
86 } else {
87 emit signalCurveFit (m_coefficients,
88 0,
89 0,
90 false,
91 false);
92 }
93
94 // Copy into displayed control
95 for (int row = 0, order = m_model->rowCount () - 1; row < m_model->rowCount (); row++, order--) {
96
97 QStandardItem *item = new QStandardItem (QString::number (m_coefficients [order]));
98 m_model->setItem (row, COLUMN_COEFFICIENTS, item);
99 }
100}
101
103{
104 m_labelY->setText ("");
105 m_model->setRowCount (0);
106 m_lblMeanSquareError->setText ("");
107 m_lblRootMeanSquare->setText ("");
108 m_lblRSquared->setText ("");
109}
110
112{
113 LOG4CPP_INFO_S ((*mainCat)) << "FittingWindow::closeEvent";
114
116}
117
118void FittingWindow::createWidgets (MainWindow *mainWindow)
119{
120 QWidget *widget = new QWidget;
122
124 widget->setLayout (layout);
125 int row = 0;
126
127 // Order row
128 QLabel *labelOrder = new QLabel (QString ("%1:").arg (tr ("Order")));
129 layout->addWidget (labelOrder, row, 0, 1, 1);
130
131 m_cmbOrder = new QComboBox;
132 for (int order = 0; order <= MAX_POLYNOMIAL_ORDER; order++) {
133 m_cmbOrder->addItem (QString::number (order), QVariant (order));
134 }
135 connect (m_cmbOrder, SIGNAL (currentIndexChanged (int)), this, SLOT (slotCmbOrder (int)));
136 layout->addWidget (m_cmbOrder, row++, 1, 1, 1);
137
138 // Y= row
139 m_labelY = new QLabel; // The text will be set in resizeTable
140 layout->addWidget (m_labelY, row++, 0, 1, 1);
141
142 // Table row
143 m_model = new FittingModel;
144 m_model->setColumnCount (2);
145
146 m_view = new WindowTable (*m_model);
147 connect (m_view, SIGNAL (signalTableStatusChange ()),
148 mainWindow, SLOT (slotTableStatusChange ()));
149
150 layout->addWidget (m_view, row++, 0, 1, 2);
151
152 // Statistics rows
153 QLabel *lblMeanSquareError = new QLabel (QString ("%1:").arg (tr ("Mean square error")));
154 layout->addWidget (lblMeanSquareError, row, 0, 1, 1);
155
156 m_lblMeanSquareError = new QLineEdit;
157 m_lblMeanSquareError->setReadOnly (true);
158 m_lblMeanSquareError->setWhatsThis (tr ("Calculated mean square error statistic"));
159 layout->addWidget (m_lblMeanSquareError, row++, 1, 1, 1);
160
161 QLabel *lblRootMeanSquare = new QLabel (QString ("%1:").arg (tr ("Root mean square")));
162 layout->addWidget (lblRootMeanSquare, row, 0, 1, 1);
163
164 m_lblRootMeanSquare = new QLineEdit;
165 m_lblRootMeanSquare->setReadOnly (true);
166 m_lblRootMeanSquare->setWhatsThis (tr ("Calculated root mean square statistic. This is calculated as the square root of the mean square error"));
167 layout->addWidget (m_lblRootMeanSquare, row++, 1, 1, 1);
168
169 QLabel *lblRSquared = new QLabel (QString ("%1:").arg (tr ("R squared")));
170 layout->addWidget (lblRSquared, row, 0, 1, 1);
171
172 m_lblRSquared = new QLineEdit;
173 m_lblRSquared->setReadOnly (true);
174 m_lblRSquared->setWhatsThis (tr ("Calculated R squared statistic"));
175 layout->addWidget (m_lblRSquared, row++, 1, 1, 1);
176}
177
179{
180 LOG4CPP_INFO_S ((*mainCat)) << "FittingWindow::doCopy";
181
182 QString text = m_model->selectionAsText (m_modelExport.delimiter());
183
184 if (!text.isEmpty ()) {
185
186 // Save to clipboard
187 QApplication::clipboard ()->setText (text);
188
189 }
190}
191
192void FittingWindow::initializeOrder ()
193{
194 const int SECOND_ORDER = 2;
195
196 int index = m_cmbOrder->findData (QVariant (SECOND_ORDER));
197 m_cmbOrder->setCurrentIndex (index);
198}
199
200int FittingWindow::maxOrder () const
201{
202 return m_cmbOrder->currentData().toInt();
203}
204
205void FittingWindow::refreshTable ()
206{
207 int order = m_cmbOrder->currentData().toInt();
208
209 // Table size may have to change
210 resizeTable (order);
211
212 calculateCurveFitAndStatistics ();
213}
214
215void FittingWindow::resizeTable (int order)
216{
217 LOG4CPP_INFO_S ((*mainCat)) << "FittingWindow::resizeTable";
218
219 m_model->setRowCount (order + 1);
220
221 // Populate the Y= row. Base for log must be consistent with base used in update()
222 QString yTerm = QString ("%1%2%3")
223 .arg (m_curveSelected)
224 .arg (m_curveSelected.isEmpty () ?
225 "" :
226 ": ")
227 .arg (m_isLogYRadius ?
228 tr ("log10(Y)=") :
229 tr ("Y="));
230 m_labelY->setText (yTerm);
231
232 // Populate polynomial terms. Base for log must be consistent with base used in update()
233 QString xString = (m_isLogXTheta ?
234 tr ("log10(X)") :
235 tr ("X"));
236 for (int row = 0, term = order; term >= 0; row++, term--) {
237
238 // Entries are x^order, ..., x^2, x, 1
239 QString termString = QString ("%1%2%3%4")
240 .arg ((term > 0) ? xString : "")
241 .arg ((term > 1) ? "^" : "")
242 .arg ((term > 1) ? QString::number (term) : "")
243 .arg ((term > 0) ? "+" : "");
244
246 m_model->setItem (row, COLUMN_POLYNOMIAL_TERMS, item);
247 }
248}
249
250void FittingWindow::slotCmbOrder(int /* index */)
251{
252 refreshTable ();
253}
254
255void FittingWindow::update (const CmdMediator &cmdMediator,
256 const MainWindowModel &modelMainWindow,
257 const QString &curveSelected,
258 const Transformation &transformation)
259{
260 LOG4CPP_INFO_S ((*mainCat)) << "FittingWindow::update";
261
262 // Save inputs
263 m_curveSelected = curveSelected;
264 m_modelExport = cmdMediator.document().modelExport();
265 m_model->setDelimiter (m_modelExport.delimiter());
266 m_isLogXTheta = (cmdMediator.document().modelCoords().coordScaleXTheta() == COORD_SCALE_LOG);
267 m_isLogYRadius = (cmdMediator.document().modelCoords().coordScaleYRadius() == COORD_SCALE_LOG);
268 m_view->setDragEnabled (modelMainWindow.dragDropExport());
269 m_significantDigits = modelMainWindow.significantDigits();
270
271 m_pointsConvenient.clear ();
272
273 if (transformation.transformIsDefined()) {
274
275 // Gather and calculate geometry data
276 const Curve *curve = cmdMediator.document().curveForCurveName (curveSelected);
277
279
280 if (curve->numPoints() > 0) {
281
282 // Copy points to convenient list
283 const Points points = curve->points();
284 Points::const_iterator itr;
285 for (itr = points.begin (); itr != points.end (); itr++) {
286
287 const Point &point = *itr;
288 QPointF posScreen = point.posScreen ();
289 QPointF posGraph;
290 transformation.transformScreenToRawGraph (posScreen,
291 posGraph);
292
293 // Adjust for log coordinates
294 if (m_isLogXTheta) {
295 double x = qLn (posGraph.x()) / qLn (10.0); // Use base 10 consistent with text in resizeTable
296 posGraph.setX (x);
297 }
298 if (m_isLogYRadius) {
299 double y = qLn (posGraph.y()) / qLn (10.0); // Use base 10 consistent with text in resizeTable
300 posGraph.setY (y);
301 }
302
303 m_pointsConvenient.append (posGraph);
304 }
305 }
306 }
307
308 refreshTable ();
309}
310
311QTableView *FittingWindow::view () const
312{
313 return dynamic_cast<QTableView*> (m_view);
314}
@ COORD_SCALE_LOG
Definition CoordScale.h:14
const int INNER_RADIUS_MIN
#define ENGAUGE_CHECK_PTR(ptr)
#endif
const int COLUMN_POLYNOMIAL_TERMS
const int MAX_POLYNOMIAL_ORDER
const int COLUMN_POLYNOMIAL_TERMS
const int COLUMN_COEFFICIENTS
log4cpp::Category * mainCat
Definition Logger.cpp:14
QList< Point > Points
Definition Points.h:13
Command queue stack.
Definition CmdMediator.h:24
Document & document()
Provide the Document to commands, primarily for undo/redo processing.
Container for one set of digitized Points.
Definition Curve.h:34
CoordScale coordScaleYRadius() const
Get method for linear/log scale on y/radius.
CoordScale coordScaleXTheta() const
Get method for linear/log scale on x/theta.
ExportDelimiter delimiter() const
Get method for delimiter.
DocumentModelCoords modelCoords() const
Get method for DocumentModelCoords.
Definition Document.cpp:695
DocumentModelExportFormat modelExport() const
Get method for DocumentModelExportFormat.
Definition Document.cpp:716
const Curve * curveForCurveName(const QString &curveName) const
See CurvesGraphs::curveForCurveNames, although this also works for AXIS_CURVE_NAME.
Definition Document.cpp:335
Model for FittingWindow.
This class does the math to compute statistics for FittingWindow.
void calculateCurveFitAndStatistics(unsigned int order, const FittingPointsConvenient &pointsConvenient, FittingCurveCoefficients &coefficients, double &mse, double &rms, double &rSquared, int significantDigits)
Compute the curve fit and the statistics for that curve fit.
virtual void clear()
Clear stale information.
virtual void doCopy()
Copy the current selection to the clipboard.
FittingWindow(MainWindow *mainWindow)
Single constructor. Parent is needed or else this widget cannot be redocked after being undocked.
virtual void update(const CmdMediator &cmdMediator, const MainWindowModel &modelMainWindow, const QString &curveSelected, const Transformation &transformation)
Populate the table with the specified Curve.
virtual void closeEvent(QCloseEvent *event)
Catch close event so corresponding menu item in MainWindow can be updated accordingly.
void signalCurveFit(FittingCurveCoefficients, double, double, bool, bool)
Signal containing coefficients from curve fit.
virtual QTableView * view() const
QTableView-based class used by child class.
virtual ~FittingWindow()
void signalFittingWindowClosed()
Signal that this QDockWidget was just closed.
Model for DlgSettingsMainWindow.
bool dragDropExport() const
Get method for drag and drop export.
int significantDigits() const
Get method for significant digits.
Main window consisting of menu, graphics scene, status bar and optional toolbars as a Single Document...
Definition MainWindow.h:92
Class that represents one digitized point. The screen-to-graph coordinate transformation is always ex...
Definition Point.h:26
QPointF posScreen() const
Accessor for screen position.
Definition Point.cpp:404
Affine transformation between screen and graph coordinates, based on digitized axis points.
void transformScreenToRawGraph(const QPointF &coordScreen, QPointF &coordGraph) const
Transform from cartesian pixel screen coordinates to cartesian/polar graph coordinates.
bool transformIsDefined() const
Transform is defined when at least three axis points have been digitized.
Dockable widget abstract base class.
QString selectionAsText(ExportDelimiter delimiter) const
Convert the selection into exportable text which is good for text editors.
void setDelimiter(ExportDelimiter delimiter)
Save output delimiter.
Table view class with support for both drag-and-drop and copy-and-paste.
Definition WindowTable.h:18
#define LOG4CPP_INFO_S(logger)
Definition convenience.h:18